You are on page 1of 471

Introduccin a Ciencias de la o Computacin I o

(con Java)

Elisa Viso G. Canek Pelez V. a


Facultad de Ciencias, UNAM

Indice general
1. Introduccin o 1.1. Conceptos generales . . . . . . . . 1.2. Historia . . . . . . . . . . . . . . 1.3. Sistemas numricos . . . . . . . . e 1.4. La arquitectura de von Neumann 1.5. Ejecucin de programas . . . . . o 1.6. Caracter sticas de Java . . . . . . 2. El 2.1. 2.2. 2.3. 1 2 3 6 13 25 27 29 29 41 46 55 55 62 90

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

proceso del software Qu es la programacin? . . . . . . . . . . . . . . . . . . . . . . . e o Diseo orientado a objetos . . . . . . . . . . . . . . . . . . . . . . . n Diseo estructurado . . . . . . . . . . . . . . . . . . . . . . . . . . . n

3. Clases y objetos 3.1. Tarjetas de responsabilidades . . . . . . . . . . . . . . . . . . . . . 3.2. Programacin en Java . . . . . . . . . . . . . . . . . . . . . . . . . o 3.3. Expresiones en Java . . . . . . . . . . . . . . . . . . . . . . . . . . .

4. Manejo de cadenas y expresiones 99 4.1. Manejo de cadenas en Java . . . . . . . . . . . . . . . . . . . . . . . 99 4.2. Implementacin de una base de datos . . . . . . . . . . . . . . . . . 104 o 4.3. Una clase Menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 5. Datos estructurados 141 5.1. La clase para cada registro . . . . . . . . . . . . . . . . . . . . . . . 142 5.2. La lista de registros . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 6. Herencia 6.1. Extensin de clases . . . . . . . . . . . . . . . . . . . . . . . . . . . o 6.2. Arreglos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.3. Aspectos principales de la herencia . . . . . . . . . . . . . . . . . . 167 167 171 189

II

INDICE GENERAL 6.4. 6.5. 6.6. Polimorsmo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Clases abstractas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196

7. Administracin de la memoria durante ejecucin o o 199 7.1. El stack y el heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 7.2. Recursividad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 8. Ordenamientos usando estructuras de datos 8.1. Base de datos en un arreglo . . . . . . . . . . . . . . . . . . . . . . 8.2. Mantenimiento del orden con listas ligadas . . . . . . . . . . . . . . 8.3. *Ordenamiento usando rboles . . . . . . . . . . . . . . . . . . . . . a 9. Manejo de errores en ejecucin o 9.1. Tipos de errores . . . . . . . . . . . . . 9.2. La clase Exception . . . . . . . . . . . . 9.3. Cmo detectar y cachar una excepcin o o 9.4. Las clases que extienden a Exception . 9.5. El enunciado nally . . . . . . . . . . . 9.6. Restricciones para las excepciones . . . 9.7. Recomendaciones generales . . . . . . . 239 239 258 265 289 289 294 296 305 311 316 319 321 321 324 324 329 333 352 353 367 395 410 411 411 412 415 418 424

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

10.Entrada y salida 10.1. Conceptos generales . . . . . . . . . . . . 10.2. Jerarqu de clases . . . . . . . . . . . . a 10.3. Entrada y salida de bytes . . . . . . . . 10.4. Entrada y salida de carcteres . . . . . . a 10.5. El manejo del men de la aplicacin . . . u o 10.6. Redireccionamiento de in, out y err . . . 10.7. Persistencia de la base de datos . . . . . 10.8. Escritura y lectura de campos que no son 10.9. Lectura y escritura de objetos . . . . . . 10.10. Colofn . . . . . . . . . . . . . . . . . . o 11.Hilos de ejecucin o 11.1. Qu es un hilo de ejecucin? . . . . e o 11.2. La clase Thread . . . . . . . . . . . . 11.3. La interfaz Runnable . . . . . . . . . 11.4. Sincronizacin de hilos de ejecucin . o o 11.5. Comunicacin entre hilos de ejecucin o o

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . cadenas . . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

INDICE GENERAL 11.6. 11.7. 11.8. 11.9. 11.10. 11.11. Alternativas para la programacin de procesos . o Abrazo mortal (deadlock ) . . . . . . . . . . . . Cmo se termina la ejecucin de un proceso . . o o Terminacin de la aplicacin . . . . . . . . . . . o o Depuracin en hilos de ejecucin . . . . . . . . . o o Otros temas relacionados con hilos de ejecucin o . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

III

432 435 439 446 449 451

Indice de guras
1.1. 1.2. 1.3. 1.4. 1.5. 1.6. 1.7. 1.8. Arquitectura de von Neumann . . . . . . . . . . . . . . . . Proceso para ejecutar un programa escrito en ensamblador Codicacin en ensamblador de frmulas matemticas. . . o o a Enteros en signo y magnitud. . . . . . . . . . . . . . . . . Nmeros en complemento a 2 . . . . . . . . . . . . . . . . u Suma de dos nmeros con complemento a 2 . . . . . . . . u Sumando 1 al mximo entero positivo . . . . . . . . . . . . a Notacin de punto jo. . . . . . . . . . . . . . . . . . . . . o . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 15 17 19 21 21 22 23 30 40 48 49 49 50 50 50 51 51 52 53 54 58 58 59 60 61 64 64 65

2.1. Proceso del software. . . . . . . . . . . . . . . . . . . . . . . . . . 2.2. Arbol de herencia en clases. . . . . . . . . . . . . . . . . . . . . . 2.3. Uso de llaves para denotar composicin. . . . . . . . . . . . . . . o 2.4. Iteracin en diagramas de Warnier-Orr. . . . . . . . . . . . . . . . o 2.5. Seleccin en diagramas de Warnier-Orr. . . . . . . . . . . . . . . . o 2.6. Diagramas de Warnier-Orr para secuencia. . . . . . . . . . . . . . 2.7. Diagramas de Warnier-Orr para iteracin. . . . . . . . . . . . . . o 2.8. Diagrama de Warnier-Orr para seleccin. . . . . . . . . . . . . . . o 2.9. Estado inicial de todo diagrama de Warnier-Orr. . . . . . . . . . . 2.10. Diagrama inicial para encontrar factores primos. . . . . . . . . . . 2.11. Diagrama de Warnier-Orr para procesar cada k. . . . . . . . . . . 2.12. Diagrama para determinar si k es primo. . . . . . . . . . . . . . . 2.13. Diagrama de Warnier-Orr para obtener factores primos de un entero. 3.1. 3.2. 3.3. 3.4. 3.5. 3.6. 3.7. 3.8. Tarjetas de clasicacin y acceso. . . . . . . . . . . . o Tarjeta de responsabilidades de la clase Reloj . . . . Tarjeta de responsabilidades para la clase Manecilla. Tarjeta de colaboraciones de la clase Manecilla. . . Tarjeta de colaboraciones de la clase Reloj. . . . . . Encabezado de una interfaz. . . . . . . . . . . . . . . Sintaxis para el xaccesoy. . . . . . . . . . . . . . . . . Reglas para la formacin de un xidenticadory. . . . . o . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

VI

INDICE DE FIGURAS 3.9. Encabezado de una clase. . . . . . . . . . . . . 3.10. Encabezado para los mtodos de acceso. . . . e 3.11. Especicacin de parmetros. . . . . . . . . . o a 3.12. Encabezado de un constructor. . . . . . . . . 3.13. Declaracin de un atributo . . . . . . . . . . . o 3.14. Acceso a atributos o mtodos de objetos . . . e 3.15. Sintaxis para la implementacin de un mtodo o e 3.16. Declaracin de variables locales . . . . . . . . o 3.17. El enunciado de asignacin . . . . . . . . . . . o 3.18. Construccin de objetos . . . . . . . . . . . . o 3.19. Invocacin de mtodo . . . . . . . . . . . . . . o e . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 69 71 76 79 81 83 84 87 88 88 105 111 112 114 116 117 118 119 120 121 121 124 125 126 127 128 130 131 132 142 150 151 152 152 153 154 155

4.1. Tarjeta de responsabilidades para Curso. . . . . . . . . . . . . . . 4.2. Tarjeta de responsabilidades para Curso. . . . . . . . . . . . . . . 4.3. Diagrama de Warnier-Orr para los constructores. . . . . . . . . . 4.4. Diagrama de Warnier-Orr para regresar el contenido de un campo. 4.5. Encontrar el nmero de registro al que pertenece una subcadena. . u 4.6. Edicin del i-simo registro, si es que existe. . . . . . . . . . . . . o e 4.7. Algoritmos para listar el curso. . . . . . . . . . . . . . . . . . . . 4.8. Enunciado compuesto while. . . . . . . . . . . . . . . . . . . . . . 4.9. Encontrar el l mite de 21 , dado . . . . . . . . . . . . . . . . . . n 4.10. Sumar nmero mientras no me den un 1 . . . . . . . . . . . . . u 4.11. Enunciado compuesto do . . . while . . . . . . . . . . . . . . . . . 4.12. Algoritmo para agregar un estudiante. . . . . . . . . . . . . . . . 4.13. Posibles situaciones para eliminar a un registro. . . . . . . . . . . 4.14. Algoritmo para eliminar a un estudiante de la lista. . . . . . . . . 4.15. Enunciado compuesto condicional . . . . . . . . . . . . . . . . . . 4.16. Mtodo que encuentra TODOS los que contienen a una subcadena. e 4.17. Men para uso de la clase Curso. . . . . . . . . . . . . . . . . . . u 4.18. Enunciado switch. . . . . . . . . . . . . . . . . . . . . . . . . . . 4.19. Enunciado break. . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1. 5.2. 5.3. 5.4. 5.5. 5.6. 5.7. 5.8. Ilustracin de una lista . . . . . . . . . . . . . . . . . . . o Contando los registros de una lista . . . . . . . . . . . . Procesando los registros de una lista . . . . . . . . . . . Agregando al principio de la lista . . . . . . . . . . . . . Esquema del agregado un registro al principio de la lista Agregando al nal de la lista . . . . . . . . . . . . . . . . Agregando al nal de la lista . . . . . . . . . . . . . . . . Imprimiendo todos los registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

INDICE DE FIGURAS 5.9. Imprimiendo registros seleccionados . . . . . . 5.10. Patrn de bsqueda de un registro que cumpla o u 5.11. Eliminacin de un estudiante . . . . . . . . . o 5.12. Eliminacin de un registro en una lista . . . . o . . . . . con . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

VII

156 157 159 160 174 174 174 175 176 177 177 177 179 184 188 194 200 203 203 205 206 209 209 210 210 211 212 213 214 215 215 216 217 218 219 219 220 221

6.1. intr s primos = {2,3,5,7,11}; . . . . . . . . . . . . . . . 6.2. oat [ ] vector = { 3.14, 8.7, 19.0}; . . . . . . . . . . . 6.3. String [ ] cadenas = { S No}; . . . . . . . . . . . , 6.4. Declaracin del contenido de un arreglo de objetos . . . o 6.5. int r s primos = new intr5s; . . . . . . . . . . . . . . . 6.6. EstudianteBasicor s estudiantes = . . . . . . . . . . . . . 6.7. oatr s vector = oatr3s; . . . . . . . . . . . . . . . . 6.8. Stringr s cadenas = new Stringr2s; . . . . . . . . . . . . 6.9. Reasignacin de arreglos . . . . . . . . . . . . . . . . . o 6.10. Acomodo en memoria de un arreglo de dos dimensiones 6.11. Ejecucin de la clase Arreglos . . . . . . . . . . . . . o 6.12. Jerarqu de clases . . . . . . . . . . . . . . . . . . . . a

7.1. Estructura de bloques de un programa. . . . . . . . . . . . 7.2. Diagrama de anidamiento dinmico. . . . . . . . . . . . . . a 7.3. Secuencia de llamadas en el listado 7.1. . . . . . . . . . . . 7.4. Esquema de una stack o pila. . . . . . . . . . . . . . . . . 7.5. Algoritmo para ejecutar un programa. . . . . . . . . . . . 7.6. Estado del stack al iniciarse la ejecucin de una clase. . . . o 7.7. El stack al iniciarse la llamada a main. . . . . . . . . . . . 7.8. Registro de activacin para main. . . . . . . . . . . . . . . o 7.9. El stack listo para iniciar la ejecucin de main. . . . . . . . o 7.10. El stack durante la ejecucin de main. . . . . . . . . . . . . o 7.11. El stack durante la ejecucin de A. . . . . . . . . . . . . . o 7.12. El stack antes de empezar a ejecutar B. . . . . . . . . . . . 7.13. El stack antes de empezar a ejecutar C desde la l nea #16:. 7.14. El stack al terminar de ejecutarse C(). . . . . . . . . . . . 7.15. El stack al terminar la ejecucin de B(10,3). . . . . . . . . o 7.16. El stack al terminar la ejecucin de A(10). . . . . . . . . . o 7.17. El stack antes de la ejecucin de B(3,2). . . . . . . . . . . o 7.18. El stack antes de la ejecucin de C(). . . . . . . . . . . . . o 7.19. El stack al terminar la ejecucin deC(). . . . . . . . . . . . o 7.20. El stack al terminar la ejecucin de B(3,2). . . . . . . . . . o 7.21. El stack antes de empezar la ejecucin de C(). . . . . . . . o 7.22. El stack listo para iniciar la ejecucin de main. . . . . . . . o

VIII

INDICE DE FIGURAS 7.23. Estado del stack al iniciarse la ejecucin de una clase. . . . . . . . o 7.24. Estado del stack al iniciarse la llamada de factorial desde main. . . 7.25. Estado del stack al iniciarse la llamada de factorial desde factorial. 7.26. Estado del stack al iniciarse la llamada de factorial desde factorial. 7.27. Estado del stack al iniciarse la llamada de factorial desde factorial. 7.28. Estado del stack al terminarse la llamada de factorial(1). . . . . . 7.29. Estado del stack al terminarse la llamada de factorial(2). . . . . . 7.30. Estado del stack al terminarse la llamada de factorial(3). . . . . . 7.31. Estado del stack al terminarse la llamada de factorial desde main. . 7.32. Juego de las torres de Hanoi . . . . . . . . . . . . . . . . . . . . . 7.33. Estrategia recursiva para las torres de Hanoi . . . . . . . . . . . . 7.34. Secuencia de llamadas en la torres de Hanoi . . . . . . . . . . . . 7.35. Situacin de las chas antes de la llamada . . . . . . . . . . . . . o 7.36. Movimientos /* 1 */ al /* 3 */ . . . . . . . . . . . . . . . . . . . 7.37. Movimiento /* 4 */ . . . . . . . . . . . . . . . . . . . . . . . . . . 7.38. Movimientos /* 5 */ al /* 7 */ . . . . . . . . . . . . . . . . . . . 7.39. Movimiento /* 8 */ . . . . . . . . . . . . . . . . . . . . . . . . . . 7.40. Movimientos /* 9 */ al /* 11 */ . . . . . . . . . . . . . . . . . . . 7.41. Movimiento /* 12 */ . . . . . . . . . . . . . . . . . . . . . . . . . 7.42. Movimientos /* 13 */ al /* 15 */ . . . . . . . . . . . . . . . . . . 8.1. Algoritmo para eliminar a un estudiante . . . . . . 8.2. Agregando al principio de la lista . . . . . . . . . . 8.3. Agregando en medio de la lista . . . . . . . . . . . 8.4. Agregando un registro en orden . . . . . . . . . . . 8.5. Denicin recursiva de un rbol . . . . . . . . . . . o a 8.6. Arbol binario bien organizado . . . . . . . . . . . . 8.7. Arbol que se forma si los registros vienen ordenados 8.8. Agregar un registro manteniendo el orden . . . . . 8.9. Ejemplo simple para recorridos de rboles . . . . . a 8.10. Recorrido simtrico de un rbol . . . . . . . . . . . e a 8.11. Recorrido en preorden de un rbol . . . . . . . . . . a 8.12. Bsqueda en un rbol ordenado . . . . . . . . . . . u a 8.13. Bsqueda de una subcadena . . . . . . . . . . . . . . u 8.14. Seleccin de registros que cumplen una condicin . o o 8.15. Algoritmo para encontrar el menor de un subrbol . a 8.16. Eliminacin de un nodo en un rbol . . . . . . . . . o a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 225 226 227 228 229 230 231 231 233 233 235 236 236 236 237 237 237 238 238 255 262 263 264 266 267 269 271 273 274 276 277 278 280 283 284

9.1. Ejecucin de AritmExc . . . . . . . . . . . . . . . . . . . . . . . . 291 o 9.2. Ejecucin de ArraySE . . . . . . . . . . . . . . . . . . . . . . . . 291 o

INDICE DE FIGURAS 9.3. 9.4. 9.5. 9.6. 9.7. 9.8. 9.9. Ejecucin del programa ClassCE . . . . . . . . . . . o Deteccin y manejo de excepciones . . . . . . . . . . o Excepciones de tiempo de ejecucin cachadas con una o Ejecucin con relanzamiento de la excepcin . . . . . o o Ejecucin de CaracteristicasExtra . . . . . . . . . o Ejecucin de FinallyTrabaja . . . . . . . . . . . . . o Ejecucin de SiempreFinally . . . . . . . . . . . . . o . . . . . . . . . . . . superclase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

IX

292 296 298 300 310 312 315 322 323 323 323 325 328 329 330 330 360 363 368 375 377 385 392 406 406 413 415 417 430 435 436 438 440 443 449 450

10.1. Algoritmo para el uso de ujos de entrada . . . . . . . . . . . . . 10.2. Algoritmo para el uso de ujos de salida . . . . . . . . . . . . . . 10.3. Funcionamiento de ujo de entrada . . . . . . . . . . . . . . . . . 10.4. Funcionamiento de ujo de salida . . . . . . . . . . . . . . . . . . 10.5. Jerarqu de clases para InputStream. . . . . . . . . . . . . . . . . a 10.6. Jerarqu de clases para OutputStream. . . . . . . . . . . . . . . . a 10.7. Jerarqu de clases para Writer. . . . . . . . . . . . . . . . . . . . a 10.8. Jerarqu de clases para Reader. . . . . . . . . . . . . . . . . . . . a 10.9. Entrada/Salida con proceso intermedio (ltros) . . . . . . . . . . 10.10. lgoritmo para guardar la base de datos en disco . . . . . . . . . A 10.11. lgoritmo para leer registros desde disco . . . . . . . . . . . . . . A 10.12. ormato de un archivo binario autodescrito . . . . . . . . . . . . . F 10.13. lgoritmo para escribir archivo binario . . . . . . . . . . . . . . . A 10.14. lgoritmo para leer de archivo binario . . . . . . . . . . . . . . . A 10.15. lgoritmo para agregar registros desde archivo de acceso directo . A 10.16. lgoritmo para sobrescribir registros en archivo de acceso directo A 10.17. lgoritmo para la lectura de objetos . . . . . . . . . . . . . . . . A 10.18. scritura a un ujo de objetos . . . . . . . . . . . . . . . . . . . . E 11.1. Salida de PingPong. . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2. Salida con asignacin de nombres. . . . . . . . . . . . . . . . . . . o 11.3. Salida de RunPingPong. . . . . . . . . . . . . . . . . . . . . . . . . 11.4. Salida que produce el servidor de impresin. . . . . . . . . . . . . o 11.5. Ejecucin con desalojo voluntario. . . . . . . . . . . . . . . . . . . o 11.6. Ejecucin con la posibilidad de abrazo mortal. . . . . . . . . . . . o 11.7. Ejecucin de Apapachosa2 con abrazo mortal . . . . . . . . . . . . o 11.8. Implementacin de destroy en la mquina virtual de Java. . . . . o a 11.9. Interrupcin de un hilo de ejecucin desde el programa principal. . o o 11.10. erminacin de procesos que son demonios . . . . . . . . . . . . . T o 11.11. erminacin de procesos . . . . . . . . . . . . . . . . . . . . . . . T o

Indice de algoritmos y listados


1.1. Algoritmo para pasar de base 10 a base h. . . . . . . . . . . . . . 1.2. Sumando dos nmeros representados con signo y magnitud. . . . . u 3.1. Encabezado de la interfaz para Reloj (ServiciosReloj) . . 3.2. Encabezado de la interfaz para Manecilla (ServiciosManecilla) 3.3. Acceso a atributos privados de Manecilla . . . . . . . . . . . . . 3.4. Mtodos de implementacin de la interfaz ServiciosManecilla . . e o 3.5. Mtodos de implementacin de la interfaz ServiciosReloj . . . . . e o 3.6. Mtodos de manipulacin para la interfaz ServiciosManecilla . . . e o 3.7. Mtodos de Manipulacin para la interfaz ServiciosReloj . . . . . e o 3.8. Encabezados para la implementacin de Reloj y Manecilla . . . . o 3.9. Constructores para la clase Reloj . . . . . . . . . . . . . . . . . . 3.10. Constructores para la clase Manecilla . . . . . . . . . . . . . . . . 3.11. Mtodos con la misma rma . . . . . . . . . . . . . . . . . . . . . e 3.12. Declaracin de atributos de las clases . . . . . . . . . . . . . . . . o 3.13. Encabezado para el mtodo main . . . . . . . . . . . . . . . . . . e 3.14. Acceso a atributos de los objetos . . . . . . . . . . . . . . . . . . 3.15. Bloqueo de nombres de atributos . . . . . . . . . . . . . . . . . . 3.16. Declaraciones locales en el mtodo muestra de Reloj . . . . . . . e 3.17. Versin nal del encabezado y declaraciones de muestra() . . . . o 3.18. Implementacin de los mtodos de acceso de la clase Manecilla . . o e 3.19. Constructores de la clase Reloj . . . . . . . . . . . . . . . . . . . 3.20. Constructores de la clase Manecillas . . . . . . . . . . . . . . . . 3.21. Implementacin de constructores de la clase Manecilla . . . . . . o 3.22. Implementacin de constructores de la clase Reloj . . . . . . . . . o 3.23. Mtodos de acceso de la clase Manecilla . . . . . . . . . . . . . . e 3.24. Mtodos de manipulacin de la clase Reloj . . . . . . . . . . . . . e o 3.25. Mtodos de manipulacin de la clase Manecilla . . . . . . . . . . e o 3.26. Mtodos de implementacin de la clase Reloj . . . . . . . . . . . e o 3.27. Clase usuaria de la clase Reloj . . . . . . . . . . . . . . . . . . . 4.1. Interfaz para el manejo de una base de datos . . . . . . . . . . . . . 10 . 20 . 66 . 66 . 71 . 72 . 72 . 73 . 73 . 74 . 76 . 77 . 78 . 80 . 80 . 82 . 83 . 85 . 86 . 87 . 89 . 89 . 94 . 94 . 95 . 95 . 96 . 96 . 97 . 105

XII

INDICE DE ALGORITMOS Y LISTADOS 4.2. Posicin de un registro que contenga una subcadena (Consultas) . . o 4.3. Posicin de un registro a partir de otra posicin (Consultas) . . . . o o 4.4. Clase que maneja listas de cursos (Curso) . . . . . . . . . . . . . . 4.5. Constructores para la clase Curso . . . . . . . . . . . . . . . . . . 4.6. Mtodo que regresa toda la lista (Curso) . . . . . . . . . . . . . . e 4.7. Clculo de la posicin donde empieza el i-simo registro (Curso) . . a o e 4.8. Mtodos que regresan el contenido de un campo (Curso) . . . . . . e 4.9. Mtodo que da el primer registro con subcadena (Curso) . . . . . . e 4.10. Mtodo que da el siguiente registro con subcadena (Curso) . . . . . e 4.11. Edicin de un registro individual (Curso) . . . . . . . . . . . . . . o 4.12. Clculo del l a mite de una sucesin . . . . . . . . . . . . . . . . . . o 4.13. Suma de nmeros le u dos . . . . . . . . . . . . . . . . . . . . . . . 4.14. Mtodo que lista todo el curso (Curso) . . . . . . . . . . . . . . . e 4.15. Mtodo que agrega un estudiante a la lista (Curso) . . . . . . . . . e 4.16. Mtodo que elimina al registro i (Curso) . . . . . . . . . . . . . . e 4.17. Mtodo que lista a los que cazan con . . . (Curso) . . . . . . . . . . e 4.18. Ejemplo de identicacin del estado civil de un individuo . . . . . . o 4.19. Encabezado de la clase Menu y el mtodo daMenu (MenuCurso) . e 4.20. Mtodos para agregar estudiante a la base de datos (MenuCurso) . e 4.21. Mtodo que reporta estudiante inexistente (MenuCurso) . . . . . . e 4.22. Mtodo principal de la clase MenuCurso . . . . . . . . . . . . . . e 5.1. Atributos de la clase Estudiante . . . . . . . . . . . . . . . . . . 5.2. Constructores para la clase Estudiante . . . . . . . . . . . . . . . 5.3. Mtodos de acceso y actualizacin de la clase Estudiante . . . . . e o 5.4. Mtodos que arman y actualizan registro completo (1) (Estudiante) e 5.5. Mtodos que arman y actualizan registro completo (2) (Estudiante) e 5.6. Atributos de la clase ListaCurso . . . . . . . . . . . . . . . . . . 5.7. Constructores para la clase ListaCurso 1/2 . . . . . 5.7. Constructores para la clase ListaCurso 2/2 . . . . . 5.8. Mtodos que dan valores de los atributos (ListaCurso) . . . . . . . e 5.9. Recorrido de una lista para contar sus registros (ListaCurso) . . . . 5.10. Modica el nmero de grupo (ListaCurso) . . . . . . . . . . . . . . u 5.11. Agregar un registro al principio de la lista (ListaCurso) . . . . . . . 5.12. Agregar un registro al nal de la lista (ListaCurso) . . . . . . . . . 5.13. Imprimiendo toda la lista (ListaCurso) . . . . . . . . . . . . . . . . 5.14. Imprimiendo registros seleccionados (ListaCurso) . . . . . . . . . . 5.15. Mtodo que busca un registro (ListaCurso) . . . . . . . . . . . . . e 5.16. Eliminacin de un registro en una lista (ListaCurso) . . . . . . . . . o 5.17. Men para el manejo de la lista (MenuLista) . . . . . . . . . . . . u 6.1. Superclase EstudianteBasico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 108 111 113 113 114 115 116 117 118 120 122 123 125 127 129 133 134 137 138 140 142 143 144 145 146 148 148 149 149 150 151 153 154 155 156 158 161 162 168

INDICE DE ALGORITMOS Y LISTADOS 6.2. Encabezado para la subclase EstudianteCurso . . . . . . . . . . . 6.3. Clculo de n! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . a 6.4. Codicacin de iteracin for con while . . . . . . . . . . . . . . . o o 6.5. Construccin de un tringulo de nmeros . . . . . . . . . . . . . . o a u 6.6. Arreglos como parmetros y valor de regreso . . . . . . . . . . . . a 6.7. Campos y constructor de EstudianteCurso . . . . . . . . . . . . 6.8. Mtodos nuevos para la subclase EstudianteCurso . . . . . . . . e 6.9. Redenicin del mtodo getRegistro() . . . . . . . . . . . . . . . o e 6.10. Registros en un arreglo . . . . . . . . . . . . . . . . . . . . . . . . 6.11. Otra versin del mtodo getRegistro . . . . . . . . . . . . . . . . o e 6.12. Clases abstractas y concretas . . . . . . . . . . . . . . . . . . . . 6.13. Interfaz para manejar una lista . . . . . . . . . . . . . . . . . . . . 6.14. Herencia con una interfaz . . . . . . . . . . . . . . . . . . . . . . 7.1. Clase que ilustra el anidamiento dinmico . . . . . . . . . . . . . . a 7.2. La funcin factorial . . . . . . . . . . . . . . . . . . . . . . . . . o 7.3. Factorial calculado iterativamente . . . . . . . . . . . . . . . . . . 7.4. Mtodos para las torres de Hanoi . . . . . . . . . . . . . . . . . . e 8.1. Superclase con informacin bsica de estudiantes (InfoEstudiante) . o a 8.2. Extendiendo la clase InfoEstudiante (EstudianteLista) . . . . . . . 8.3. Extensin de InfoEstudiante con calicaciones (EstudianteCalifs) . o 8.4. Base de datos implementada en un arreglo (CursoEnVector) . . . . 8.5. Corrimiento de registros hacia la derecha e izquierda CursoEnVector) 8.6. Mtodos de acceso y manipulacin e o (CursoEnVector) . . . 8.7. Mtodo de acceso al nmero de registros e u (CursoEnVector) . . 8.8. Agregando registros a la base de datos (CursoEnVector) . . . . . . 8.9. Quitando a un estudiante de la base de datos (CursoEnVector) 8.10. Bsqueda de subcadena en campo del arreglo (CursoEnVector) . . . u 8.11. Listar todos los registros de la base de datos (CursoEnVector) . . . 8.12. Listando los que cumplan con algn criterio (CursoEnVector) . . . . u 8.13. Denicin de la clase Estudiante para los registros (Estudiante) . . o 8.14. Agregar un registro manteniendo el orden (ListaCurso) . . . . . . . 8.15. Clase ArbolEstudiante para cada registro o nodo (ArbolEstudiante) . 8.16. Agregar un registro en un rbol binario ordenado (ArbolOrden) . . . a 8.17. Listado de la base de datos completa (ArbolOrden) . . . . . . . . . 8.18. Recorrido simtrico del rbol (ArbolOrden) . . . . . . . . . . . . . e a 8.19. Bsqueda del registro con el nombre dado (ArbolOrden) . . . . . . u 8.20. Bsqueda de subcadena en determinado campo (ArbolOrden) . . . u 8.21. Listado de registros que contienen a subcadena (ArbolOrden) . . . . 8.22. Localizacin del padre de un nodo (ArbolOrden) . . . . . . . . . . o 8.23. Localiza al menor del subrbol derecho (ArbolOrden) . . . . . . . . a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

XIII

170 181 182 183 187 189 190 191 192 193 195 197 197 202 224 232 234 240 243 244 248 250 251 251 252 254 256 257 258 259 263 268 272 275 275 277 278 280 282 283

XIV

INDICE DE ALGORITMOS Y LISTADOS 8.24. Eliminacin de un nodo en el rbol (ArbolOrden) . . . . . . . . . . . 285 o a 9.1. Ejemplo de una excepcin aritmtica . . . . . . . . . . . . . . . . . 290 o e 9.2. Ejemplo de una excepcin de la subclase ArrayStoreException . . . 291 o 9.3. Programa que lanza una excepcin ClassCastException . . . . . . 292 o 9.4. Manejo de una excepcin a travs de la superclase (CatchExc) . . . . 297 o e 9.5. La excepcin es cachada y relanzada . . . . . . . . . . . . . . . . . 299 o 9.6. Creacin de excepciones propias . . . . . . . . . . . . . . . . . . . . 300 o 9.7. Deteccin de excepciones propias (DivPorCeroUso) . . . . . . . . . . 300 o 9.8. Declaracin de excepcin propia . . . . . . . . . . . . . . . . . . . . 302 o o 9.9. Clase que usa la excepcin creada . . . . . . . . . . . . . . . . . . . 302 o 9.10. Excepciones del programador (I) . . . . . . . . . . . . . . . . . . . . 303 9.11. Uso de excepciones del programador (I) . . . . . . . . . . . . . . . . 303 9.12. Excepciones del programador y su uso (II) (BaseDeDatos) . . . . . . 304 9.13. Denicin de Excepciones propias (RegNoEncontradoException) . . . . 306 o 9.14. Denicin de excepciones propias (ejemplo) (Ejemplo) . . . . . . . . 307 o 9.15. Excepciones creadas por el programador (MiExcepcion2) . . . . . . . 308 9.16. Uso de excepciones creadas por el programador (CaracteristicasExtra) 309 9.17. Ejemplo con la clusula nally (Excepcin) . . . . . . . . . . . . . . 311 a o 9.18. Ejemplo con la clusula nally (uso) . . . . . . . . . . . . . . . . . 312 a 9.19. Otro ejemplo con la clusula nally (Switch) . . . . . . . . . . . . . 313 a 9.20. Otro ejemplo con la clusula nally (OnOException1) . . . . . . . . 313 a 9.21. Otro ejemplo con la clusula nally (OnOException2) . . . . . . . . 313 a 9.22. Otro ejemplo con la clusula nally (OnOSwitch) . . . . . . . . . . 314 a 9.23. Otro ejemplo con la clusula nally (ConFinally) . . . . . . . . . . . 314 a 9.24. Anidamiento de bloques try (CuatroException) . . . . . . . . . . . . 314 9.25. Anidamiento de bloques try (SiempreFinally) . . . . . . . . . . . . . 315 9.26. Manejo de excepciones con herencia . . . . . . . . . . . . . . . . . . 316 10.1. Mtodo que solicita al usuario nombre de archivo (MenuListaIO) . . . 360 e 10.2. Cdigo para guardar la base de datos (MenuListaIO) . . . . . . . . . 361 o 10.3. Opcin para leer registros desde disco (MenuListaIO) . . . . . . . . . 363 o 10.4. Opcin de agregar registros a un archivo en disco (MenuListaIO) . . . 365 o 10.5. Declaraciones de ujos binarios (MenuListaIO) . . . . . . . . . . . . 371 10.6. Opciones de leer y escribir a archivo binario (MenuListaIOReg) . . . . 372 10.7. Salto de bytes en lectura secuencial (MenuListaIOReg) . . . . . . . . 379 10.8. Lectura de nombre de archivo y su apertura (case LEERDIRECTO) . . 386 10.9. Clculo del tamao del registro (case LEERDIRECTO) . . . . . . . . 387 a n 10.10. eticin del nmero de registro al usuario (case LEERDIRECTO) . . . 388 P o u 10.11. osicionamiento de apuntador de archivo y lectura (case LEERDIRECTO)390 P 10.12. pcin de modicar registros (case GUARDARDIRECTO) . . . . . . . 393 O o 10.13. ambios a InfoEstudiante (InfoEstudianteSerial) . . . . . . . . . . . 402 C

INDICE DE ALGORITMOS Y LISTADOS 10.14. odicaciones a la clase Estudiante (EstudianteSerial) . . . M 10.15. onversin de (a) Estudiante a (de) EstudianteSerial . . . C o 10.16. olicitud de nombre para ujo de objetos (MenuListaIOObj) . S 10.17. eclaracin de ujo de objetos (MenuListaIOObj) . . . . . . . D o 10.18. aso de lectura de objetos (declaraciones) (MenuListaIOObj) . C 10.19. aso de lectura de objetos (MenuListaIOObj) . . . . . . . . . C 11.1. Objeto que lanza dos hilos de ejecucin . . . . . . . . . . . . o 11.2. Asignacin de nombres a los hilos de ejecucin . . . . . . . . o o 11.3. Hilos de ejecucin con la interfaz Runnable . . . . . . . . . o 11.4. Manejo sincronizado de una cuenta de cheques . . . . . . . . 11.5. Sincronizacin selectiva sobre objetos . . . . . . . . . . . . . o 11.6. Sincronizacin de variables primitivas en enunciados . . . . . o 11.7. Cola genrica con operaciones sincronizadas . . . . . . . . . e 11.8. Denicin de elementos de la cola . . . . . . . . . . . . . . . o 11.9. PrintJob: trabajo de impresin . . . . . . . . . . . . . . . . . o 11.10. ervidor de impresin que corre en un hilo propio de ejecucin S o o 11.11. ntorno en el que funciona un servidor de impresin . . . . . E o 11.12. ara vericar tiempo transcurrido. . . . . . . . . . . . . . . . P 11.13. esalojo voluntario . . . . . . . . . . . . . . . . . . . . . . . D 11.14. osibilidad de abrazo mortal . . . . . . . . . . . . . . . . . . P 11.15. brazo mortal entre hilos de ejecucin . . . . . . . . . . . . A o 11.16. so de destroy para terminar un hilo de ejecucin . . . . . . U o 11.17.nterrupcin de procesos . . . . . . . . . . . . . . . . . . . . I o 11.18. ignicado de la interrupcin en un proceso . . . . . . . . . . S o 11.19. spera para la terminacin de un coproceso . . . . . . . . . . E o 11.20. rograma principal para ejemplicar la espera . . . . . . . . . P 11.21. ericacin de terminacin con isAlive() . . . . . . . . . . . V o o 11.22. iferencia entre procesos normales y demonios . . . . . . . . D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

XV

403 404 405 406 407 407 412 414 416 419 422 422 426 427 428 428 429 432 434 435 437 440 441 443 444 445 446 447

Introduccin o

La disciplina de la computacin es el estudio sistemtico de proceo a sos algor tmicos que describen y transforman informacin: su teor o a, anlisis, diseo, eciencia, implementacin y aplicacin. La pregunta a n o o fundamental subyacente en toda la computacin es, Qu puede ser o e (ecientemente) automatizado? Peter Denning, 2005. Hay mucha confusin respecto a trminos que, aparentemente describen a la o e misma disciplina. Usaremos a lo largo de estas notas los trminos computacin y e o ciencias de la computacin casi indistintamente. Es necesario recalcar que estamos o usando el trmino computacin como abreviatura para ciencias de la computacin, e o o con el signicado particular que le estamos dando a este ultimo en nuestro con texto. El error ms comn es el de confundir la programacin con la computacin. a u o o La diferencia que existe entre estos dos trminos es tal vez la misma que existe e entre saber la frmula para resolver una ecuacin de segundo grado y conocer la o o teor de ecuaciones. Si bien la programacin es una parte de la computacin, a o o la computacin contempla much o simos otros aspectos que no forzosamente tienen que ver con la programacin o llevarse a cabo con una computadora. Tambin se o e utilizan los trminos de ingenier y ciencias de la computacin y, excepto por el e a o enfoque que se pudiera dar en uno u otro caso, estar amos hablando del mismo cuerpo de conocimientos. Otro trmino que se utiliza frecuentemente (sobre todo en nuestro medio) es e el de informtica. Si bien en muchos casos, se utiliza este trmino para referirse a e

Introduccin o

a todo lo que tiene que ver con computacin, nosotros lo entendemos ms bien o a como rerindose a aquellos aspectos de la computacin que tienen que ver con la e o administracin de la informacin (sistemas de informacin, bases de datos, etc.). o o o Al igual que la programacin, la informtica la podemos considerar contenida o a propiamente en la computacin. o El trmino ciberntica es un trmino forjado por los soviticos en los aos e e e e n cincuenta. Sus ra ces vienen de combinar aspectos biolgicos de los seres vivos o con ingenier mecnica, como es el caso de los robots, la percepcin remota, la a a o simulacin de funciones del cuerpo, etc. A pesar de que se utiliza muchas veces en o un sentido ms general, no lo haremos as en estas notas. a

1.1 Conceptos generales


El objeto fundamental de estudio de las ciencias de la computacin son los o algoritmos y, en su caso, su implementacin. Veamos antes que nada la denicin o o de algoritmo:

Denicin 1.1 Un algoritmo es un mtodo de solucin para un problema que cumple con: o e o
1. Trabaja a partir de 0 o ms datos (entrada). a 2. Produce al menos un resultado (salida). 3. Est especicado mediante un nmero nito de pasos (nitud). a u 4. Cada paso es susceptible de ser realizado por una persona con papel y lpiz a (denicin). o 5. El seguir el algoritmo (la ejecucin del algoritmo) lleva un tiempo nito o (terminacin). o Estamos entonces preocupados en ciencias de la computacin por resolver proo blemas; pero no cualquier problema, sino unicamente aqullos para los que po e damos proporcionar un mtodo de solucin que sea un algoritmo ms adelante e o a en la carrera demostrarn ustedes que hay ms problemas que soluciones, ya no a a digamos soluciones algor tmicas. La segunda parte importante de nuestra disciplina es la implementacin de o algoritmos. Con esto queremos decir el poder llevar a cabo un algoritmo dado (o diseado) de manera automtica, en la medida de lo posible. n a

1.2 Historia

3 En la seccin que sigue exploraremos la historia de estos dos conceptos y o la manera en que se distinguieron para conformar lo que hoy conocemos como ciencias de la computacin. o

1.2 Breve historia del concepto de algoritmo


La computacin es una disciplina mucho muy antigua. Tiene dos ra o ces fundamentales: La bsqueda de una sistematizacin del pensamiento, que dicho en nuestro u o terreno se interpreta como la bsqueda de algoritmos para resolver probleu mas, algunas veces generales y otras concretos. La bsqueda para desarrollar implementos o medios que permitan realizar u clculos de manera precisa y eciente. a En esta segunda ra podemos reconocer los implementos de cmputo como z o el baco chino y el baco japons, las tablillas de los egipcios para llevar la cona a e tabilidad de las parcelas cultivadas y, en general, todos aquellos mecanismos que permit de manera ms expedita, conocer un cierto valor o llevar a cabo un an a clculo. a Respecto a la bsqueda de la sistematizacin del pensamiento, en la Antigua u o Grecia se hizo una contribucin enorme en este direccin con el desarrollo del o o mtodo axiomtico en matemticas y el desarrollo de la geometr como un sistee a a a ma lgico deductivo, en el que juegan un papel mucho muy importante el modus o ponens (pA B Aq B) y el modus tollens (pA B 2B q 2A). Lo que se pretende con estos mecanismos es, a partir de un conjunto de hechos aceptados (axiomas) y mediante un cierto conjunto de reglas del juego (reglas de inferencia o de transformacin) lograr determinar la validez de nuevos hechos. o En el fondo lo que se buscaba era un algoritmo que describiera o automatizara la manera en que los seres humanos llegamos a conclusiones. Estos patrones de razonamiento se utilizan no slo en matemticas, sino en la vida diaria y han o a dado origen a una disciplina muy extensa y rigurosa que es la lgica matemtica. o a No creemos necesario remarcar el gran avance que ha tenido esta disciplina en la ultima mitad del siglo XX. Sin embargo, es importante notar que el desarro llo ha estado siempre presente, no unicamente en este ultimo per odo. Para ello presentamos en la siguiente pgina una tabla con aquellos eventos histricos que a o

Introduccin o

consideramos ms relevantes en el desarrollo de la computacin, con una pequea a o n anotacin de cual fue la aportacin en cada una de las instancias. o o

Cuadro 1.1

Resumen de la historia de Ciencias de la computacin o


2000 AC Tablas para clculos aritmticos, como a e ra cuadradas, inters compuesto, rea ces e a de un c rculo (8/9*1/2=3.1604...) Siglo IV AC Aristteles (384-322 o Lgica formal con Modus Ponens y Moo AC) dus Tollens 825 Abu Jafar Mohammed Libro sobre recetas o mtodos para e ibn Msa u hacer aritmtica con los nmeros e u arbigos a al-Khowrizm` a 1580 Franois Vi`te c e Uso de letras para las incgnitas: surgio (1540-1603) miento del lgebra a 1614 1620 John Napier (1550-1617) Edmund Gunter (1581-1626) Galileo (1564-1642) Huesos de Napier para la multiplicacin. o El concepto de logaritmo Primer antecesor de la regla de clculo a Formulacin matemtica de la F o a sica Descubre la geometr anal a tica, posibilitando la aplicacin del lgebra a probleo a mas geomtricos, y por lo tanto a problee mas que ten que ver con movimiento an f sico Primera calculadora digital: sumaba, restaba automticamente; multiplicaba y a divid semiautomtico a a Calculadora que sobrevivi. Slo sumaba o o y restaba automticamente a Coinventor del clculo con Newton. a Primer investigador occidental de la aritmtica binaria. Inventor de la ruee da de Leibniz, que hac las 4 operacioa nes aritmticas. Fundamentos de la lgie o ca simblica o Babilonios y egipcios

Siglos XVI y XVII Siglo XVI y Descartes (1596-1650) XVII

1623

Wilhelm Schickard

1642-1644 Siglo XVII y XVIII

Blaise Pascal (1623-1662) Gottfried Wilhelm Leibniz (1646-1717)

1.2 Historia

Cuadro 1.1

(cont.) Resumen de la historia de Ciencias de la computacin o


Siglo XIX 1800 1854 1854 1890 1893 1900 Charles Babbage (1791-1871) Ada Lovelace Mquina diferencial y mquina anal a a tica

1928

1936

1936

1939-1945

1937-1942

Primer programador de la mquina a anal tica de Babbage Pehr George Scheutz Construy un modelo de la mquina dio a ferencial de Babbage George Boole Estableci los fundamentos para el estuo (1815-1864) dio moderno de la Lgica Formal o Herman Hollerith Uso de equipo tabulador (de registro unitario) Leonardo Torres y Mquina electromecnica basada en la de a a Quevedo (1852-1936) Babbage David Hilbert Propone a los matemticas el encontrar a (1862-1943) un sistema de axiomas lgico matemtio a co unico para todas las reas de la ma a temtica a Hollerith Elaboracin de tablas de posicin de la o o luna utilizando su mquina de registro a unitario: uso cient co de herramientas pensadas para procesamiento de datos Kurt Gdel (1906-1978) Demuestra que lo que propone Hilbert o no es posible, i.e. que hay problemas matemticos inherentemente insolubles a Alan Turing Atac el problema de cuando se puede o (1912-1954) decir que se tiene un mtodo de solucin, e o que el problema no tiene solucin, etc. o Disea la Mquina de Turing n a Wallace J. Eckert con Extensin de la mquina tabuladora de o a John W. Mauchly IBM para propsitos cient o cos. Diseo n y construccin de la ENIAC, primeo ra gran computadora digital totalmente electrnica o Claude Shannon El uso del lgebra booleana para el anlia a sis de circuitos electrnicos. Liga entre la o teor y el diseo a n

Introduccin o

Cuadro 1.1

(cont.) Resumen de la historia de Ciencias de la computacin o


1940 1937-1944 John V. Atanaso (1903-) Howard T. Aiken (1900-1937) Solucin de ecuaciones lineales sio multneas. Computadora ABC a Construccin de la MARK I que ten o a: Posibilidad de manejar nmeros u positivos y negativos Posibilidad de utilizar diversas funciones matemticas a Totalmente automtica a Posibilidad de ejecutar operaciones largas en su orden natural ENIAC. Primera computadora totalmente electrnica o Notacin para describir la circuiter de o a la computadora. Conjunto de instrucciones para la EDVAC. El concepto de programa almacenado - la nocin de que los o datos y los programas pueden compartir el almacenaje. El concepto de operacin o serial Aritmtica binaria como mecanise mo en las computadoras

1943-1945 1944

John W. Mauchly John von Neumann

1.3 Sistemas numricos e


A lo largo de esta historia se han transformado notablemente los s mbolos que se usaron para denotar a los objetos de los clculos, a los algoritmos, y a los a resultados mismos. Podemos pensar, por ejemplo, en que una sumadora de las de Shickard usaba engranes como s mbolos para realizar las cuentas. El hombre primitivo usaba notacin unaria para contar: pon tantos s o a mbolos como objetos deseaba contar. Una primera abreviatura a estos mtodos se dio con sistemas e que agrupaban s mbolos. As por ejemplo, el sistema de nmeros romanos com u bina s mbolos para abreviar, y en lugar de escribir 100 rayas verticales utiliza el s mbolo C. Un concepto importante es el del nmero cero. Su origen es mucho muy anu

1.3 Sistemas numricos e

tiguo. Tambin los mayas ten un s e an mbolo asociado al cero. Este concepto es muy importante para poder utilizar notacin posicional. La notacin posicional o o es lo que usamos hoy en d y consiste en que cada s a, mbolo tiene dos valores asociados: el peso y la posicin. Por ejemplo, el nmero 327.15 se puede presentar o u de la siguiente manera:

EJEMPLO 1.3.2
327,15

 3 102 2 101 7 100 1 101 5 102

Decimos que la notacin es posicional, porque dependiendo de la posicin que o o tenga un d gito con respecto al resto de los d gitos en un nmero, se es el valor u e (o peso) que tiene. El sistema que usamos es el decimal posicional, porque es un sistema posicional que usa al nmero 10 como base. El peso que se le asigna a cada u d gito depende del d gito y de su posicin. Cada posicin lleva un peso de alguna o o potencia de 10 (decimal) asignadas, alrededor del punto decimal, de la siguiente forma: 104 103 102 101 100 | 101 102 103 . . . 10000 1000 100 10 1 | ,1 ,01 ,001 4 5 1 3 |. 6 3 0 1 0 0 |. 0 0 0 |. 0 7 5

... 4513,6  30100  ,075 

Hay varias reglas que observamos respecto a la notacin posicional: o


I. II.

En cada posicin se coloca un solo d o gito. Los ceros antes del primer d gito distinto de cero, desde la izquierda, no aportan nada al nmero. u Los ceros a la derecha del punto decimal y antes de un d gito distinto de cero s cuentan. No es lo mismo .75 que .00075. Los ceros a la derecha del ultimo d gito distinto de cero despus del punto e decimal no cuentan.

III.

IV.

8
V. VI.

Introduccin o

Los d gitos que podemos utilizar son del 0 al 9. Cada d gito aporta su valor espec co multiplicado por el peso de la posicin o que ocupa.

Sabemos todos trabajar en otras bases para la notacin posicional. Por ejemo plo, base 8 (mejor conocida como octal ) ser de la siguiente manera: a

EJEMPLO 1.3.3
... 476,18 413708 84 83 82 81 80 | 81 82 83 ... 4096 512 64 8 1 | ,125 ,15625 ,001953125 4 7 6 |. 1  318,12510 4 1 3 7 |.  214310

Tambin podemos pensar en base 16 (hexadecimal ), para lo que requerimos de e 16 s mbolos distintos. Los d gitos del 0 al 9 nos proporcionan 10 de ellos. Tenemos el problema de que con la restriccin de que cada posicin debe ser ocupada por un o o unico d gito, tenemos que inventar s mbolos (o d gitos) para 6 valores que nos faltan, y que ser del 10 al 15 inclusive. La tradicin es utilizar las letras A, B, an o C, D, E y F para los valores consecutivos 10, 11, 12, 13, 14 y 15 respectivamente. Siguiendo la notacin posicional, pero en base 16, tenemos los siguientes ejemplos: o ... 476,116 BA7C16 164 163 162 161 160 | 161 162 ... 65536 4096 256 16 1 | ,0625 ,00390625 4 7 6 |. 1  1142,062510 11 10 7 12 |.  4774010

Pasamos ahora a la base 2, que es la ms importante hoy en d en compua a tacin. Los primeros implementos de cmputo que usaban notacin posicional (los o o o a bacos, huesos de Napier, calculadoras) usaban base 10 (los bacos, en realidad, a usaban una especie de base 5. Por qu?). Mientras las calculadoras se constru e an con partes f sicas (engranes, cuentas) la base 10 no era muy distinta de cualquier otra base. Pero al intentar construir calculadoras (o computadoras) electrnicas, o la base 10 presentaba problemas de implementacin: cmo distinguir entre el 4 o o y el 5, cuando se estaba midiendo en trminos de niveles, analgicamente? Por lo e o tanto se opt, aunque no desde el principio, por usar base 2, que tiene un mapeo a o

1.3 Sistemas numricos e

la electrnica bastante simple. Para base 2 requerimos de dos s o mbolos, y utilizamos el 0 y el 1. El 0 se puede interpretar como ausencia y el 1 como presencia, por lo que ms all de cierto nivel se asume presencia. Esto es mucho ms sencillo que a a a ver si tenemos uno de 10 (u 8) niveles distintos. La tabla que sigue corresponde a la notacin posicional en base 2: o ... 11001101,112 1010002 27 26 25 24 23 22 21 20 | 21 22 23 . . . 128 64 32 16 8 4 2 1 | ,5 ,25 ,125 1 1 0 0 1 1 0 1 |. 1 1 1  205,7510 1 0 1 0 0 0 |.  4010

Como se puede ver, tratndose de nmeros enteros, es fcil pasar de una base a u a cualquiera a base 10, simplemente mediante la frmula o num10

0 i n

di bi

donde di se reere al d gito correspondiente en la i-sima posicin, con la posicin e o o 0 en el extremo derecho, y bi se reere a la base elevada a la potencia de la posicin o correspondiente. 1010002

0 i 5

1 25 0 24 1 23 0 22 0 21 0 20  32 0 8 0 0 0  4010
Para pasar de base 10 a cualquier otra base, se utiliza el algoritmo 1.1. Hay que notar que en la l nea 7 se est concatenando un s a mbolo, por lo que si la base es mayor a 10, se tendrn que utilizar s a mbolos para los d gitos mayores que 9. As para pasar de base 10 a base 16 el nmero 857510 el algoritmo se , u ejecutar de la siguiente manera: a dividendo10 cociente10 residuo10 S mbolo residuo16 8575 535 15 F 535 33 7 7 33 2 1 1 2 0 2 2 N mero16 u F 7F 17F 217F

di 2i

10

Introduccin o

Algoritmo 1.1 Algoritmo para pasar de base 10 a base h.


1: 2: 3: 4: 5: 6: 7: 8: 9:

dividendo = num10 ; divisor = h; residuo = 0; nmero = ; u repeat residuo = dividendo % divisor; nmero = pegar(residuo,nmero); u u dividendo = dividendo divisor; until (dividendo = 0);

Para que nos convenzamos que, en efecto 857510 descomponer 217F en potencias de 16: 217F16

217F16 , procedemos a

 2 163 1 162 7 161 15 160  2 4096 1 256 7 16 15  8192 256 112 15  8575

En general, para pasar de una base a otra, todo lo que se tiene que hacer es la divisin en la base en la que se encuentra el nmero que queremos convertir. Por o u ejemplo, si deseamos pasar un nmero base 8 a base 6, lo har u amos de la siguiente manera:
I.

Consideremos que para base 8 contamos con los d gitos del 0 al 7, mientras que para base 6 unicamente los d gitos del 0 al 5. Seguimos el algoritmo 1.1, pero haciendo la divisin y la resta en base 8. El o resultado se muestra a continuacin: o dividendo8 cociente8 residuo8 S mbolo residuo6 7535 1217 3 3 1217 155 1 1 155 22 1 1 22 3 0 0 3 0 3 3 N mero6 u 3 13 113 0113 30113

II.

1.3 Sistemas numricos e

11

Trabajemos con los dos nmeros en base 10 para corroborar que el algoritmo u trabaj bien: o

    301136    
75358

7 83 5 82 3 81 5 80 7 512 5 64 3 8 5 1 3584 320 24 5 393310 3 64 0 63 1 62 1 61 3 60 3 1296 0 216 1 36 1 6 3 1 3888 0 36 6 3 393310

En general, para pasar de una base B a otra base b, lo que tenemos que hacer es expresar b en base B, y despus llevar a cabo el algoritmo en base B. Cada vez e que tengamos un residuo, lo vamos a tener en base B y hay que pasarlo a base b. Esto ultimo es sencillo, pues el residuo es, forzosamente, un nmero entre 0 y b. u Cuando una de las bases es potencia de la otra, el pasar de una base a la otra es todav ms sencillo. Por ejemplo, si queremos pasar de base 8 a base 2 (binario), a a observamos que 8  23 . Esto nos indica que cada posicin octal se convertir a o a exactamente a tres posiciones binarias. Lo unico que tenemos que hacer es, cada d gito octal, pasarlo a su representacin binaria: o 75358 7 5 3 5 111 101 011 101 1111010111012

Algo similar se hace para pasar de base 16 a base 2, aunque tomando para cada d gito base 16 cuatro d gitos base 2. El proceso inverso, para pasar de base 2, por ejemplo, a base 16, como 16  24 deberemos tomar 4 d gitos binarios por cada d gito hexadecimal: 1111010111012 11112 01012 11012 1510 510 1310 F16 516 D16 F 5D Las computadoras actuales son, en su inmensa mayor digitales, esto es, que a, representan su informacin de manera discreta, con d o gitos. Operan en base 2

12

Introduccin o

(binario) ya que la electrnica es ms sencilla en estos trminos. Sin embargo, o a e hay procesos que no son discretos, como las ondas de luz o sonoras. Pero hoy en d se pueden alcanzar excelentes aproximaciones de procesos continuos mediante a d gitos binarios. Para ello se cuenta con componentes analgicos/digitales que o transforman seales analgicas (continuas) en seales digitales (discretas). Hubo n o n una poca en que se ten mucha fe en las computadoras analgicas, aquellas que e a o funcionaban con dispositivos continuos, pero prcticamente han desaparecido del a mercado, excepto por algunas de propsito muy espec o co, o las que convierten seales analgicas en seales digitales, o viceversa. n o n La siguiente pregunta que debemos hacernos es: Cules son los distintos elementos que requerimos para poder implementar un a algoritmo en una computadora digital? Cmo se representan en binario esos distintos elementos? o Pensemos, por ejemplo, en las mquinas de escribir. La orden para que se a escriba una letra determinada se lleva a cabo oprimiendo una cierta tecla. Esto es porque hay una conexin mecnica (f o a sica) entre la tecla del teclado y el dado que imprime la tecla. La primera computadora, la ENIAC, funcionaba de manera muy similar. Cada vez que se deseaba que resolviera algn problema, se alambraban u los paneles de la computadora para que hubiera conexiones f sicas entre lo que se recib en el teletipo y las operaciones que se ejecutaban. De manera similar, a las calculadoras mecnicas, al darle vuelta a una manivela, se consegu que los a a engranes seleccionados efectuaran una determinada operacin. o En las computadoras modernas, de propsito general, vienen alambradas para o reconocer ciertos patrones, de forma similar a como lo hac el telar de Jackard o la a mquina del censo de Hollerith. Cada patrn indica una operacin a realizarse. Los a o o patrones son nmeros binarios con un nmero jo de posiciones (binary digits). A u u cada conjunto de posiciones de un cierto tamao se le llama una palabra. El tamao n n de palabra es, en general, una potencia de 2: 8, 16, 32, 64, 128. A los grupos de 8 bits se les llama byte. Al conjunto de patrones distintos es a lo que se conoce como lenguaje de mquina, que por supuesto es personal de cada modelo o tipo de a computadora. Originalmente se distingu entre micro, mini y computadoras por el a tamao en bits de sus palabras. El tamao de la palabra va unido al poder de la n n o mquina: si tengo ms bits por palabra tengo ms patrones posibles, y por lo tanto a a a un lenguaje de mquina ms extenso y posibilidad de representar nmeros ms a a u a 1 grandes o con ms precisin Las primeras microcomputadores ten palabras de a o an 8 bits, mientras que las grandes computadoras ten palabras de 64 bits. Las an
1

En breve veremos la representacin de datos en la computadora. o

1.4 La arquitectura de von Neumann

13

supercomputadoras, que surgieron alrededor de 1985, ten palabras de 128 bits an y posibilidades de proceso en paralelo. El tamao de la palabra tambin le da velocidad a una computadora, pues n e indica el nmero de bits que participan electrnicamente en cada operacin. u o o

1.4 La arquitectura de von Neumann


Se le llama arquitectura de una computadora a la organizacin qu tiene en sus o e componentes electrnicos, y la manera como stos estn integrados para funcionar. o e a Lo que se conoce como arquitectura de von Neumann es una organizacin muy o parecida a la de Babbage: tenemos un procesador central el molino de Babbage en el que se ejecutan las operaciones aritmticas y de comparacin (lgicas); una e o o memoria central que se utiliza para almacenar datos, resultados intermedios y el programa a ejecutarse; tenemos unidades de entrada y salida (input/output) que sirven para darle a la computadora el programa y los datos y recibir los resultados; por ultimo, tenemos memoria externa o auxiliar, como discos, diskettes, cintas magnticas, que nos sirven para almacenar, ya sean datos o programas, de una e ejecucin a otra, sin tener que volver a realizar el proceso, o sin que tengamos que o volverlos a proporcionar. Un esquema de una computadora con arquitectura de von Neumann se muestra en la gura 1.1. Las echas que van de un componente a otro pueden tener distintas formas de funcionar y muy diversas capacidades. Una de las formas en que funciona es lo que se conoce como un bus. Por ejemplo, si la capacidad de la l nea que va de memoria al procesador es de menos de 1 palabra, aunque la computadora tenga palabras muy grandes, la velocidad de la mquina se va a ver afectada. a La memoria est cuadriculada, o dividida en celdas. Cada celda ocupa una a posicin dentro de la memoria, aunque en principio cada una es igual a cualquier o otra: tiene el mismo nmero de bits y las mismas conexiones. Se puede ver como un u vector de celdas, cuyo primer elemento tiene el ndice cero. Se habla de posiciones altas y posiciones bajas rerindose a aqullas que tienen e e ndices grandes o pequeos respectivamente. En cada celda de memoria se puede colocar (escribir, n copiar) una instruccin, un nmero, una cadena de carcteres, etc. o u a El proceso mediante el que se ejecuta un programa es el siguiente:
I.

Se coloca el programa en la memoria de la computadora (se carga, load ).

14
II.

Introduccin o

La unidad de control en el procesador se encarga de ver cul es la siguiente a instruccin. Esta aparece, simplemente, como un patrn de bits (un cdigo o o o de mquina). a La Unidad de Control se encarga de ejecutar la instruccin, valindose para o e ello de cualquiera de los componentes de la mquina. a

III.

Figura 1.1

Arquitectura de von Neumann

Memoria auxiliar
Discos

Memoria Central

Procesador central
Unidad de control Unidad aritmtica e Unidad lgica o Cache

Dispositivos de entrada
Teclado, scanner, usb

Dispositivos de salida
Monitor, impresora, usb, bocinas

El tipo de instrucciones que tiene una computadora incluyen instrucciones para sumar, restar, multiplicar, dividir, copiar, borrar, recorrer el patrn de bits, o

1.4 La arquitectura de von Neumann

15

comparar y decidir si un nmero es mayor que otro, etc. En realidad son instrucu ciones que hacen muy pocas cosas y relativamente sencillas. Recurdese que se e hace todo en sistema binario.

Lenguajes de programacin o
Un lenguaje de programacin es aqul que nos permite expresar un problema o e de tal manera que podamos instalarlo (cargarlo) en la computadora y se ejecute. Hasta ahora slo hemos visto el lenguaje de mquina, y ste era el unico disponible o a e con las primeras computadoras de propsito general. o

Figura 1.2

Proceso para ejecutar un programa escrito en ensamblador

(4) (3)
Datos para el programa Programa a ejecutar Programa ensamblador (binario) Unidad de control Programa objeto (binario)

(5) (2) MEMORIA (6)


Resultados

(1)

16

Introduccin o

Programar en binario es, en el mejor de los casos, sumamente tedioso y complicado. El programador (que es quien escribe los programas) tiene que tener un conocimiento muy profundo de la computadora para la que est programando. a Adems de eso, tiene que ejercer un cuidado extremo para no escribir ningn 1 a u por 0, o para no equivocarse de direccin. Lo primero que se hizo fue escribir en o octal, ya que era un poco ms claro que binario. El siguiente paso fue asociar a nemnicos a las instrucciones, asociando a cada patrn de bits un nombre o o o identicador: add, sub, mul, div, etc. A esto se le llam lenguaje ensamblador. Se o construy un programa, llamado ensamblador, que se encargaba de traducir los o nemnicos de este estilo y las direcciones escritas en octal a binario. Este programa o no es algo complicado de hacer. Tanto el programa ensamblador como el programa a traducir se alimentaban, cargados en tarjetas perforadas ver gura 1.2. El primer paquete era el programa ensamblador, escrito en binario; a continuacin o se presentaba el programa que se deseaba traducir, como datos del primero. La computadora contaba con un tablero, en el que se le indicaba que empezara a cargar el programa ensamblador, y una vez cargado (1), empieza a ejecutarlo. El programa ensamblador indicaba que ten que traducir las siguientes tarjetas (o a cinta perforada), conforme las fuera leyendo (2), y producir tarjetas con el programa en binario (3) o, ms adelante, cargar el programa binario a memoria para a ser ejecutado (4); al ejecutarse el programa en binario, se le los datos (5) y se an produc el resultado (6). a El siguiente paso en lenguajes de programacin fue el de los macroensambladoo res, que asignaban etiquetas a las posiciones de memoria que se estaban utilizando para acomodar los datos y resultados intermedios, y las posiciones donde iba a quedar el cdigo. El proceso de traduccin era sencillo, ya que se agregaban a o o las tablas de traduccin del cdigo los equivalentes en octal. Tambin se permit o o e a construir secuencias pequeas de cdigo, a las que nuevamente se les asociaba un n o nombre o identicador, y que pod presentar parmetros. A estas secuencias se an a les llamaba macros y de ah el nombre de macroensamblador. Las computadoras se estaban utilizando tanto con nes cient cos como comerciales. En el uso cient co era muy comn expresar frmulas matemticas, que u o a ten que despedazarse en operaciones bsicas para poderse llevar a cabo ver an a gura 1.3. El siguiente paso importante fue el de permitirle a un programador que especicara su frmula como se muestra en la parte izquierda de la gura 1.3. El o primer lenguaje de uso generalizado orientado a esto fue FORTRAN For mula Translator, alrededor de 1956. Pocos aos despus se desarroll un lenguaje para n e o usos comerciales, donde lo que se deseaba es poder manejar datos a distintos niveles de agregacin. Este lenguaje se llamaba COBOL COmon B ussiness Oriented o Language. Ambos lenguajes, o versiones modernizadas, sobreviven hasta nuestros

1.4 La arquitectura de von Neumann

17

Figura 1.3

Codicacin en ensamblador de frmulas matemticas. o o a

Frmula o
 b c2
b 2a

Programa simplicado
4ac
def def def def def def def def x1 a b c ac a2 b2 rad 100 102 104 106 108 110 112 114 mul mul mul mul add sqrt sub div b2 b ac a ac 4 a2 2 rad ac rad rad x1 rad x1 x1 b c ac a b2 b a2

x1

d as. Estos lenguajes ten un formato tambin ms o menos estricto, en el sentido an e a de que las columnas de las tarjetas perforadas estaban perfectamente asignadas, y cada elemento del lenguaje ten una posicin, como en lenguaje ensamblador. a o El proceso por el que tiene que pasar un programa en alguno de estos lenguajes de programacin para ser ejecutado, es muy similar al de un programa escrito o en lenguaje ensamblador, donde cada enunciado en lenguaje de alto nivel se traduce a varios enunciados en lenguaje de mquina (por eso el calicativo de a alto nivel, alto nivel de informacin por enunciado), que es el unico que puede o ser ejecutado por la computadora. Hacia nales de los aos 50 se dise un lenguaje, ALGOL ALGorithmic n no Oriented Language que result ser el modelo de desarrollo de prcticamente o a todos los lenguajes orientados a algoritmos de hoy en d como Pascal, C, C++, a, Java y muchos ms. No se pretende ser exhaustivo en la lista de lenguajes, basa te mencionar que tambin en los aos 50 surgi el lenguaje LISP, orientado a e n o inteligencia articial; en los aos 60 surgi BASIC, orientado a hacer ms fcil n o a a el acercamiento a las computadoras de personas no forzosamente con antecedentes cient cos. En los aos 60 surgi el primer lenguaje que se puede considerar n o orientado a objetos, SIMULA, que era una extensin de ALGOL. En los aproxio madamente 50 aos que tienen en uso las computadoras, se han diseado y usado n n ms de 1,000 lenguajes de programacin, por lo que pretender mencionar siquiera a o a los ms importantes es una tarea titnica, y no el objetivo de estas notas. a a

18

Introduccin o

Representacin de la informacin o o
Acabamos de ver que la representacin de los programas debe ser, eventualo mente en lenguaje de mquina, o sea, en binario. Tambin tenemos restricciones a e similares para el resto de la informacin, como son los datos, los resultados intero medios y los resultados nales. Al igual que con los lenguajes de programacin, o si bien la computadora slo tiene la posibilidad de representar enteros positivos o en binario, debemos encontrar la manera de poder representar letras, nmeros de u varios tipos, como enteros negativos, reales, racionales, etc. Para ello se sigue una lgica similar a la del lenguaje de mquina. o a

Carcteres a
Supongamos que tenemos un tamao de palabra de 16 bits y queremos repren sentar letras o carcteres. Simplemente hacemos lo que hac a amos en la primaria cuando quer amos mandar mensajes secretos: nos ponemos de acuerdo en algn u cdigo. o El primer cdigo que se utiliz fue el BCD, que utilizaba 6 bits por carcter. o o a Con esto se pod representar 64 carcteres distintos (en 6 bits hay 64 posibles an a enteros, del 0 al 63). Con este cdigo alcanzaba para las maysculas, los d o u gitos y algunos carcteres importantes como los signos de operacin y de puntuacin. a o o Con el perfeccionamiento de las computadoras se requirieron cada vez ms a carcteres, por lo que se extendi el cdigo a 7 y 8 bits, con el cdigo ASCII, que a o o o se us mucho para transmitir informacin, y el cdigo EBCDIC que se us como o o o o cdigo nativo de muchas computadoras, respectivamente. El lenguaje Java utiliza o Unicode, que ocupa 16 bits, para representar a cada carcter. Con esto tiene la a posibilidad de utilizar casi cualquier conjunto de carcteres de much a simos de los idiomas en uso actualmente. Se requiere de programas que transformen a un carcter en su cdigo de mquia o a na y viceversa. Estos son programas sencillos que simplemente observan patrones de bits y los interpretan, o bien, observan carcteres y mediante una tabla, a los convierten al patrn de bits en el cdigo que utilice la computadora. o o Prcticamente todo manual de programacin trae una tabla de los distintos a o cdigos que corresponden a los carcteres. Estas tablas vienen con varias columnas; o a en la primera de ellas vendr el carcter, y en columnas subsecuentes su cdigo a a o en octal hexadecimal, binario, y utilizando alguno de estos esquemas para dar

1.4 La arquitectura de von Neumann

19

el cdigo que le corresponde en ASCII, EBCDIC o Unicode. Por supuesto que o requerimos de 32,768 para mostrar la codicacin de Unicode, por lo que no o lo haremos, mas que en la medida en que tengamos que conocer el de algunos carcteres espec a cos.

Nmeros enteros u
Ya vimos la manera en que se representan nmeros enteros en la computadora: u simplemente tomamos una palabra y usamos notacin posicional binaria para ver o el valor de un entero. Hoy en d las computadoras vienen, en su gran mayor con palabras de al a a, menos 32 bits. Eso quiere decir que podemos representar enteros positivos que van desde el 0 (32 bits apagados) hasta 232 1 (todos los bits prendidos). Pero, cmo le hacemos para representar enteros negativos? Tenemos dos opciones. La o primera de ellas es la ms intuitiva: utilizamos un bit, el de la extrema izquierda, a como signo. A esta notacin se le llama de signo y magnitud. Si ste es el caso, o e tenemos ahora 31 bits para la magnitud y 1 bit para el signo ver gura 1.4 con palabras de 16 bits.

Figura 1.4

Enteros en signo y magnitud.


s 214 213 212 211 210 29 28 27 26 25 24 23 22 21 20 0 0 0 0 1 0 1 1 0 1 1 1 0 0 1 1 1 0 0 0 1 0 1 1 0 1 1 1 0 0 1 1

1467 1467

La representacin se signo y magnitud es muy costosa. Por ejemplo, cuando o se suman dos cantidades que tienen signos opuestos, hay que ver cul es la que a tiene mayor magnitud, pues la suma se puede convertir en resta de magnitudes. Veamos el algoritmo 1.2 en la siguiente pgina. a Como se puede ver, los circuitos que se requieren para sumar dos nmeros en u notacin de signo y magnitud son muy complicados, y por lo tanto muy caros. o El otro modo de codicar enteros positivos y negativos es lo que se conoce como complemento a 2. En este mtodo, al igual que con signo y magnitud, se e parte al dominio en 2 partes, los que tienen al bit de potencia ms alta en 0, y los a que lo tienen en 1; los que tienen el bit ms alto en cero son los enteros positivos, a y los que lo tienen en 1 son los enteros negativos. Para saber la magnitud del nmero, en el caso de los positivos se calcula igual que con signo y magnitud, pero u

20

Introduccin o

Algoritmo 1.2 Sumando dos nmeros representados con signo y magnitud. u


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:

Sean a y b los enteros a sumar. Sa  signo de a; Ma  magnitud de a. Sb  signo de b; Mb  magnitud de b. if Sa  Sb Ssuma  Sa . Msuma  Ma Mb . else if Ma Mb Ssuma  Sa . Msuma  Ma Mb . else Ssuma  Sb . Msuma  Mb Ma .

en el caso de los negativos, la magnitud es la que resulta de restar la palabra de una con una posicin ms, y donde todas las posiciones originales de la palabra o a tienen 0, con un 1 en la posicin extra. Veamos algunos ejemplos en la gura 1.5 o en la pgina opuesta. a Como vemos en la gura 1.5, el bit 15 (el que corresponde a 215 ) tambin e nos indica de cierta forma, como en la notacin de signo y magnitud, cuando o tenemos un entero negativo. En el caso de 16 bits, los enteros positivos son del 0 al 215 1  32, 767 que corresponde a una palabra de 16 bits con todos menos el bit 15 prendidos ver gura 1.5, l nea (1). A partir del nmero 215  32, 768 y hasta 216 u 1  65, 535, que corresponde a todos los bits en una palabra de 16 bits prendidos, estamos representando a nmeros negativos ver en la gura 1.5, l u nea (3). En estos ultimos, el bit 15 est siempre prendido, y por eso reconocemos el signo a del nmero. La magnitud (o el valor absoluto) del nmero que estamos viendo se u u obtiene sacando el complemento a 2 de la palabra en cuestin. El complemento a o 2 se obtiene de dos maneras posibles: I. Se resta en binario de un n mero de 17 bits en la gura 1.5, l u nea (2) con ceros en todos los bits, menos el bit 16, que tiene 1. II. Se complementan cada uno de los bits de la palabra de 16 bits (se cambian los 1s por 0s y los 0s por 1s) y despus se le suma 1 a lo que se obtuvo. e Ambas maneras de obtener la magnitud (el complemento a 2) se observa en la l nea (4) de la gura 1.5. La gran ventaja de la notacin en complemento a 2 es que es sumamente fcil o a hacer operaciones aritmticas como la suma y la resta. En complemento a 2, todo lo e que tenemos que hacer es sumar (o restar) utilizando los 16 bits. Pudiera suceder,

1.4 La arquitectura de von Neumann

21

sin embargo, que el resultado no sea vlido. Por ejemplo, si sumamos dos nmeros a u positivos y el resultado es mayor que la capacidad, tendremos acarreo sobre el bit 15, dando aparentemente un nmero negativo. Sabemos que el resultado es u invlido porque si en los dos sumandos el bit 15 estaba apagado, tiene que estar a apagado en el resultado. Algo similar sucede si se suman dos nmeros negativos u y el resultado ya no cabe en 16 bits ver gura 1.6.

Figura 1.5

Nmeros en complemento a 2 u

215 214 213 212 211 210 29 28 27 26 25 24 23 22 21 20 Un entero positivo en complemento a 2: 0 0 0 0 1 1 1 0 0 0 0 0 1 0 0 0 La palabra con el complemento a 2: 1 0 0 0 0 0 0 0 0

3, 592 216  65, 536

p1q p2q p3q p4q

Un entero negativo en complemento a 2: 1 0 0 0 1 1 1 0 0 La magnitud del entero original: 0 0 1 1 1 0 0 0

36, 360

29, 176

Figura 1.6

Suma de dos nmeros con complemento a 2 u


215 214 213 212 211 210 29 28 27 26 25 24 23 22 21 20 0 0 0 0 0 0 0 0 1 1 0 0 1 1 1 1 0 0 0 1 0 0 0 1 1 1 0 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 1 0 1 1 0 0 1 1 0 0 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 1 1 1 1 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 0 207 4547 4754 25 939

914

22

Introduccin o

Pueden vericar, sumando las potencias de 2 donde hay un 1, que las sumas en la gura 1.5 se hicieron directamente y que el resultado es el correcto. La desventaja del complemento a 2 es que se pueden presentar errores sin que nos demos cuenta de ello. Por ejemplo, si le sumamos 1 al mximo entero positivo a (una palabra con 0 en el bit 15 y 1s en el resto de los bits) el resultado resulta ser un nmero negativo, aquel que tiene 1s en todas las posiciones de la palabra u ver gura 1.7.

Figura 1.7

Sumando 1 al mximo entero positivo a


215 214 213 212 211 210 29 28 27 26 25 24 23 22 21 20 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32767 1

32768

En algunos lenguajes de programacin este tipo de errores se detectan en o ejecucin, pero en la mayor no. Hay que tener presente que la representacin o a o interna es con complemento a 2, para manejar de manera adecuada este tipo de posibles errores. Muchas veces el tamao de la palabra de una computadora no es suciente n para los nmeros que deseamos representar. Entonces, el lenguaje de programacin u o puede usar ms de una palabra para representar enteros, simplemente utilizando a notacin posicional base 216 , de manera similar a como se maneja la base 2. Se o dice entonces que la aritmtica se hace por software. e

Nmeros reales u
Para representar a los nmero reales tenemos realmente dos opciones: u Punto jo: Como su nombre lo indica, el fabricante (o el lenguaje de programacin) elige una posicin entre dos bits, y lo que se encuentra a la derecha de o o esa posicin es la parte fraccionaria y lo que se encuentra a la izquierda es o la parte entera, ambos vistos como enteros en notacin posicional binaria o ver gura 1.8. Como se puede ver en la gura 1.8, se mantiene la notacin de complemento o a 2, el bit ms alto indicndonos que el nmero es negativo. a a u

1.4 La arquitectura de von Neumann

23

Figura 1.8

Notacin de punto jo. o


27 26 25 24 23 22 21 20 0 1 0 0 1 1 0 1 1 0 0 1 1 0 1 1

27
0 0

26 25 24 23 22 21 20 1 0 0 0 0 1 1 1 0 1 0 1 1 0

77,67 100,162

Una de las ventajas de este tipo de notacin es que es muy sencillo hacer o operaciones aritmticas, pues se usa a toda la palabra como si fuera un entero e y el proceso de colocar el punto decimal se hace al nal. Sin embargo, tiene una gran desventaja que es la poca exibilidad para representar nmeros u que tengan muchos d gitos en la fraccin, o muy pocos. o Punto otante: El punto otante es otra manera de representar nmeros reales. u Bsicamente como se logra es dividiendo a la palabra en dos partes, una a para la mantisa y la otra para el exponente, utilizando lo que se conoce como notacin cient o ca. Veamos los siguientes ejemplos: 1,32456 106 1,32456 106 1,32456 103

1324560 ,00000132456  1324,56

Como podemos ver del ejemplo anterior, nos ponemos de acuerdo en cuntos a d gitos van a estar a la izquierda del punto decimal, y todos los nmeros u reales los representamos con ese nmero de enteros. A continuacin, damos u o una potencia de 10 por la que hay que multiplicar el nmero, para obtener u el nmero que deseamos. u Una abreviatura de esta notacin ser escribiendo los dos nmeros anterioo a u res de la siguiente forma: 1,32456E6 1324560 1,32456E 6 ,00000132456 1,32456E3  1324,56

24

Introduccin o

Esta representacin es much o simo ms verstil que la de punto jo, y de a a hecho es la que se usa generalmente. Al momento de representar los nmeros, u se hace en binario. Si damos mucho espacio para los exponentes tenemos la posibilidad de representar nmeros muy grandes o muy pequeos (con el u n exponente negativo). Si en cambio le damos mucho espacio a la mantisa, vamos a tener nmeros con mucha precisin (muchas cifras signicativas). u o Es conveniente encontrar un balance entre la magnitud y la precisin. Por o ejemplo, la IEEE tiene sus estndares. a Las operaciones con este tipo de nmeros son un poco ms complejas que con u a punto jo. Por ejemplo, si deseamos sumar dos nmeros, tenemos primero u que llevarlos a que tengan el mismo exponente, y una vez hecho esto se puede llevar a cabo la suma. En cambio, multiplicar dos nmeros es sumamente u fcil. Por qu? a e Al igual que en los nmeros enteros, adems de lo que nos proporcione el u a hardware de la computadora como tamao de palabra, por software se pueden n usar tantas palabras como uno quiera para representar a un entero o a un real. Cada lenguaje de programacin proporciona un conjunto de enteros y reales de o diversos tamaos. n La inmensa mayor de las computadoras utilizan complemento a 2 y mantisa a y exponente para representar nmeros. u

Limitaciones en la representacin interna o


Vimos ya que en la computadora no podemos representar a todos y cualquier entero: tenemos un nmero nito de enteros distintos que podemos representar, u no importa que tan grande sea la palabra de una computadora dada, ya que tenemos un nmero nito de combinaciones de 0s y 1s en cualquier tamao dado u n de palabra. Algo similar ocurre con los nmeros reales. No slo no tenemos la posibilidad u o de representar a un nmero innito de nmeros reales, sino que adems tampoco u u a tenemos la densidad de los nmeros reales. Como estamos usando binario para u representar a nmeros que nosotros manejamos y pensamos como nmeros en u u base 10, habr nmeros que no tengan un representacin exacta al convertirlos a u o a base 2 (por supuesto que estamos hablando de nmeros fraccionarios). Adiciou nalmente, al agregarle 1 a una mantisa, no obtenemos el siguiente nmero real, u ya que estamos sumando, posiblemente, en la parte fraccionaria. Por ello, no es posible tener una representacin para todos y cada uno de los nmeros reales en o u

1.5 Ejecucin de programas o

25

un intervalo dado: nuevamente, ste es un nmero innito y lo que tenemos es un e u nmero nito de combinaciones. u

1.5 Ejecucin de programas o


Mencionamos arriba que lo unico que puede ejecutar una computadora de hardware es un programa escrito en su lenguaje de mquina. Por lo que para que a nuestros programas escritos en Java puedan ser ejecutados, debern estar escritos a en lenguaje de mquina. a Recuerdan lo que hac amos para obtener un programa escrito en lenguaje de mquina a partir de uno escrito en ensamblador? Se lo dbamos como datos a un a a programa que traduc de enunciados en ensamblador a enunciados en lenguaje a de mquina. Algo similar hacemos cuando estamos trabajando en un lenguaje de a alto nivel. En general, tenemos dos maneras de conseguir ejecutar un programa escrito en un lenguaje de alto nivel. La primera de ellas es mediante un intrprete, y la e segunda mediante un compilador. Veamos qu queremos decir con cada uno de e estos trminos. e

Denicin 1.4 Un intrprete es un programa que una vez cargado en la memoria de una o e
computadora, y al ejecutarse, procede como sigue: Toma un enunciado del programa en lenguaje de alto nivel, llamado el cdigo o fuente. Traduce ese enunciado y lo ejecuta. Repite estas dos acciones hasta que alguna instruccin le indique que pare, o o bien tenga un error fatal en la ejecucin. o

Denicin 1.5 Un compilador es un programa, una vez que reside en memoria y al o


ejecutarse, toma un programa fuente y lo traduce completo a un programa en otro lenguaje de programacin, que generalmente es lenguaje de mquina, equivalente. o a Mientras que un intrprete va traduciendo y ejecutando, el compilador no e se encarga de ejecutar, sino simplemente de producir un programa equivalente, susceptible de ser cargado a la memoria de la mquina y ejecutado. a

26

Introduccin o

A los intrpretes se les conoce tambin como mquinas virtuales, porque una e e a vez que estn cargados en una mquina, se comportan como si fueran otra compua a tadora, aquella cuyo lenguaje de mquina es el que se est traduciendo y ejecua a tando.

1.5.1.

Filosof de programacin as o
Dependiendo del tipo de problema que queramos resolver numrico, admie nistrativo, de propsito general, inteligencia articial, lgico tenemos distintos o o lenguajes de programacin que permiten representar de mejor manera los formao tos de los datos y los recursos que requerimos para resolver el problema. As , para procesos numricos se requiere de bibliotecas muy extensas con funciones e matemticas, un manejo sencillo de matrices y, en general, de espacios de varias a dimensiones, etc. El lenguaje que fue diseado para este tipo de problemas fue n FORTRAN, y recientemente se usa C. Si lo que queremos es resolver problemas administrativos tenemos a COBOL, que se rehusa a morir, o Visual Basic que provee una fabricacin rpida de interfaces con el usuario2 . Como representantes o a de lenguajes de propsito general tenemos Pascal, C, Algol. Para problemas que o involucran cambios de estado en los datos, o situaciones que suceden no forzosamente una despus de la otra se cuenta con lenguajes orientados a objetos como e C++, SmallTalk y Java. Para resolver problemas que involucran manejo simblico o de datos, como lo que se requiere para Inteligencia Articial, se tienen lenguajes como LISP y Scheme. Para problemas de tipo lgico se tiene ProLog. En n, casi o cualquier tipo de aplicacin que se nos ocurra, se puede disear un lenguaje de o n programacin para el cual el lenguaje que se utilice en el algoritmo sea mucho muy o cercano al lenguaje de programacin: ste es el objetivo que se persigue cuando o e se disean nuevos lenguajes de programacin. Este curso se enfocar a resolver n o a problemas que se expresan fcilmente con orientacin a objetos, y el lenguaje que a o utilizaremos es Java. Es importante darse cuenta que, nalmente, cualquier problema se puede resolver utilizando cualquier lenguaje: nalmente, todo programa tiene que traducirse a lenguaje de mquina, por lo que no importa en qu lenguaje hayamos programaa e do, terminaremos con un programa equivalente escrito en lenguaje de mquina. a El meollo del asunto es, simplemente, qu tanto trabajo nos cuesta pensar en el e problema en un lenguaje pensado para resolver otro tipo de problemas. Buscamos que el lenguaje de programacin se ajuste de manera sencilla a nuestro modo de o
Una interfaz con el usuario es aquel programa que permite una comunicacin mejor entre o el usuario y el programa en la computadora. Se usa para referirse a las intefaces grcas. a
2

1.6 Caracter sticas de Java

27

pensar respecto al problema que deseamos resolver.

1.6 Caracter sticas de Java


Java es un lenguaje orientado a objetos, cuyo principal objetivo de diseo es n que fuera porttil. Una manera de hacer programas escritos en Java es mediante a el siguiente truco: Se traduce el programa escrito en Java a un lenguaje de bajo nivel, tipo lenguaje de mquina, pero que no sea el de una mquina en espec a a co. Se construye (programa) un intrprete de este lenguaje de mquina, y e a entonces se ejecuta el programa en lenguaje de mquina en la mquina a a virtual de Java. Esto resulta relativamente sencillo. El lenguaje de mquina de Java se llama a bytecode. Es ms fcil construir una mquina virtual que entienda el bytecode que a a a construir un compilador para cada posible lenguaje de mquina. Adems, una vez a a que est denida la mquina virtual, se le pueden agregar capacidades al lenguaje, a a simplemente dando su transformacin a bytecode. o Por todo esto, para ejecutar un programa escrito en Java necesitamos: a. Tener un compilador de Java que traduzca de programas escritos en Java a bytecode (javac). b. Tener un intrprete de bytecode (o mquina virtual de Java) a la que se le da e a como datos el programa en bytecode y los datos pertinentes al programa.

El proceso del software

2.1 Qu es la programacin? e o
Como ya platicamos al hablar de lenguajes de programacin, la programao cin consiste en elaborar un algoritmo, escrito en un lenguaje susceptible de ser o ejecutado por una computadora, para resolver una clase de problemas. Podemos pensar en un algoritmo como la denicin de una funcin. Una vez o o denida sta, se procede a aplicar la funcin a distintos conjuntos de argumentos e o (datos) para obtener el resultado. El proceso que nos lleva nalmente a aplicar un programa a datos determinados conlleva varias etapas, algunas de ellas repetitivas. En el estado inicial de este proceso tendremos un enunciado del problema que deseamos resolver, junto con la forma que van a tomar los datos (cuntos datos, de qu tipo). En el estado a e nal deberemos contar con un programa correcto que se puede instalar en una computadora para ser ejecutado con cualquier conjunto de datos que cumpla las especicaciones planteadas. Por ello deberemos observar los siguientes pasos (ver gura 2.1 en la siguiente pgina para el proceso del Software.1 ) a
1 El diagrama que presentamos es lo que corresponde a un proceso de software en espiral, ya que se regresa una y otra vez a etapas anteriores, hasta que se pueda llegar al nal del diagrama.

30

El proceso del software

Figura 2.1

Proceso del software.


Especicacin o

Anlisis y Diseo a n

Implementacin o

Validacin o

Mantenimiento

Renamiento y extensin o

Especicacin del problema: Se nos presenta el enunciado del problema y deo beremos determinar de manera precisa las especicaciones: de dnde paro timos (con qu entradas contamos) y a dnde queremos llegar (cul es el e o a resultado que deseamos obtener). Como producto de esta etapa deberemos producir tres incisos: a. Enunciado preciso del problema. b. Entradas. c. Salidas.

2.1 Qu es la programacin? e o

31

Anlisis y dise o del algoritmo: Planteamos la manera en que vamos a transa n formar los datos de entrada para obtener el resultado que buscamos, y procedemos a elaborar un modelo de la solucin. Muchas veces este modelo o involucra varios pasos intermedios (estados de los datos), o ms que un rea sultado concreto, buscamos un cierto comportamiento, como en el caso de un juego o una simulacin como la de un reloj. En estos ultimos casos o deberemos pensar en procesos sucesivos que nos lleven de un estado de cosas (estado de los datos) al estado inmediato sucesor fotos instantneas a y cul o cules son las transformaciones de los datos que nos producen el a a siguiente estado. Dependiendo del ambiente (dominio) de nuestro problema, las soluciones que diseemos debern tener distintas caracter n a sticas. La primera de ellas, que comparten todos los dominios es: a) La solucin debe ser correcta, eciente y efectiva. o La unica excepcin posible a esta reglas se presenta si estamos haciendo o un programa para ayudarnos a calcular algo, o para sacarnos de un brete momentneo o coyuntural, el programa se va a utilizar pocas veces en un a lapso corto de tiempo; tal vez hasta podemos eliminar la caracter stica de que sea eciente (haga el trabajo en un tiempo razonable, utilizando una cantidad razonable de recursos como la memoria). En los primeros aos de n las computadoras, casi todos los programas eran de este tipo: la gente los hac para s mismos, o para un grupo reducido que estaba muy al tanto de a lo que estaba pasando. Hoy en d en que las computadoras estn en todo, la mayor de la gente a, a a involucrada haciendo programas los hace para otros. Adems, el tamao a n de los sistemas ha crecido tanto, que ya casi nadie es el dueo de sus n programas, sino que se trabaja en el contexto de proyectos grandes, con mucha gente involucrada. En este tipo de situaciones, que hoy en d son a ms la regla que la excepcin, se requiere adems que los programas: a o a b) Sean modulares. Se puedan trazar claramente las fronteras entre pedazos del programa (o sistema), para que la tarea se pueda repartir. c) Tengan un bajo nivel de acoplamiento. Esta propiedad se reere a que utilicen lo menos posible del mundo exterior y entreguen lo m nimo posible: que haya poco trco entre los mdulos, de tal manera que haya la a o posibilidad de reutilizarlos.

32

El proceso del software

d) Alta cohesin, que se reere al hecho de que todo lo que est relacionado o e (funciones, datos) se encuentren juntos, para que sean fciles de localizar, a entender y modicar. Implementacin o construccin del modelo: En esta etapa deberemos trao o ducir nuestro algoritmo al lenguaje de programacin que hayamos elegido. o A esta etapa se le conoce tambin como de codicacin. Esta no es una labor e o muy dif si es que tenemos un diseo que siga la losof 2 del lenguaje de cil, n a programacin. o Asegurar la documentacin del programa: Esto no es o no deber ser o a una etapa separada del proceso, ya que lo ideal es que, conforme se va progresando se vaya documentando el programa. Hoy en d existen muchos a paquetes que ayudan a llevar a cabo el diseo y que ayudan con la documenn tacin, por lo que Actualmente es imperdonable que falte documentacin en o o los programas. En el presente, en que los programas se hacen en un 90 % de los casos para otros, es muy importante que el programa lo pueda entender cualquier lector humano; esto se logra, la mayor de las veces, mediante la a documentacin. o Prueba y validacin: Debemos tener la seguridad de que nuestro programa hao ce lo que se supone debe de hacer. Para esto hay pruebas informales, que consisten en presentarle al programa distintos conjuntos de datos y vericar que el programa hace lo que tiene que hacer. Estas pruebas deben incluir conjuntos de datos errneos, para vericar que el programa sabe defendero se en situaciones anmalas. o La validacin de los programas conlleva demostraciones matemticas de que o a los enunciados cambian el estado de los datos de la manera que se busca. Mantenimiento: La actividad que mayor costo representa hoy en d es la del a mantenimiento de los sistemas. Tenemos dos tipos de mantenimiento: correctivo y extensivo. El correctivo tiene que ver con situaciones que el programa o sistema no est resolviendo de manera adecuada. El mantenimiento extena sivo tiene que ver con extender las capacidades del programa o sistema para que enfrente conjuntos nuevos de datos o entregue resultados adicionales. Sin una documentacin adecuada, estas labores no se pueden llevar a cabo. o Sin un diseo correcto, es prcticamente imposible extender un sistema. n a
Con esto nos referimos a la manera como el lenguaje de programacin interpreta el mundo: o por procedimientos, orientado a objetos, funcional, lgico. A esto le llamamos paradigmas de o programacin. o
2

2.1 Qu es la programacin? e o

33

Renamiento y Extensin: Esta etapa generalmente la llevan a cabo personas o distintas a las que disearon el sistema. Se busca que cuando se extiende n un sistema, no se tape un hoyo haciendo otro. La modularidad ayuda, pues se entiende mejor el funcionamiento del programa si se ataca por mdulos. o Pero esta propiedad debe tener, adems, la propiedad de encapsulamiento, a que consiste en que cada mdulo tiene perfectamente delimitado su campo o de accin, la comunicacin entre mdulos es mucho muy controlada y una o o o implementacin interna (modo de funcionar) que pueda ser cambiada sin o que altere su comportamiento y sin que haya que cambiar nada ms. a Estas etapas, como ya mencionamos en algunas de ellas, no se presentan forzosamente en ese orden. Ms an, muchas veces se da por terminada una de ellas, a u pero al proceder a la siguiente surgen problemas que obligan a regresar a etapas anteriores a modicar el producto o, de plano, rehacerlo. Queremos insistir en la importancia de tener un buen anlisis y un buen diseo, no importa cunto a n a tiempo nos lleve: sta es la llave para que en etapas posteriores no tengamos que e regresar a rehacer, o nos veamos en la necesidad de tirar trabajo ya hecho. Pasemos a detallar un poco ms algunas de las etapas de las que hablamos. a

Especicacin o
Una buena especicacin, sea formal o no, hace hincapi, antes que nada, en o e cul es el resultado que se desea obtener. Este resultado puede tomar muy distintas a formas. Digamos que el cmputo corresponde a un modelo (la instrumentacin o o o implementacin del modelo). o Podemos entonces hablar de los estados por los que pasa ese modelo, donde un estado corresponde a los valores posibles que toman las variables. Por ejemplo, si tenemos las variables x, y, z, un posible estado ser a:

t t t

x5

7

9 u u u

Si tenemos la especicacin de un programa (rutina) que intercambie los vao lores de dos variables x, y, podemos pensarlo as : x  K1 x  K2 y

 K2  K1

es el estado inicial (con los valores que empieza el proceso), mientras que y

corresponde al estado nal deseado. Podemos adelantar que una manera de lograr que nuestro modelo pase del estado inicial al estado nal es si a x le damos el

34

El proceso del software

valor de K2 (el valor que tiene y al empezar) y a y le damos el valor que ten x. a Podemos representar este proceso de la siguiente manera:

t t

x K1 A x ponle K2 A y ponle K1 x K2

K2 K1

u u

// estado inicial

// estado nal

y podemos garantizar que nuestras operaciones cumplen con llevar al modelo al estado nal. Sin embargo, las operaciones que estamos llevando a cabo estn cona siderando a K1 y K2 valores constantes. Un proceso ms general ser el siguiente: a a

x K1

x K2

y K2 u // estado inicial En t copia el valor de x En x copia el valor de y // En estos momentos x y y valen lo mismo! En y copia el valor de t y K1 u // estado nal

y este proceso funciona no importando qu valores dimos para x y y. e Resumiendo, un estado de un proceso, cmputo o modelo es una lista de vao riables, cada una de ellas con un cierto valor. Una especicacin de un problema es la descripcin del problema, que como o o m nimo debe tener el estado nal del cmputo. El estado inicial puede ser jado o a partir del estado nal (determinando qu se requiere para poder alcanzar ese e estado), o bien puede darse tambin como parte de la especicacin. e o

Anlisis y diseo a n
Podemos decir, sin temor a equivocarnos, que la etapa de diseo es la ms n a importante del proceso. Si sta se lleva a cabo adecuadamente, las otras etapas e se simplican notoriamente. La parte dif en la elaboracin de un programa cil o de computadora es el anlisis del problema (denir exactamente qu se desea) y a e el diseo de la solucin (plantear cmo vamos a obtener lo que deseamos). Para n o o esta actividad se requiere de creatividad, inteligencia y paciencia. La experiencia juega un papel muy importante en el anlisis y diseo. Dado que la experiencia a n se debe adquirir, es conveniente contar con una metodolog que nos permita ir a construyendo esa experiencia.

2.1 Qu es la programacin? e o

35

As como hay diversidad en los seres humanos, as hay maneras distintas de analizar y resolver un problema. En esa bsqueda por automatizar o matematiu zar el proceso de razonamiento, se buscan mtodos o metodolog que nos lleven e as desde la especicacin de un problema hasta su mantenimiento, de la mejor mao nera posible. El principio fundamental que se sigue para analizar y disear una n solucin es el de divide y vencers, reconociendo que si un problema resulta deo a masiado complejo para que lo ataquemos, debemos partirlo en varios problemas de menor magnitud. Podemos reconocer dos vertientes importantes en cuanto a las maneras de dividir: a) Programacin o anlisis estructurado, que impulsa la descomposicin del proo a o blema en trminos de acciones, convergiendo todas ellas en un conjunto de e datos, presentes todo el tiempo. La divisin se hace en trminos del proceso, o e reconociendo distintas etapas en el mismo. Dado que el nfasis es en el proceso, e cada mdulo del sistema corresponde a un paso o etapa del proceso. Cuando o usamos este enfoque perdemos la posibilidad de encapsular, pues los datos se encuentran disponibles para todo mundo, y cada quien pasa y les mete mano. Adems, hay problemas que son dif a ciles de analizar en trminos de etapas. e Por ejemplo, los juegos de computadora estilo aventura, las simulaciones de sistemas biolgicos, el sistema de control de un dispositivo qu o mico, un sistema operativo. Sin embargo, esta metodolog es muy adecuada para disear, como a n lo acabamos de mencionar, un cierto proceso que se lleva a cabo de manera secuencial. b) Anlisis y diseo orientado a objetos. El anlisis orientado a objetos pretende a n a otro enfoque, partiendo al problema de acuerdo a los objetos presentes. Determina cules son las responsabilidades de cada objeto y qu le toca hacer a a e cada quin. e

Implementacin del modelo o


Para la programacin, como ya mencionamos, utilizaremos Java y aprovechao remos la herramienta JavaDoc para que la documentacin se vaya haciendo, como o es deseable, durante la codicacin. o

Mantenimiento
Porque se trata de un curso, no nos veremos expuestos a darle mantenimiento a nuestros programas. En las sesiones de laboratorio, sin embargo, tendrn a que trabajar con programas ya hechos y extenderlos, lo que tiene que ver con el mantenimiento.

36

El proceso del software

2.1.1.

Conceptos en la orientacin a objetos o


Al hacer el anlisis de nuestro problema, como ya mencionamos, trataremos a de dividir a la solucin en tantas partes como sea posible, de tal manera que cada o parte sea fcil de entender y disear. La manera de dividir al problema ser en a n a trminos de actores y sus respectivas responsabilidades (o facultades): qu puede e e y debe hacer cada actor para contribuir a la solucin. Cada uno de estos actores o corresponde a un objeto. Agrupamos y abstraemos a los objetos presentes en clases. Cada clase cumple con: Tiene ciertas caracter sticas. Funciona de determinada manera. Quedan agrupados en una misma clase aquellos objetos que presentan las mismas caracter sticas y funcionan de la misma manera. En el diseo orientado a objetos, entonces, lo primero que tenemos que hacer n es clasicar nuestro problema: encontrar las distintas clases involucradas en el mismo. Las clases nos proporcionan un patrn de comportamiento: nos dicen qu y o e cmo se vale hacer con los datos de la clase. Es como el guin de una obra de teatro, o o ya que la obra no se nos presenta hasta en tanto no haya actores. Los actores son los objetos, que son ejemplares o instancias de las clases (representantes de las clases). Al analizar un problema deberemos tratar de identicar a los objetos involucrados. Una vez que tengamos una lista de los objetos (agrupando datos que tienen propsitos similares, por ejemplo), deberemos abstraer, encontrando caraco ter sticas comunes, para denir las clases que requerimos. Distinguimos a un objeto de otro de la misma clase por su nombre identidad o identicador. Nos interesa de un objeto dado:
I. II.

Su estado: cul es el valor de sus atributos. a Su conducta: Qu cosas sabe hacer. e Cmo va a reaccionar cuando se le hagan solicitudes. o

Lo correspondiente a i. est determinado por cada objeto, ya que cada objeto a es capaz de almacenar su propia informacin. Lo correspondiente a ii. est dado o a por la denicin de la clase, que nos da un patrn de conducta. o o

2.1 Qu es la programacin? e o

37

Tratando de aclarar un poco, pensemos en lo que se conoce como sistema cliente/servidor. Cliente es aqul que pide, compra, solicita algo: un servicio, un e valor, un trabajo. Servidor es aqul que provee lo que se le est pidiendo. Esta e a relacin de cliente/servidor, sin embargo, no es esttica. El servidor puede tomar o a el papel de cliente y viceversa. Lo que le interesa al cliente es que el servidor le proporcione aquello que el cliente est pidiendo. No le importa cmo se las arregla el servidor para hacerlo. a o Si el cliente le pide al servidor algo que el servidor no sabe hacer, que no reconoce, simplemente lo ignora, o le contesta que eso no lo sabe hacer. El anlisis orientado a objetos pretende reconocer a los posibles clientes y servia dores del modelo, y las responsabilidades de cada quien. Divide la responsabilidad global del proceso entre distintos objetos. Un concepto muy importante en la orientacin a objetos es el encapsulamiento o de la informacin. Esto quiere decir que cada objeto es dueo de sus datos y sus o n funciones, y puede o no permitir que objetos de otras clases ajenas vean o utilicen sus recursos. Un objeto entonces tiene la propiedad de que encapsula tanto a los procesos (funciones) como a los datos. Esto es, conoce cierta informacin y sabe cmo llevar o o a cabo determinadas operaciones. La ventaja del encapsulamiento es que en el momento de disear nos va a permitir trazar una l n nea alrededor de operaciones y datos relacionados y tratarlos como una cpsula, sin preocuparnos en ese momento a de cmo funciona, sino unicamente de qu es capaz de hacer. o e En el caso de los objetos, la cpsula alrededor del mismo oculta al exterior a la manera en que el objeto trabaja, el cmo. Cada objeto tiene una interfase o pblica y una representacin privada. Esto nos permite poder hacer abstracciones u o ms fcil y modelos ms sencillos, pues lo que tomamos en cuenta del objeto es a a a exclusivamente su interfase; posponemos la preocupacin por su representacin o o privada. Pblicamente un objeto anuncia sus habilidades: puedo hacer estas cosas, u puedo decir estas cosas; pero no dice cmo es que las puede hacer o cmo es que o o sabe las cosas. Los objetos actan como un buen jefe cuando le solicitan a otro u objeto que haga algo: simplemente le hacen la solicitud y lo dejan en paz para que haga lo que tiene que hacer; no se queda ah mirando sobre su hombro mientras lo hace. El encapsulamiento y el ocultamiento de la informacin colaboran para aislar o a una parte del sistema de otras, permitiendo de esta manera la modicacin y o extensin del mismo sin el riesgo de introducir efectos colaterales no deseados. o Para determinar cules son los objetos presentes en un sistema, se procede de la a siguiente manera:

38

El proceso del software

1. Se determina que funcionalidades e informacin estn relacionadas y deben o a permanecer juntas, y se encapsulan en un objeto. 2. Entonces se decide qu funcionalidades e informacin se le van a solicitar a e o este objeto (qu servicios va a prestar). Estos se mantienen pblicos, miene u tras que el resto se esconde en el interior del objeto. Esto se logra mediante reglas de acceso, que pueden ser de alguno de los tipos que siguen: P blico: Se permite el acceso a objetos de cualquier otra clase. u Privado: Slo se permite el acceso a objetos de la misma clase. o En algunos lenguajes de programacin se permiten otros tipos de acceso. Por o ejemplo, en Java tambin tenemos los siguientes, pero que no pretendemos dejar e claros por el momento: Paquete: Se permite el acceso a objetos que estn agrupados en el mismo paquete a (generalmente un sistema). Protegido: Se permite el acceso a objetos de clases que hereden de esta clase. Veamos la terminolog involucrada en el diseo orientado a objetos: a n

Mensajes (messages)
Un objeto le pide un servicio a otro mandndole un mensaje. A esta accin le a o llamamos el env de un mensaje y es la unica manera en que un objeto se puede o comunicar con otro. Un mensaje consiste del nombre de una operacin y los argumentos que la o operacin requiere. La solicitud no especica cmo debe ser satisfecha. o o

Comportamiento o conducta (behaviour )


El conjunto de mensajes a los que un objeto puede responder es a lo que se conoce como la conducta o el comportamiento del objeto. Al nombre de la operacin en el mensaje le llamamos el nombre del mensaje. o

2.1 Qu es la programacin? e o

39

Mtodos (methods) e
Cuando un objeto recibe un mensaje, ejecuta la operacin que se le solicita, o mediante la ejecucin de un mtodo. Un mtodo es un algoritmo paso a paso o e e que se ejecuta como respuesta a un mensaje cuyo nombre es el mismo que el del mtodo. Para que un mtodo pueda ser invocado desde un objeto de otra clase, e e debe ser pblico. En el caso de algunos lenguajes de programacin, a los mtodos u o e se les llama funciones miembro (member functions), porque son miembros de la clase u objeto.

Clases (classes)
Si dos objetos presentan el mismo comportamiento, decimos que pertenecen a la misma clase. Una clase es una especicacin genrica para un nmero arbitrario o e u de objetos similares. Las clases permiten construir una taxonom de los objetos a en un nivel abstracto, conceptual. Nos permiten describir a un grupo de objetos. Por ejemplo, cuando describimos a los seres humanos damos las caracter sticas que les son comunes. Cada ser humano es un objeto que pertenece a esa clase. Hay que insistir en que las clases corresponden unicamente a descripciones de objetos, no tienen existencia en s mismas.

Ejemplares (instances)
Las instancias corresponden, de alguna manera, a los ejemplares que podemos exhibir de una clase dada. Por ejemplo, describimos a los seres humanos y decimos que Fulanito es un ejemplar de la clase de seres humanos: existe, tiene vida propia, le podemos pedir a Fulanito que haga cosas. Fulanito es un objeto de la clase de seres humanos. Fulanito es el nombre (identicador, identidad) del ejemplar u objeto. Su comportamiento es el descrito por los mtodos de su clase. e Est en un cierto estado, donde el estado es el conjunto de datos (caracter a sticas) junto con valores particulares. Un mismo objeto puede pasar por distintos estados. Por ejemplo, podemos denir que las caracter sticas de un ser humano incluyen: estado de conciencia, estado de nimo, posicin. Fulanito puede estar dormido o despierto, contento o a o triste, parado o sentado, correspondiendo esto a cada una de las caracter sticas (o

40

El proceso del software

variables). Sin embargo, hay variables entre las que corresponden a un objeto, que si bien cambian de un objeto a otro (de una instancia a otra), una vez denidas en el objeto particular ya no cambian, son invariantes. Por ejemplo, el color de los ojos, el sexo, la forma de la nariz.

Figura 2.2

Arbol de herencia en clases.


Elementos geomtricos e

punto

l nea

Volumen

recta

curva Supercie

cubo

cilindro

a esfera pirmide

cuadrado

rectngulo a

c rculo

elipse

Herencia (inheritance)

Es frecuente encontrar una familia de objetos (o clases) que tienen un ncleo u en comn, pero que dieren cada uno de ellos por algn atributo, una o ms funu u a ciones. Cuando esto sucede, deberemos reconocer al ncleo original, identicando u a una clase abstracta a partir de la cual cada uno de las clases de la familia son una extensin. A esta caracter o stica es a lo que se le conoce como herencia: las clases en la familia de la que hablamos, heredan de la clase abstracta o clase base, remedando un rbol como el que se muestra en la gura 2.2. En esta gura las a clases abstractas o base se presentan encuadradas con l neas intermitentes, mientras que las subclases se presentan encuadradas con l nea continua. A este tipo de esquema le llamamos de jerarqua de clases.

2.2 Dise o orientado a objetos n

41

Polimorsmo (polymorphism)
Dado que tenemos la posibilidad de agrupar a las clases por familias, queremos la posibilidad de que, dependiendo de cul de los herederos se trate, una a funcin determinada se lleve a cabo de manera personal a la clase. Por ejemplo, o si tuviramos una familia, la funcin de arreglarse se deber llevar a cabo de e o a distinta manera para la abuela que para la nieta. Pero la funcin se llama igual: o arreglarse. De la misma manera, en orientacin a objetos, conforme denimos heo rencia podemos modicar el comportamiento de un cierto mtodo, para que tome e en consideracin los atributos adicionales de la clase que hereda. A esto, el que o el mismo nombre de mtodo pueda tener un signicado distinto dependiendo de e la clase a la que pertenece el objeto particular que lo invoca, es a lo que se llama polimorsmo tomar varias formas. En resumen, presentados ante un problema, estos son los pasos que deberemos realizar: 1. Escribir de manera clara los requisitos y las especicaciones del problema. 2. Identicar las distintas clases que colaboran en la solucin del problema y la o relacin entre ellas; asignar a cada clase las responsabilidades correspondieno tes en cuanto a informacin y proceso (atributos y mtodos respectivameno e te); identicar las interacciones necesarias entre los objetos de las distintas clases (diseo). n 3. Codicar el diseo en un lenguaje de programacin, en este caso Java. n o 4. Compilar y depurar el programa. 5. Probarlo con distintos juegos de datos. De lo que hemos visto, la parte ms importante del proceso va a ser el anlisis a a y el diseo, as que vamos a hablar de l con ms detalle. n e a

2.2 Diseo orientado a objetos n


El diseo orientado a objetos es el proceso mediante el cual se transforman las n especicaciones (o requerimientos) de un sistema en una especicacin detallada o

42

El proceso del software

de objetos. Esta ultima especicacin debe incluir una descripcin completa de o o los papeles y responsabilidades de cada objeto y la manera en que los objetos se comunican entre s . Al principio, el proceso de diseo orientado a objetos es exploratorio. El din seador busca clases, agrupando de distintas maneras para encontrar la manera n ms natural y razonable de abstraer (modelar) el sistema. Inicialmente consiste a de los siguientes pasos: 1. Determina (encuentra) las clases en tu sistema. 2. Determina qu operaciones son responsabilidad de cada clase, y que conoe cimientos la clase debe mantener o tener presentes para poder cumplir con sus responsabilidades. 3. Determina las formas en las que los objetos colaboran con otros objetos para delegar sus responsabilidades. Estos pasos producen: una lista de clases dentro de tu aplicacin, o una descripcin del conocimiento y operaciones que son responsabilidad de o cada clase, y una descripcin de la colaboracin entre clases. o o Una vez que se tiene este esquema, conviene tratar de establecer una jerarqu a entre las clases que denimos. Esta jerarqu establece las relaciones de herencia a entre las clases. Dependiendo de la complejidad del diseo, podemos tener anidan dos varios niveles de encapsulamiento. Si nos encontramos con varias clases a las que conceptualizamos muy relacionadas, podemos hablar entonces de subsistemas. Un subsistema, desde el exterior, es visto de la misma manera que una clase. Desde adentro, es un programa en miniatura, que presenta su propia clasicacin o y estructura. Las clases proporcionan mecanismos para estructurar la aplicacin o de tal manera que sea reutilizable. Si bien suena sencillo eso de determina las clases en tu aplicacin, en la vida o real ste es un proceso no tan directo como pudiera parecer. Veamos con un poco e de ms detalle estos subprocesos: a 1. Determina las clases en tu sistema (encuentra los objetos). Para poder determinar cules son los objetos en tu sistema, debes tener a muy claro cul es el objetivo del mismo. Qu debe lograr el sistema? Cul a e a

2.2 Dise o orientado a objetos n

43

es la conducta que est claramente fuera del sistema? La primera pregunta a se responde dando una especicacin completa del problema. En un prino cipio daremos descripciones narrativas del problema y nuestro primer paso deber consistir en dividir el problema en subproblemas, identicando las a clases. En esta descripcin narrativa, existe una relacin entre los sustantivos o o o nombres y los objetos (o clases). Una primera aproximacin puede ser, eno tonces, hacer una lista de todos los sustantivos que aparezcan en la especicacin narrativa. Una vez que se tiene esta lista, debemos intentar reconocer o similitudes, herencia, interrelaciones. Debemos clasicar a nuestros objetos para determinar cules son las clases que vamos a necesitar. a Las probabilidades de xito en el diseo del sistema son directamente proe n porcionales a la exactitud y precisin con que hagamos esta parte del diseo. o n Si este primer paso no est bien dado, el modelo que obtengamos a partir de a l no va a ser util y vamos a tener que regresar posteriormente a parcharlo. e 2. Determina sus responsabilidades (mtodos y estructuras de datos). e Determinar las responsabilidades de un objeto involucra dos preguntas: Qu es lo que el objeto tiene que saber de tal manera que pueda e realizar las tareas que tiene encomendadas? Cules son los pasos, en la direccin del objetivo nal del sistema, en a o los que participa el objeto? Respondemos a esta pregunta en trminos de responsabilidades. Posponemos e lo ms posible la denicin de cmo cumple un objeto con sus responsabilia o o dades. En esta etapa del diseo nos interesa qu acciones se tienen que llevar n e a cabo y quin es el responsable de hacerlo. e De la misma manera que existe una cierta relacin entre los sustantivos de o la especicacin y las clases, podemos asociar los verbos de la especicacin o o a los mtodos o responsabilidades. Si hacemos una lista de las responsabilie dades y las asociamos a los objetos, tenemos ya un buen punto de partida para nuestro modelo. Un objeto tiene cinco tipos de funciones: funciones constructoras, que son las encargadas de la creacin de los o objetos, as como de su inicializacin (establecer el estado inicial); o funciones de implementacin, que son aquellas que representan a los o servicios que puede dar un objeto de esa clase;

44

El proceso del software

funciones de acceso que proporcionan informacin respecto al estado o del objeto; funciones auxiliares que requiere el objeto para poder dar sus servicios, pero que no interactan con otros objetos o clases; y u funciones de actualizacin y manipuladoras, que modican el estado o del objeto. 3. Determina su colaboracin (mensajes). En esta subdivisin nos interesa reso o ponder las siguientes preguntas respecto a cada una de las clases denidas: Con qu otros objetos tiene que colaborar para poder cumplir con sus e responsabilidades (a quin le puede delegar)? e Qu objetos en el sistema poseen informacin que necesita o sabe como e o llevar a cabo alguna operacin que requiere? o En qu consiste exactamente la colaboracin entre objetos? e o Es importante tener varios objetos que colaboran entre s De otra manera, . el programa (o sistema) va a consistir de un objeto enorme que hace todo. En este paso, aunque no lo hemos mencionado, tenemos que involucrarnos ya con el cmo cumple cada objeto con su responsabilidad, aunque no todav a o a mucho nivel de detalle. Un aspecto muy importante es el determinar dnde o es que se inician las acciones. En el esquema de cliente/servidor del que hemos estado hablando, en este punto se toman las decisiones de si el objeto subcontrata parte de su proceso, si es subcontratado por otro objeto, etc. Es importante recalcar que mientras en la vida real algunos de los objetos tienen habilidad para iniciar por s mismos su trabajo, en el mundo de la programacin orientada a objetos esto no es as se requiere de un agente o : que inicie la accin, que ponga en movimiento al sistema. o Es muy importante en esta etapa describir con mucha precisin cules son o a las entradas (input) y salidas (output) de cada objeto y la manera que cada objeto va a tener de reaccionar frente a una solicitud. En teor un objeto a, siempre da una respuesta cuando se le solicita un servicio. Esta respuesta puede ser No s hacerlo, no lo reconozco. e Un valor o dato que posee. La realizacin de un proceso o

2.3 Dise o estructurado n

45

La manera como estas respuestas se maniestan van a cambiar de sistema a sistema.

4. Determina la accesibilidad de las funciones y datos. Una vez que se tiene clasicado al sistema, es importante perseguir el principio de ocultamiento de la informacin. Esto consiste en decidir, para cada clase, cules de sus o a funciones y sus datos van a estar disponibles, pblicos, y cules van a estar u a ocultos dentro de los objetos de la clase. Es claro que los mtodos o funciones e forman parte de la seccin pblica, pues van a ser solicitados por otros o u objetos. Tambin es claro que los datos deben permanecer ocultos, pues e queremos que el objeto mismo sea el unico que puede manipular su propio estado. No queremos que otra clase u objeto tenga acceso a los valores del objeto. Sin embargo, a veces requerimos de funciones que slo el objeto necesita o o usa. En estos casos, estas funciones las vamos a colocar tambin en el espacio e privado de la clase, buscando que el acoplamiento entre clases sea m nimo: si nadie requiere de ese servicio, ms que el mismo objeto, para qu ponerlo a e disponible?

Si bien tratamos de dar una metodolog para el diseo orientado a objetos, es a n imposible hacerlo de manera completa en unas cuantas pginas (hay cursos que a se dedican unicamente a este tema). Lo que se menciona arriba son unicamente indicaciones generales de cmo abordar el problema, aprovechando la intuicin o o natural que todos poseemos. Conforme avancemos en el material, iremos extendiendo tambin la metodolog e a.

2.2.1.

Tarjetas de responsabilidades
Como resultado de este anlisis elaboraremos, para cada clase denida, lo a que se conoce como una tarjeta de responsabilidades. Esta tarjeta registrar los a atributos, responsabilidades y colaboracin que lleva a cabo esa clase, y una breve o descripcin del objetivo de cada atributo y de cada mtodo. o e

46

El proceso del software

2.3 Diseo estructurado n


Como ya mencionamos antes, para disear cada uno de los mtodos o funciones n e propias de un sistema debemos recurrir a otro tipo de anlisis que el orientado a a objetos. Esto se debe fundamentalmente a que dentro de un mtodo debemos e llevar a cabo un algoritmo que nos lleve desde un estado inicial a otro nal, pero donde no existe colaboracin o responsabilidades, sino simplemente una serie de o tareas a ejecutar en un cierto orden. Tenemos cuatro maneras de organizar a los enunciados o l neas de un algoritmo: Secuencial, donde la ejecucin prosigue, en orden, con cada l o nea, una despus e de la otra y siguiendo la organizacin f o sica. Por ejemplo: 1: pone 1 en x 2: suma 2 a x 3: copia x a y se ejecutar exactamente en el orden en que estn listadas. a a Iteracin, que marca a un cierto conjunto de enunciados secuenciales e indica la o manera en que se van a repetir. Por ejemplo: 1: pone 1 en x 2: Repite 10 veces desde el enunciado 3 hasta el 5: 3: suma 2 a x 4: copia x a y 5: Escribe el valor de x En este caso, podemos decir que el estado inicial de las variables al llegar a la iteracin es con x valiendo 1 y con y con un valor indeterminado. Cul o a es el estado nal, al salir de la iteracin? o La manera como indicamos el grupo de enunciados a repetirse es dando una sangr mayor a este grupo; siguiendo esta convencin, el enunciado de la a o l nea 2 podr simplemente ser a 2: Repite 10 veces: y el solo hecho de que los enunciados 3, 4 y 5 aparecen con mayor sangr a da la pauta para que sos sean los enunciados a repetirse. e Ejecucin condicional, que se reere a elegir una de dos o ms opciones de o a grupos de enunciados. Por ejemplo:

2.3 Dise o estructurado n


1: 2: 3: 4: 5: 6: 7: 8:

47 poner un valor arbitrario a y, entre 0 y 9999 Si x es mayor que 1: Divide a y entre x y coloca el resultado en z Multiplica a z por 1 3 escribe el valor de z Si x no es mayor que 1: multiplica a y por 1 6 escribe el valor de y

En este caso planteamos dos opciones, una para cuando el estado inicial antes de entrar a la ejecucin condicional sea con x teniendo un valor mayor o que 1, y la otra para cuando x tenga un valor menor que 1 (que pudiera ser 0). Recursividad, que es cuando un enunciado est escrito en trminos de s mismo, a e como es el caso de la denicin del factorial de un nmero: o u
6 9n 8 9 7

n! 

pn 1q!

para n 1 para n  1

Toda solucin algor o tmica que demos, sobre todo si seguimos un diseo esn tructurado, deber estar dado en trminos de estas estructuras de control. El a e problema central en diseo consiste en decidir cules de estas estructuras utilin a zar, cmo agrupar enunciados y como organizar, en general los enunciados del o programa. Una parte importante de todo tipo de diseo es la notacin que se utiliza n o para expresar los resultados o modelos. Al describir las estructuras de control utilizamos lo que se conoce como pseudocdigo, pues escribimos en un lenguaje o parecido al espaol las acciones a realizar. Esta notacin, si bien es clara, resulta n o fcil una vez que tenemos denidas ya nuestras estructuras de control, pero no a nos ayuda realmente a disear. Para disear utilizaremos lo que se conoce como n n la metodolog de Warnier-Orr, cuya caracter a stica principal es que es un diseo n controlado por los datos, i.e. que las estructuras de control estn dadas por las a estructuras que guardan los datos. Adems, el diseo parte siempre desde el estado a n nal del problema (qu es lo que queremos obtener) y va deniendo pequeos pasos e n que van transformando a los datos hacia el estado inicial del problema (que es lo que sabemos antes de empezar a ejecutar el proceso). Empecemos por ver la notacin que utiliza el mtodo de Warnier-Orr, y dado o e que es un mtodo dirigido por los datos, veamos la notacin para representar e o

48

El proceso del software

grupos de datos, que al igual que los enunciados, tienen las mismas 4 formas de organizarse: secuencial, iterativa, condicional o recursiva . Por supuesto que tambin debemos denotar la jerarqu de la informacin, donde este concepto se e a o reere a la relacin que guarda la informacin entre s Representa a los datos o o . con una notacin muy parecida a la de teor de conjuntos, utilizando llaves para o a denotar a los conjuntos de datos (o enunciados). Sin embargo, cada conjunto puede ser renado con una ampliacin de su descripcin, que se encuentra siempre o o a la derecha de la llave. Otro aspecto muy importante es que en el caso de los conjuntos de Warnier-Orr el orden es muy importante. La manera en que el mtodo de Warnier-Orr representa estos conceptos se explica a continuacin: e o

Jerarqu a
Abre llaves. El nombre de la llave es el objeto de mayor jerarqu e identica a al subconjunto de datos que se encuentran a la derecha de la llave. Decimos entonces que lo que se encuentra a la derecha de la llave rena (explica con mayor detalle) al nombre de la llave. Veamos la gura 2.3. Jerarqu en la gura gura 2.3, nombre agrupa a descr1 , descr2 ,. . . ,descrn ; a: decimos entonces que las descripciones son un renamiento de nombre, y que nombre es de mayor jerarqu que las descripciones. a

Figura 2.3

Uso de llaves para denotar composicin. o


6 9descr1 9 9 9 9 9 9 9 9 9descr 9 2 8

nombre

9 9 9. . . 9 9 9 9 9 9 9 9 7descr

Secuencia: el orden (la secuencia) se denota verticalmente: descr1 se ejecuta antes que descr2 , y as sucesivamente, en el orden vertical en el que aparecen. Iteracin: la repeticin se representa con un parntesis abajo del nombre o o e donde se indican las reglas de repeticin: o

2.3 Dise o estructurado n

49

Figura 2.4

Iteracin en diagramas de Warnier-Orr. o


nombre descr2 pMientras te diganq 9. . . 9 9 7 descrn En el la gura 2.4, la condicin de repeticin es mientras te digan. Eso o o querr decir que se ejecutan en orden, descr1 hasta descrn . Al terminar, se a checa si todav me dicen. Si es as se regresa a ejecutar completo desde a , descr1 hasta descrn , y as sucesivamente hasta que ya no me digan, en cuyo caso sigo adelante. Condicional: Por ultimo, para denotar seleccin se utiliza el s o mbolo del o exclusivo , que aparece entre una pareja de opciones ver gura 2.5.
6 9descr1 9 9 8

Figura 2.5

Seleccin en diagramas de Warnier-Orr. o


5 6 9 9digito 1P . . . 9 9 9 ... 9 9 9 9 9 9 9 5 9 9 9 9 9digito P 2P . . . 9 8 ... nombre 9 9 9 9 9. . . 9 9 9 9 9 9 9 5 9 9 9 9 9digito P 9P . . . 9 7

 

...

Veamos cmo quedar representados los pequeos procesos que dimos arriba o an n en trminos de la notacin de Warnier-Orr en las guras 2.6 y 2.7, donde el s e o mbolo signica obtn el valor de lo que est a la derecha y coloca ese valor en la e a variable que est a la izquierda. a

50

El proceso del software

Figura 2.6

Diagramas de Warnier-Orr para secuencia.


6 9x 8

secuencial

1 xx2 9 7 yx

Figura 2.7

Diagramas de Warnier-Orr para iteracin. o

x2 Repite iteracin o yx 9 p10q 9 7 7 escribe el valor de x


Por ultimo, el bloque de pseudocdigo que tenemos para la ejecucin condi o o cional podr quedar como se ve en la gura 2.8. a

6 9 8

6 9x 8

Figura 2.8

Diagrama de Warnier-Orr para seleccin. o


6 9y 9 9 9 9 9 9 9 9x 9 9 9 9 8

randomp10000q 6 9z y {x 8 1 9z z{3
7 5

seleccin o

escribe el valor de z

9 9 9 9 9 9 9 9 9 9 9 9x 9 7

y y {6 escribe el valor de y

Por supuesto que el mtodo de Warnier-Orr nos proporciona herramientas para e llegar a estos esquemas, que se basan, como dijimos antes, en dejar que los datos nos indiquen las estructuras a travs de la jerarqu que guardan entre ellos. e a Adems, utilizando el principio de divide y vencers, decimos que cualquier a a problema puede ser dividido en al menos tres partes: prepararse, ejecutarlo y

2.3 Dise o estructurado n

51

completar o cerrar. Veamos en un ejemplo, el de obtener los factores primos de un entero n, cules son los pasos a seguir. Por lo que acabamos de mencionar, todo a diagrama de Warnier-Orr empieza con el formato de la gura 2.9.

Figura 2.9

Estado inicial de todo diagrama de Warnier-Orr.


6 9 9 9.P rincipio 9 9 9 9 9 9 9 9 8 9 9 9 9 9 9 9.F inal 9 7 5 6 9. . . 8 9 7 ... 5

Inicializar Obtener datos

N ombre del P roceso P roblema 9 9 9

Entregar resultados Atar cabos sueltos

Siguiendo el principio de ir de lo general a lo particular indica que intentamos ver el problema desde un punto de vista general, particularizando unicamente cuando ya no es posible avanzar sin hacerlo. En este momento, en el problema que estamos atacando, debemos empezar ya a renar el proceso. Por ultimo, cuando decimos que vamos desde los resultados hacia los datos, decimos que el problema lo vamos resolviendo preocupndonos por cul es el resultado que debemos producir a a y cul es el paso inmediato anterior para que lo podamos producir. a

Figura 2.10

Diagrama inicial para encontrar factores primos.


6 9.Principio 9 9 9 9 9 9 9 9 9 9 Verica si k es factor 9 9 9 9 9 primo de n 8 3 6 9. . . 9 9 9 9 9 8

Obtener n

F actores primos

9 9 9 9 9 9 9 9 9 9 9 9 9.Final 9 9 9 7

pk  2 . . . cnq

9 9 9 9 9 9 7 ... 6 9 Da la lista de 8 9 7

...

factores primos de n

52

El proceso del software

Para el caso que nos ocupa, determinar los factores primos de un entero, el diagrama con el que empezamos lo podemos ver en la gura 2.10 en la pgina a anterior. En el primer momento, todo lo que sabemos es qu es lo que tenemos de e datos (n) y qu esperamos de resultado (una lista con todos los valores de k para e los cuales k es primo y k divide a n). Para el principio y nal de nuestros procesos podemos observar que lo ultimo que vamos a hacer es proporcionar o escribir la lista de factores primos del nmero n. Esto corresponde al nal de nuestro proceso. u Sabemos, porque algo de matemticas manejamos, que deberemos vericar todos a c los enteros k entre 2 y n, y que durante este proceso es cuando se debe construir la lista de primos divisores de n. Tambin sabemos, porque corresponde a e nuestros datos, que al principio de nuestro proceso deberemos obtener n. Con esta informacin, podemos producir el diagrama inicial que, como ya mencionamos, se o encuentra en la gura 2.10 en la pgina anterior. a

Figura 2.11

Diagrama de Warnier-Orr para procesar cada k.

6 3 9.P rincipio 9 9 9 6 5 9 9 9 9 esP rimo 9 9 9 9.P rincipio 9 9 9 9 9 9 true 9 9 9 9 6 9 9 9 9 9 9 9.P rincipio 9 9 9 9 V erif ica si i divide a k 8 9 9 9 9 9 9 ... 9 9 9 9 9 i 2, . . . , k 9 9 7 9 9 9 .F inal 9 9 9 5 9 8 9 9 9k es factor de n Agrega k a 9 9 9esP rimo true 9 Verica si k es factor 9 9 la lista 8 9 9 9 9 primo de n 9 9 9 9 9 9 9 9 k 2, . . . , n 9 9 9 9 9 9 3 9 9 9 9 9 9esP rimo true 9 9 9 9 9 9 3 9 9 9 9 9 7.F inal 9 9 9 9 9 9 9 9 9 9 9 9 9 3 9 9 9k es factor de n 9 9 9 3 9 9 7.F inal

p 

 

p 

cq

2.3 Dise o estructurado n

53

Progresamos de atrs hacia adelante. Para poder escribir la lista de factores a primos, debemos construirla. Esto lo hacemos en el bloque inmediato anterior al del nal, que es el que procesa a cada k y decide, primero, si k es un divisor de n, y despus, si lo es, si k es primo o no. Si k es primo, se le agrega a la lista. Esta e etapa la podemos observar en el diagrama de la gura 2.11 en la pgina opuesta, a que muestra unicamente lo relativo al bloque que maneja a cada k. Nos falta desarrollar el bloque que corresponde a determinar si k es primo. Esto se hace de manera sencilla, dividiendo a k entre cada i. Si para alguna de estas is, el residuo es 0, sabemos ya que k no es primo y no vale la pena seguir vericando. Si, en cambio, para ninguna de las i con las que se prueba, la i divide a k, entonces sabemos que k es primo. El diagrama correspondiente a este bloque lo mostramos en la gura 2.12.

Figura 2.12

Diagrama para determinar si k es primo.


6 3 9.P rincipio 9 9 9 6 3 9 9 9 9.P rincipio 9 9 9 9 9 9 9 9 9 8 9 f also 9 9k es factor de n esP rimo 9 9 9 9i n 9 9 3 9 9 V erif ica si i divide a k 8 9 7.F inal 9 i 2, . . . , k 9 9 9 9 9 9 9 9 9 3 9 9 9 9k es factor de n 9 9 3 9 9 9 7.F inal

p 

El diagrama completo se puede observar en la gura 2.13 en la siguiente pgina a (para ahorrar espacio, y dado que no contribuyen, eliminamos todos los principios y nales que no hacen nada). Cuando alguno de los procesos no tiene que hacer nada, simplemente le ponemos un signo de conjunto vac (). Varios de los procesos de P rincipio y F inal o no tienen encomendada ninguna tarea, por lo que aparecen vac os. Una vez completo el diagrama de Warnier de un problema, el programa est daa do realmente por los enunciados en la jerarqu menor (stos son realmente los a e enunciados ejecutables), respetando las estructuras de control de repeticin y cono dicionales. Conforme vayamos avanzando en el curso, trataremos de adquirir expe-

54

El proceso del software

riencia en el diseo estructurado. No hay que ignorar el hecho de que la intuicin n o y la experiencia juegan un papel importante en esta etapa. Haciendo el s mil con arquitectura, una clase corresponde a los planos de una casa mientras que cada casa que se construye de acuerdo a esos planos corresponde a una instancia u objeto de esa clase. Para poder determinar cada una de estas funciones (no siempre tenemos presentes a todos los tipos) deberemos recurrir al diseo estructurado. n Hablaremos ms de este tema ms adelante en el curso El mtodo de Warniera a e Orr no proporciona mecanismos para analizar problemas de naturaleza recursiva. Este tipo de soluciones estar dado por las frmulas mismas, o la descripcin de a o o la solucin. o

Figura 2.13

Diagrama de Warnier-Orr para obtener factores primos de un entero.

3 6 9.P rincipio n el nmero a f actorizar u 9 9 3 6 9 6 9 9 9.P rincipio 9 9 esP rimo true 9 9 9 9 9 9 6 5 9 9 9 9 9 9 9 9 9 9i es factor esP rimo 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9V erif ica si 9 9 9 de k 9 i n 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 9i divide a k 9 9 9 9 9 9 9 9 9 9 9 9 9 8 9 9k es factor 9 i 2, 9 9 9 9 9 5 9 9 V erif ica si k 9 9 9 9 9 9i es factor 9 9 9 9 ..., k 9 9 de n 9 9 9 9 9 9 9 9 9 9 9 9 divide a 7 de k 9 8 8 9 9 3 9 9 OF P n 9esP rimo true 9 9 9 Agrega k a la lista 9 9 9 9 9 9 9 k 2... 9 9 9 9 9 9 9 9 9 9 9 9 9 ... n 9 3 9 9 9 9 9 9 7esP rimo true 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9k es factor 9 9 9 9 3 9 9 9 9 9 7 de n 9 9 9 3 9 9 7

f also

p c

p  cq

.F inal

escribe lista construida

Con esto queda terminado el algoritmo (y los diagramas correspondientes) para obtener factores primos de un entero. Procedemos ahora a incursionar en otros problemas, donde utilizaremos la metodolog que acabamos de describir. a

Clases y objetos

3.1 Tarjetas de responsabilidades


Para ilustrar de mejor manera el proceso de anlisis y diseo procederemos a a n trabajar con un ejemplo concreto: el diseo de un reloj analgico. Este ejemplo, n o aunque sencillo, nos va a permitir seguirlo hasta su implementacin. Los pasos o para ello son, como lo mencionamos en secciones anteriores, hacer diagramas o esquemas para cada una de las etapas de denicin, que volvemos a listar: o Accin o Encontrar las clases y los objetos Determinar las responsabilidades A partir de: Una descripcin o o especicacin del o problema La lista de objetos. La especicacin o del programa Produce: Una lista de objetos y un esquema para cada clase

1.

2.

3.

Determinar la colaboracin o entre objetos

Esquema de clases con lista de funciones miembro o mtodos, clasicados en e pblicos y privados, y con un breve desu cripcin de qu se supone que hace cada o e una La lista de Adiciona al esquema de responsabilidaresponsabilidades. des, para cada funcin o mtodo, quin o e e La lista de objetos. la puede llamar y a quin le va a respone der

56 Accin o Determinar la accesibilidad de las funciones y datos A partir de: El esquema de colaboracin y o responsabilidades

Clases y objetos

4.

Produce: El esquema revisado para que coincida con las reglas que se dieron para accesibilidad.

Pasemos a analizar nuestro problema.

Paso 1: Descripcin del problema o


Descripcin del problema: o Un reloj analgico consiste de una cartula de despliegue, con dos manecillas, o a una para horas y una para minutos. Cada manecilla tendr un valor entre 0 a y un l mite superior prejado (en el caso de los minutos es 60, mientras que para las horas es 12). El usuario del programa debe ser capaz de construir el reloj inicializando el valor de cada manecilla a las 0 horas con 0 minutos, o bien, a un valor arbitrario (que podr ser el reloj de la mquina). El usuario a a debe ser capaz tambin de incrementar el reloj incrementando la manecilla e de los minutos y algunas veces tambin la manecilla de las horas. El usuario e deber ser capaz de establecer el reloj en un cierto valor, estableciendo el a valor de cada uno de las manecillas. Finalmente, el usuario puede pedirle al reloj que muestre su valor mostrando la posicin de cada una de las o manecillas. Del prrafo anterior, podemos distinguir los siguientes objetos: a reloj manecilla de horas manecilla de minutos valor de horas valor de minutos valor del reloj l mites Podemos ver que el objeto reloj, posee dos objetos que corresponde cada uno de ellos a una manecilla. Cada manecilla posee un objeto valor y un objeto l mite. El valor concreto de cada manecilla es sucientemente simple como para que se lleve en un entero, lo mismo que el l mite; excepto que este ultimo debe ser constante porque una vez que se je ya no deber cambiar. Las horas y los minutos a

3.1 Tarjetas de responsabilidades

57

con su valor y l mite correspondientes, pertenecen a la misma clase. Podemos entonces mostrar nuestra estructura de la siguiente manera: Manecilla Datos: Funciones: valor l mite constructores: incrementa pon valor muestra horas minutos constructores incrementa pon valor muestra

Poner el l mite

Reloj Datos: Funciones: Una manecilla con l mite 12 Una manecilla con l mite 60

En la clase Manecilla tenemos dos variables, valor y l mite, que no queremos puedan ser manipuladas directamente, sin controlar que se mantenga en los l mites establecidos, por lo que van a tener acceso privado. Por ello es conveniente agregar dos responsabilidades (mtodos) a esta clase, getValor y getLimite, para que se le e pueda pedir a los objetos de la clase, en caso necesario, que diga cunto valen1 . a

Paso 2: Elaboracin de tarjetas de responsabilidades o


En este punto podemos hacer una distribucin de los objetos en clases y un o esquema de las clases determinadas que presente qu es lo que tiene que contener e y tener cada clase, anotndolo en una tarjeta, como se ve en la gura 3.1 en la a siguiente pgina. a

Usaremos la palabra get en estos casos, en lugar del trmino en espaol da o dame, ya que e n en Java existe la convencin de que los mtodos de acceso a los atributos de una clase sean de o e la forma get seguidos del identicador del atributo empezado con mayscula. Similarmente en u los mtodos de modicacin o actualizacin de los valores de un atributo la convencin es usar e o o o set seguido del nombre del atributo escrito con mayscula. u

58

Clases y objetos

Figura 3.1

Tarjetas de clasicacin y acceso. o

Clase: Reloj
P u b l i c o P r i v a d o
constructores incrementa setValor muestra

Clase: Manecilla
P u b l i c o P r i v a d o
constructores incrementa setValor muestra getValor getL mite

horas minutos

valor l mite

Si1 completamos estos esquemas con las responsabilidades de cada quien, van a quedar como se muestra en la gura 3.2 para la clase Reloj y en la gura 3.3 en la pgina opuesta para la clase Manecilla. a

Figura 3.2

Tarjeta de responsabilidades de la clase Reloj Clase: Reloj P u b l i c o P r i v a d o


constructor incrementa setValor muestra horas minutos

(responsabilidades)
Inicializa el reloj a una hora dada. Para ello, debe construir las manecillas. Incrementa el reloj en un minuto Pone un valor arbitrario en el reloj Muestra el reloj Registra el valor en horas Registra el valor en minutos

3.1 Tarjetas de responsabilidades

59

Figura 3.3

Tarjeta de responsabilidades para la clase Manecilla. Clase: Manecilla P u b l i c o P r i v a d o


constructor incrementa setValor muestra getValor getLimite valor limite

(responsabilidades)
Establece el l mite de la manecilla y da valor inicial a la manecilla Incrementa el valor Pone valor a la manecilla Muestra el valor Dice el valor que tiene Regresa el valor del l mite Tiene la informacin de la manecilla o Tiene la informacin respecto al l o mite

Paso 3: Determinar colaboracin o


El tercer paso nos dice que determinemos la colaboracin entre los objetos. En o una primera instancia podemos ver que un objeto de la clase Reloj puede pedirle a cada uno de los objetos de la clase Manecilla que haga su parte: construirse, mostrar su valor, incrementarse. Podemos anar nuestra descripcin de cada una o de las clases, describiendo de manera breve en qu consiste cada mtodo o funcin e e o propia y deniendo la colaboracin (quin inicia las acciones, o quin le solicita a o e e quin): e Clase: Manecilla Datos valor l mite Mtodos Constructor Reloj e incrementa Reloj Cliente: Descripcin: o el valor actual de la manecilla, en el rango 0..l mite 1. el valor en el que el reloj da la vuelta o se vuelve a poner en ceros Pone el valor de la manecilla en ceros y establece el l mite Suma 1 al valor y lo regresa a cero si es necesario

60 Clase: setValor muestra Reloj Datos

Clases y objetos

Cliente: Descripcin: o Reloj Pone el valor Reloj Muestra el valor que tiene la manecilla Una manecilla con l mite 12 Una manecilla con l mite 60 Manda un mensaje a ambas manecillas instalando sus l mites respectivos Incrementa la manecilla de minutos, y si es necesario la de horas Establece el tiempo en el reloj y para ello lo establece en las manecillas horas y minutos Pide a las manecillas que se acomoden

horas minutos Mtodos Constructor usuario e incrementa usuario setValor muestra usuario usuario

En forma esquemtica las tarjetas quedan como se muestran en las guras 3.4 y 3.5 a en la pgina opuesta. a

Figura 3.4

Tarjeta de colaboraciones de la clase Manecilla. Clase: Manecilla P u b l i c o P r i v a d o


constructor incrementa setValor muestra getValor getLimite valor limite

(colaboracin) o
El El El El El El Reloj Reloj Reloj Reloj Reloj Reloj a a a a a a la la la la la la manecilla manecilla manecilla manecilla manecilla manecilla

3.2 Programacin en Java o

61

Figura 3.5

Tarjeta de colaboraciones de la clase Reloj. Clase: Reloj P u b l i c o P r i v a d o


constructor incrementa setValor muestra horas minutos

(colaboracin) o
El El El El usuario usuario usuario usuario al al al al Reloj Reloj Reloj o Reloj a s mismo Reloj

Tenemos ya completo el paso de anlisis y diseo, ya que tenemos las tarjetas a n de responsabilidades completas. Pasemos ahora al siguiente paso en la elaboracin o de un programa, que consiste en la instrumentacin del diseo para ser ejecutado o n en una computadora. Si bien el diseo orientado a objetos no es un concepto reciente (aparece alren dedor de 1972), lo que si es ms reciente es la popularidad de las herramientas que a facilitan la transicin de un modelo orientado a objetos a un programa orientado o a objetos. El primer lenguaje que maneja este concepto es Simula (hermanito de Algol 60), aunque su popularidad nunca se generaliz. o Al mismo tiempo que Wirth diseaba el lenguaje Pascal (una herramienta n de programacin para el diseo estructurado), se dise Smalltalk, un lenguaje o n no orientado a objetos, de uso cada vez ms generalizado hoy en d Tambin se han a a. e hecho muchas extensiones a lenguajes estructurados para proveerlos de la capacidad de manejar objetos. Entre ellos tenemos Objective Pascal, C++, Objective C, Modula 3, Ada. Muchos de los abogados de la programacin orientada a objeo tos consideran a este tipo de extensiones como sucias, pues en muchas ocasiones mezclan conceptos, o cargan con problemas que se derivan de tratar de mantener la relacin con sus lenguajes originales. Hemos seleccionado Java como herramienta o de instrumentacin pues contamos con amplia bibliograf al respecto, aceptacin o a o generalizada fuera de los ambientes acadmicos, acceso a muy diversas versiones e de la herramienta. Estamos conscientes, sin embargo, de que Java es un lenguaje sumamente extenso, por lo que no pretendemos agotarlo en este curso.

62

Clases y objetos

3.2 Programacin en Java o


En todo lenguaje de programacin hay involucrados tres aspectos, relativos a o los enunciados escritos en ese lenguaje: Sintaxis: Se reere a la forma que tiene que tomar el enunciado. Cada lenguaje tiene sus propias reglas y corresponder a la gramtica para un lenguaje a a natural. Utilizamos para describir la sintaxis lo que se conoce como BNF extendido. Semntica: Se reere de alguna manera al signicado del enunciado. Generala mente el signicado corresponde a la manera cmo se ejecuta el enunciado, o una vez traducido a lenguaje de mquina (en el caso de Java a bytecode). a Usaremos lenguaje natural y predicados para describir este aspecto. Pragmtica: Se reere a restricciones o caracter a sticas dadas por la computadora o la implementacin del lenguaje. Por ejemplo, un entero en Java tiene un o l mite superior e inferior, que no corresponde a lo que entendemos como entero. Este l mite es impuesto por la implementacin del lenguaje o de la o computadora en la que se van a ejecutar los programas. Usaremos lenguaje natural para hablar de la pragmtica de un enunciado. a Hablemos un poco de BNF extendido, donde cada enunciado se muestra como si fuera una frmula: o

xtrmino a deniry :: xexpresin regulary e o


En esta notacin del lado izquierdo de ::= aparece lo que ser un tipo de eleo a mentos, lo que vamos a denir, como por ejemplo acceso, encerrado entre x y y para distinguir al conjunto de alguno de sus representantes. El ::= se lee se dene como; del lado derecho se encuentra una xexpresin regulary, que puede o contener a su vez conjuntos o elementos del lenguaje. Una expresin regular es una o sucesin de s o mbolos terminales y no terminales (como en cualquier gramtica), a pero donde extendemos la gramtica de la siguiente manera: usamos parntesis a e para agrupar cuando queramos que aparezca un parntesis tal cual lo marcaree mos con negritas ; el s mbolo | para denotar opciones; el s mbolo * para denotar que el grupo anterior se puede repetir cero o ms veces; y + para dea notar que el grupo anterior se puede repetir una o ms veces. A los elementos a del lenguaje (representantes de los conjuntos, s mbolos terminales) los escribimos con negritas, tal como deben aparecer en el archivo fuente. Conforme vayamos avanzando quedar ms claro el uso de BNF. a a Cuando describamos un recurso del lenguaje, sea ste un enunciado o la manera e de organizar a stos, hablaremos al menos de los dos primeros aspectos; el tercero e

3.2 Programacin en Java o

63

lo trataremos en aquellos casos en que tenga sentido. Como Java es un lenguaje orientado a objetos, la modularidad de los programas en Java se da a travs de clases. Una clase es, como ya dijimos, una plantilla para e la construccin de objetos, una lista de servicios que los objetos de la clase van a o poder realizar. Otro elemento que utiliza Java para construir sus aplicaciones es la interfaz. Una interfaz en Java describe a un grupo de servicios, en trminos de lo que los e objetos de las clases que la implementen saben hacer, esto es, lista unicamente los servicios que la clase en cuestin va a dar, utilizando qu datos de entrada y o e proporcionando qu resultados. Una interfaz corresponde a un contrato. Posteriore mente podemos construir una o ms clases capaces de cumplir con ese contrato. A a esto ultimo le llamamos implementar a la interfaz. Trataremos de trabajar siem pre a travs de interfaces, pues nos dan un nivel de abstraccin ms alto que el e o a que nos dan las clases. Decimos que declaramos una interfaz o una clase cuando escribimos la plantilla en un archivo, al que denominamos archivo fuente. Se acostumbra, aunque no es obligatorio, que se coloque una clase o interfaz por archivo2 para tener fcilmente a identicable el archivo fuente de la misma. El nombre que se d al archivo, en este e caso, debe coincidir con el nombre de la clase o interfaz. Por ejemplo, la clase Reloj deber estar en un archivo que se llame Reloj.java; de manera similar, la interfaz a ServiciosReloj deber estar en un archivo que se llame ServiciosReloj.java. a

3.2.1.

Declaraciones en Java
Lo primero que haremos en Java es, entonces, la denicin (declaracin) de o o una interfaz. La sintaxis para ello se puede ver en la gura 3.6 en la siguiente pgina. a Las palabras que aparecen en negritas tienen que aparecer tal cual. Ese es el caso de interface y el ; que aparece al nal de cada xencabezado de mtodoy. Los e que aparecen entre x y y deben ser proporcionados por el programador, siguiendo ciertas reglas para ello. En Java el punto y coma (;) se usa para terminar enunciados, como las declaraciones (los encabezados de un mtodo juegan el papel de e una declaracin). Por ejemplo, la sintaxis para el xaccesoy es como se ve en la guo ra 3.7 en la siguiente pgina, mientras que un xidenticadory es cualquier sucesin a o de letras, d gitos y carcter de subrayado, que empiece con letra o subrayado. a
La unica restriccin real para que haya ms de una clase en un archivo es en trminos de o a e identicarla, pues no habr un archivo fuente con el nombre de la clase. Pero s habr el archivo a a correspondiente al bytecode de la clase (nombre.class).
2

64

Clases y objetos

Figura 3.6

Encabezado de una interfaz.


Sintaxis: x declaracin de interfazy ::= o xaccesoy interface xidenticadory { xencabezado de mtodoy; e (xencabezado de mtodoy;)* e } Semantica: Se declara una interfaz en un archivo. El nombre del archivo debe tener como extensin .java y coincide con el nombre que se le d a la interfaz. o e Una interfaz, en general, no tiene declaraciones de atributos, sino unica mente de mtodos, de los cules unicamente se da el encabezado. Los ene a cabezados de los distintos mtodos se separan entre s por un ; (punto y e coma). El que unicamente contenga encabezados se debe a que una interfaz no dice cmo se hacen las cosas, sino unicamente cules cosas sabe hacer. o a

Figura 3.7

Sintaxis para el xaccesoy.


Sintaxis: xacceso y ::= public | private | protected | package | Semantica: El acceso a una clase determina quin la puede usar: e public La puede usar todo mundo. private No tiene sentido para una clase ya que delimita a usar la clase a la misma clase: no se conoce fuera de la clase. protected Slo la pueden ver las clases que extienden a sta. No tiene o e sentido para clases. package Slo la pueden ver clases que se encuentren declaradas en el mismo o subdirectorio (paquete). Es el valor por omisin. o Puede no haber regla de acceso, en cuyo caso el valor por omisin es package. o En el caso de las interfaces, el acceso slo puede ser de paquete o pblico, ya o u que el concepto de interfaz tiene que ver con anunciar servicios disponibles.

3.2 Programacin en Java o

65

Siguiendo la notacin de BNF extendido, el enunciado de Java para denotar a o un elemento del conjunto xidenticadory quedar como se ve en la gura 3.8. a

Figura 3.8

Reglas para la formacin de un xidenticadory. o


Sintaxis: gito xidenticador y ::= (xletra y | )(xletray | xd y | )* Semantica: Los identicadores deben ser nemnicos, esto es, que su nombre ayude a la o memoria para recordar qu es lo que representan. No pueden tener blancos e insertados. Algunas reglas no obligatorias (aunque exigidas en este curso) son: Clases: Empiezan con mayscula y consiste de una palabra descriptiva, u como Reloj, Manecilla. Mtodos: Empiezan con minsculas y se componen de un verbo da, e u calcula, mueve, copia seguido de uno o ms sustantivos. Cada uno a de los sustantivos empieza con mayscula. u Variables: Nombres sugerentes con minsculas. u Constantes: Nombres sugerentes con maysculas. u Hay que notar que en Java los identicadores pueden tener tantos carcteres a como se desee. El lenguaje, adems, distingue entre maysculas y minscua u u las no es lo mismo carta que Carta. Una interfaz puede servir de contrato para ms de una clase (que se llamen a distinto). Es la clase la que tiene que indicar si es que va a cumplir con algn u contrato, indicando que va a implementar a cierta interfaz. El acceso a los mtodos de una interfaz es siempre pblico o de paquete. Esto e u se debe a que una interfaz anuncia los servicios que da, por lo que no tendr a sentido que los anunciara sin que estuvieran disponibles. Siempre es conveniente poder escribir comentarios en los programas, para que nos recuerden en qu estbamos pensando al escribir el cdigo. Tenemos tres tipos e a o de comentarios: Empiezan con // y terminan al nal de la l nea. Todo lo que se escriba entre /* y */. Puede empezar en cualquier lado y terminar en cualquier otro. Funcionan como separador. Todo lo que se escriba entre /** y */. Estos comentarios son para JavaDoc, de tal manera que nuestros comentarios contribuyan a la documentacin del o programa.

66

Clases y objetos

Utilizaremos de manera preponderante los comentarios hechos para JavaDoc, en particular para documentar interfaces, clases y mtodos. Los comentarios deben e tener en el primer rengln unicamente /, y cada uno de los renglones subsecueno tes, menos el ultimo, debern empezar con un asterisco. En el ultimo rengln a o aparecer unicamente /. A partir del segundo rengln deber aparecer una desa o a cripcin breve del objetivo de la clase o interfaz. o En el caso de los comentarios de las clases e interfaces, tenemos entre otros un campo, @author, que nos indica quin es el autor de esa clase o interfaz. e La interfaz para nuestro reloj deber anunciar a los servicios que listamos para a el reloj en la gura 3.4 en la pgina 60 excepto por el constructor , y la interfaz a para la clase Manecilla debe listar los servicios que listamos en la gura 3.5 en la pgina 61 tambin excluyendo al constructor . Pospondremos por el momento a e la codicacin de los encabezados, hasta que veamos este tema con ms detalle. La o a codicacin del encabezado de las interfaces para Reloj y Manecilla se encuentran o en los listados 3.1 y 3.2.

Cdigo 3.1 Encabezado de la interfaz para Reloj o

(ServiciosReloj)

/ P aque te : R e l o j . D e f i n e l o s s e r v i c i o s que van a d a r l o s o b j e t o s de l a c l a s e Reloj . / public interface S e r v i c i o s R e l o j { / L i s t a de m todos a i m p l e m e n t a r / e }

Cdigo 3.2 Encabezado de la interfaz para Manecilla o

(ServiciosManecilla)

/ P aque te : R e l o j . D e f i n e l o s s e r v i c i o s que van a d a r l o s o b j e t o s de l a c l a s e Reloj . / public interface S e r v i c i o s M a n e c i l l a { / L i s t a de m todos a i m p l e m e n t a r / e }

Veamos en la gura 3.9 en la pgina opuesta la sintaxis y semntica del encaa a bezado de una clase. Esta es una descripcin parcial, ya que por el momento no o tiene sentido ver la denicin completa. o

3.2 Programacin en Java o

67

Figura 3.9

Encabezado de una clase.


Sintaxis: xdeclaracin de clasey ::= xaccesoy class xidenticadory o ( |p implements (xidenticadory)+) ( | extends xidenticadory) { xdeclaracionesy ( | xmtodo mainy) e } Semantica: Se declara una clase en un archivo. El nombre del archivo debe tener como extensin .java y, en general, coincide con el nombre que se le d a la o e clase. Una clase debe tener xdeclaracionesy y puede o no tener xmtodo e mainy. La clase puede o no adoptar una interfaz para implementar. Si lo hace, lo indica mediante la frase implements e indicando a cul o cules a a interfaces implementa. Las xdeclaracionesy corresponden a los ingredientes o variables y a los mtodos que vamos a utilizar. Una xvariabley corresponde a e una localidad (cajita, celda) de memoria donde se va a almacenar un valor. El xmtodo mainy se usa para poder invocar a la clase desde el sistema e operativo. Si la clase va a ser invocada desde otras clases, no tiene sentido que tenga este mtodo. Sin embargo, muchas veces para probar que la clase e funciona se le escribe un mtodo main. En Java todo identicador tiene que e estar declarado para poder ser usado. Cuando un archivo que contiene interfaces o clases se compila bien aparecern a en el subdirectorio correspondiente un archivo con el nombre de cada una de las clases o interfaces, pero con el sujo .class, que es la clase o interfaz pero en bytecode. Este es el archivo que va a ser interpretado por la Mquina Virtual de a Java y el que puede ser ejecutado o invocado. Vamos codicando lo que ya sabemos cmo. Tenemos dos interfaces, Servio ciosReloj y ServiciosManecilla, para los que tenemos que denir los servicios que cada una de ellas va a contratar. Regresamos a las tarjetas de responsabilidades donde los servicios corresponden a los verbos, y van a ser implementados a travs e de mtodos. Sabemos que hay cinco tipos posibles de mtodos: e e (a) Constructores. Son los que hacen que los objetos de esa clase existan. (b) De acceso. Son los que permiten conocer el estado del objeto.

68

Clases y objetos

(c) De actualizacin o modicacin. Son los que permiten modicar el estado o o del objeto. (d) De implementacin. Son los que dan los servicios que se requieren del obo jeto. (e) Auxiliares. Los que requiere el objeto para dar sus servicios de manera adecuada. Como los mtodos involucrados en la interfaz deben ser pblicos o de paquete, e u slo los de tipo b, c y d van a aparecer en la denicin de la interfaz correspondieno o te. Asimismo, tampoco se ponen en la interfaz a los mtodos constructores, pues e la interfaz no dene ni es capaz de construir objetos. Pospondremos la descripcin o de los mtodos de tipo a y e para cuando revisemos con detalle la denicin de e o clases. Lo que aparece en la interfaz es unicamente el encabezado de los mtodos que e van a ser de acceso pblico o de paquete. Los mtodos de actualizacin o de impleu e o mentacin pueden recibir como entrada datos a los que llamamos parmetros. Los o a parmetros tambin se pueden usar para manipulacin o para dejar all informaa e o cin. Un parmetro es, simplemente, una marca de lugar para que ah se coloquen o a datos que el mtodo pueda usar y que pueda reconocer usando el nombre dado e en la lista. Si regresamos al s mil de una obra de teatro, podemos pensar que los parmetros corresponden a la lista de los personajes que viene, adicionalmente, a con una descripcin de si el personaje es alto, viejo, mujer, etc. (porque el puro o nombre no me indica a qu clase de actor contratar para ese papel). El guin e o viene despus en trminos de estos personajes: Hamlet dice o hace. El guin e e o nunca dice quin va a hacer el papel de Hamlet; eso se hace cuando se monta e la obra. De manera similar con los parmetros, no es sino hasta que se invoca al a mtodo que hay que pasar valores concretos. A la lista de parmetros se les llama e a tambin parmetros formales. Cuando se invoque el mtodo debern aparecer los e a e a actores que van a actuar en lugar de cada parmetro. A estos les llamamos los a argumentos o parmetros reales. Daremos la sintaxis de los parmetros cuando a a aparezcan en alguna denicin sintctica. o a En el encabezado de un mtodo cualquiera se localiza lo que se conoce como e la rma del mtodo, que consiste de los tipos de los parmetros y el nombre del e a mtodo. Adems de la rma, en el mtodo se marca de alguna manera el tipo de e a e mtodo de que se trata. Esto lo revisaremos conforme veamos los distintos tipos e de mtodos. e Para documentar los distintos mtodos de nuestra aplicacin utilizaremos tame o bin JavaDoc, donde cada comentario empieza y termina como ya mencionamos. e En el caso de los mtodos, en el segundo rengln deber aparecer una descripcin e o a o corta del objetivo del mtodo (que puede ocupar ms de un rengln) que debe e a o

3.2 Programacin en Java o

69

terminar con un punto. Despus del punto se puede dar una explicacin ms ame o a plia. A continuacin deber aparecer la descripcin de los parmetros, cada uno o a o a en al menos un rengln precedido por @param y el nombre del parmetro, con una o a breve explicacin del papel que juega en el mtodo. Finalmente se proceder a o e a informar del valor que regresa el mtodo, precedido de @returns y que consiste de e una breve explicacin de qu es lo que calcula o modica el mtodo. o e e

Mtodos de acceso e
Los mtodos de acceso los tenemos para que nos informen del estado de un e objeto, esto es, del valor de alguno de los atributos del objeto. Por ello, la rma del mtodo debe tener informacin respecto al tipo del atributo que queremos e o observar. La sintaxis se puede ver en la gura 3.10, donde las deniciones de xtipo y, xaccesoy e xidenticadory son como se dieron antes.

Figura 3.10

Encabezado para los mtodos de acceso. e


Sintaxis: x encabezado demtodo de acceso y ::= e xaccesoy xtipoy xidenticadory ( xParmetros y) a ntica: Sema La declaracin de un mtodo de acceso consiste del tipo de valor que deseao e mos ver, ya que nos va a regresar un valor de ese tipo, seguido de la rma del mtodo, que incluye a los xParmetrosy. El identicador del mtodo es e a e arbitrario, pero se recomienda algo del estilo getAtributo, que consista de un verbo que indica lo que se va a hacer, y un sustantivo que indique el atributo que se busca o el valor que se desea calcular. Los tipos que se manejan en Java pueden ser primitivos o de clase. Un tipo primitivo es aqul cuyas variables no son objetos y son atmicos, esto es, no se e o subdividen en otros campos o atributos. En la tabla 3.3 en la siguiente pgina se a encuentra una lista, con los tipos primitivos y sus rangos. Otro tipo de dato que vamos a usar mucho, pero que corresponde a una clase y no un dato primitivo como en otros lenguajes, son las cadenas, sucesiones de carcteres. La clase se llama String. Las cadenas (String) son cualquier sucesin a o de carcteres, menos el de n de l a nea, entre comillas. Los siguientes son objetos tipo String:
"Esta es una cadena 1 2 3 " ""

70

Clases y objetos

Cuadro 3.3

Tipos primitivos y sus rangos.


Identicador boolean char byte short int long oat double Capacidad true o false 16 bits, Unicode 2.0 8 bits con signo en complemento a 2 16 bits con signo en complemento a 2 32 bits con signo en complemento a 2 64 bits con signo en complemento a 2 32 bits de acuerdo al estndar IEEE 754-1985 a 64 bits de acuerdo al estndar IEEE 754-1985 a

La primera es una cadena comn y corriente y la segunda es una cadena vaca, u que no tiene ningn carcter. u a La operacin fundamental con cadenas es la concatenacin, que se representa o o con el operador +. Podemos construir una cadena concatenando (sumando) dos o ms cadenas: a
"a"+"b"+"c" "Esta cadena es"+"muy bonita " "abc" "Esta cadena esmuy bonita "

Una de las ventajas del operador de concatenacin de cadenas es que fuerza a o enteros a convertirse en cadenas cuando aparecen en una expresin de concatenao cin de cadenas. Por ejemplo, si tenemos una variable LIM que vale 12, tenemos o lo siguiente:
"El l mite es: "+LIM+"." "El l mite es: 12."

Hablaremos ms, mucho ms, de la clase String ms adelante. a a a En la gura 3.11 damos la sintaxis y semntica para la declaracin de los a o xParmetrosy. a Cuando invocamos a un mtodo le pasamos el nmero y tipo de argumentos e u que dene su rma, en el orden denido por la rma. A esto se le conoce como paso de parmetros. En general se denen tres objetivos en el paso de parmetros: a a parmetros de entrada, parmetros de salida y parmetros de entrada y salida. A a a a estos tres tipos se asocian mecanismos de paso de parmetros, entre los que estn a a el paso por valor, donde se evala al argumento y se pasa nada ms unas copia u a de ese valor; el paso por referencia, donde se toma la direccin en memoria y eso o

3.2 Programacin en Java o

71

es lo que se pasa como argumento; la evaluacin perezosa, donde no se evala o u el argumento hasta que se vaya usar dentro de la implementacin del mtodo; o e dependiendo de qu tipo de paso de parmetros contemos, es el efecto que vamos e a a tener sobre los argumentos. En el caso de Java todos los argumentos se pasan por valor, incluyendo a las referencias a los objetos. Aclararemos ms este punto a cuando lo enfrentemos.

Figura 3.11

Especicacin de parmetros. o a
Sintaxis: xParmetrosy::= | a xparmetroy(, xparmetroy)* a a xparmetroy ::= xtipoy xidenticadory a Semantica: Los parmetros pueden estar ausentes o bien consistir de una lista de a parmetros separados entre s por coma (,). Un parmetro marca lugar a a y tipo para la informacin que se le d al mtodo. Lo que le interesa al o e e compilador es la lista de tipos (sin identicadores) para identicar a un mtodo dado, ya que se permite ms de un mtodo con el mismo nombre, e a e pero con distinta rma.

Por ejemplo, los mtodos de una Manecilla que dan los valores de los atributos e privados tienen rmas como se muestra en el listado 3.3. En general, podemos pedirle a cualquier mtodo que regrese un valor, y tendr entonces la sintaxis de e a los mtodos de acceso. Como lo que queremos del Reloj es que se muestre, no que e nos diga qu valor tiene, no tenemos ningn mtodo de acceso para esta clase. e u e

Cdigo 3.3 Mtodos de acceso para los atributos privados de Manecilla o e


interface ServiciosManecilla { ... public int getValor ( ) ; public int getLimite ( ) ; ... } // S e r v i c i o s M a n e c i l l a

(ServiciosManecilla)

72

Clases y objetos

Mtodos de implementacin e o
Estos mtodos son los que dan los servicios. Por ello, el mtodo muestra cuya e e rma aparece en el listado 3.4 es de este tipo. Es comn que este tipo de mtodos u e regresen un valor que indiquen algn resultado de lo que hicieron, o bien que u simplemente avisen si pudieron o no hacer lo que se les pidi, regresando un o valor booleano. En el caso de que sea seguro que el mtodo va a poder hacer lo e que se le pide, sin contratiempos ni cortapisas, se indica que no regresa ningn u valor, poniendo en lugar de xtipo yla palabra void. Por ejemplo, el encabezado del mtodo que muestra la Manecilla queda como se muestra en el listado 3.4. Tambin e e en el listado 3.5 mostramos el mtodo de implementacin muestra para la interfaz e o ServiciosReloj.

Cdigo 3.4 Mtodos de implementacin de la interfaz ServiciosManecilla o e o


interface ServiciosManecilla { ... public void muestra ( ) ; ... } // S e r v i c i o s M a n e c i l l a

Cdigo 3.5 Mtodos de implementacin de la interfaz ServiciosReloj o e o


interface ServiciosReloj { public void muestra ( ) ; } // R e l o j

Como pueden ver, ninguno de estos dos mtodos regresa un valor, ya que e simplemente hace lo que tiene que hacer y ya. Tampoco tienen ningn parmetro, u a ya que toda la informacin que requerir es el estado del objeto, al que tienen o a acceso por ser mtodos de la clase. e

Mtodos de manipulacin e o
Los mtodos de manipulacin son, como ya mencionamos, aquellos que came o bian el estado de un objeto. Generalmente tienen parmetros, pues requieren ina formacin de cmo modicar el estado del objeto. Los mtodos que incrementan o o e y que asignan un valor son de este tipo, aunque el mtodo que incrementa no ree quiere de parmetro ya que el valor que va a usar es el 1. Muchas veces queremos a que tambin nos proporcionen alguna informacin respecto al cambio de estado, e o

3.2 Programacin en Java o

73

como pudiera ser un valor anterior o el mismo resultado; tambin podr e amos querer saber si el cambio de estado procedi sin problemas. En estos casos el mtodo o e tendr valor de regreso, mientras que si no nos proporciona informacin ser un a o a mtodo de tipo void. Por ejemplo, el mtodo que incrementa a la Manecilla nos e e interesa saber si al incrementar lleg a su l o mite. Por ello conviene que regrese un valor de 0 si no lleg al l o mite, y de 1 si es que lleg (dio toda una vuelta). La o rma de este mtodo se muestra en los listados 3.6 y 3.7. e

Cdigo 3.6 Mtodos de manipulacin para la interfaz ServiciosManecilla o e o


interface ServiciosManecilla { / I n c r e m e n t a a l a m a n e c i l l a en una u n i d a d . @ r e t u r n 0 s i no l l e g a l l m i t e y 1 s i l l e g / o o public int incrementa ( ) ; / E s t a b l e c e un v a l o r a r b i t r a r i o en e s t a m a n e c i l l a . @param v a l E l v a l o r que s e d e s e a e s t a b l e c e r . / public void s e t V a l o r ( i n t v a l ) ; } // S e r v i c i o s M a n e c i l l a

Cdigo 3.7 Mtodos de Manipulacin para la interfaz ServiciosReloj o e o


interface ServiciosReloj { / I n c r e m e n t a e s t e r e l o j en un minuto . / public void incrementa ( ) ; / Establece e l v a l o r del h o r a r i o y e l minutero . @param h r s e l v a l o r a e s t a b l e c e r en e l h o r a r i o . @param mins e l v a l o r a e s t a b l e c e r en e l m i n u t e r o . / p u b l i c v o i d s e t V a l o r ( i n t h r s , i n t mins ) ; } // S e r v i c i o s R e l o j

Noten que el mtodo setValor de ServiciosManecilla tiene un parmetro, que es e a el nuevo valor que va a tomar la manecilla, mientras que el mtodo con el mismo e nombre de la clase ServiciosReloj tiene dos parmetros, ya que requiere los valores a para las horas y para los minutos. Procedemos a construir las clases que implementen a cada una de estas interfaces. Identicamos dos clases en nuestro sistema, Manecilla y Reloj. Como la clase Manecilla no se usar ms que dentro de Reloj, la ponemos en el mismo archivo a a que a Reloj, pero dejando a sta como la primera. El archivo se llamar Reloj.java e a

74

Clases y objetos

Cdigo 3.8 Encabezados para la implementacin de Reloj y Manecilla o o


/ C l a s e p a r a r e p r e s e n t a r a un R e l o j a n a l g i c o o E l R e l o j debe s e r c a p a z de m a n t e n e r l a hora , i n c r e m e n t a r s e minuto p o r minuto , y p o n e r s e a una c i e r t a h o r a / p u b l i c c l a s s R e l o j implements S e r v i c i o s R e l o j { / Ac va l o c o r r e s p o n d i e n t e a d e c l a r a c i o n e s de R e l o j / a public void incrementa ( ) { / Ac va l a i m p l e m e n t a c i n de i n c r e m e n t a / a o } // i n c r e m e n t a p u b l i c v o i d s e t V a l o r ( i n t h r s , i n t mins ) { / Ac va l a i m p l e m e n t a c i n . / a o } // s e t V a l o r public void muestra ( ) { / Ac va l a i m p l e m e n t a c i n . / a o } // m u e s t r a } // R e l o j // E n c a b e z a d o s p a r a l a i m p l e m e n t a c i n de Manecilla o / Una m a n e c i l l a debe s e r c a p a z de i n c r e m e n t a r s e , y p o d e r s e r v i r t a n t o p a r a h o r a s , como m i n u t o s y segundos / c l a s s M a n e c i l l a implements S e r v i c i o s M a n e c i l l a { / Ac va l o c o r r e s p o n d i e n t e a l a s d e c l a r a c i o n e s a de l a c l a s e M a n e c i l l a / public int getValor () { / Ac va l a i m p l e m e n t a c i n a o / } // g e t V a l o r public int getLimite () { / Ac va l a i m p l e m e n t a c i n a o / } // g e t L i m i t e public void muestra ( ) { / Ac va l a i m p l e m e n t a c i n . a o / } // m u e s t r a } // M a n e c i l l a

porque ste es el objetivo principal del programa. Veamos cmo queda lo que llee o vamos del programa en el listado 3.8 (omitimos los comentarios de JavaDoc para

3.2 Programacin en Java o

75

ahorrar algo de espacio). Como declaramos que nuestras clases Reloj y Manecilla implementan, respectivamente, a las interfaces ServiciosReloj y ServiciosManecilla, estas clases tendrn que proporcionar las implementaciones para los mtodos que a e listamos en las interfaces correspondientes. El esqueleto construido hasta ahora se puede ver en el listado 3.8. Como a la clase Manecilla unicamente la vamos a utilizar desde la clase Reloj no le damos un archivo fuente independiente. De las cinco variedades de mtodos que listamos, nos falta revisar a los mtodos e e constructores y a los mtodos auxiliares, que tienen sentido slo en el contexto de e o la denicin de clases. o

Mtodos auxiliares e
Estos mtodos son aquellos que auxilian a los objetos para llenar las solicitudes e que se les hacen. Pueden o no regresar un valor, y pueden o no tener parmetros: a depende de para qu se vayan a usar. Dado que el problema que estamos atacando e por el momento es relativamente simple, no se requieren mtodos auxiliares para e las clases. En cuanto a las interfaces no se presentan otra categor de mtodos, ya que las a e interfaces no describen objetos, sino unicamente los contratos a los que se obligan las clases que los implementen.

Mtodos constructores e
Una clase es un patrn (descripcin, modelo, plano) para la construccin de o o o objetos que sean ejemplares (instances) de esa clase. Por ello, las clases s tie nen constructores que determinan el estado inicial de los objetos construidos de acuerdo a esa clase. En Java los mtodos constructores tienen una sintaxis un poco distinta a la de e otros tipos de mtodos. Esta se puede ver en la gura 3.12 en la siguiente pgina. e a Un constructor es el que permite el instanciamiento (la construccin) de un o objeto de una clase dada, para que sea asociado a (referido por) una variable de ese tipo. Su objetivo principal es el de establecer el estado inicial del objeto (inicializar los atributos). Puede recibir para la construccin datos en la forma de o parmetros. a

76

Clases y objetos

Figura 3.12

Encabezado de un constructor.
Sintaxis: xconstructory ::=xaccesoy xidenticador de Clasey ( xParmetrosy ) { a ximplementaciny o } xParmetrosy ::=xparmetroy(, xparmetroy) | a a a xparmetroy ::=xtipoy xidenticadory a ntica: Sema Los constructores de una clase son mtodos que consisten en un acceso e que puede ser cualquiera de los dados anteriormente seguido del nombre de la clase y entre parntesis los xParmetrosy del mtodo. Un parmetro e a e a corresponde a un dato que el mtodo tiene que conocer (o va a modicar). e Cada parmetro deber tener especicado su tipo. Los nombres dados a caa a da parmetro pueden ser arbitrarios, aunque se recomienda, como siempre, a que sean nemnicos y no se pueden repetir. o

Cdigo 3.9 Constructores para la clase Reloj o


/ . . . / p u b l i c c l a s s R e l o j implements S e r v i c i o s R e l o j { / . . . / / Constructor . @param limH lm i t e p a r a l a s h o r a s . @param limM lm i t e p a r a l o s m i n u t o s . / R e l o j ( i n t limH , i n t limM ) { / C o n s t r u c t o r : e s t a b l e c e l o s lm i t e s / // xImplementacin y o } // Firma : R e l o j ( i n t , i n t ) / Constructor . @param limH Lm i t e p a r a l a s h o r a s . @param limM Lm i t e p a r a l o s m i n u t o s @param h r s Horas a c t u a l e s . @param mins M i n u t o s a c t u a l e s . / R e l o j ( i n t limH , i n t limM , i n t h r s , i n t mins ) { // xImplementaciny o } // Firma : R e l o j ( i n t , i n t , i n t , i n t ) } // R e l o j

3.2 Programacin en Java o

77

Podemos tener tantos constructores como queramos, siempre y cuando se distingan por sus rmas. Por ejemplo, podemos tener un constructor que no tenga parmetros, o uno que tenga otra organizacin con sus parmetros. Un segundo a o a constructor para Manecilla pudiera ser uno que establece un valor prejado para la manecilla. Algo similar podemos hacer con la clase Reloj y lo podemos ver en el listado 3.10. Los constructores siempre tienen el mismo nombre que la clase de la que son constructores. No es necesario decir qu tipo de valor regresan, porque e regresan (construyen) a un objeto de su clase.

Cdigo 3.10 Constructores para la clase Manecilla o


/ . . . / p u b l i c c l a s s M a n e c i l l a implements S e r v i c i o s M a n e c i l l a { / . . . / / C o n s t r u c t o r . @param l i m Cota s u p e r i o r p a r a e l v a l o r que puede tomar l a manecilla . / Manecilla ( int lim ) { // xImplementacin y o } // Firma : M a n e c i l l a ( i n t ) / C o n s t r u c t o r que e s t a b l e c e l a h o r a a c t u a l . @param l i m Cota s u p e r i o r p a r a e l v a l o r de l a m a n e c i l l a . / M a n e c i l l a ( i n t lim , i n t v a l )\ { / C o n s t r u c t o r : pone v a l o r m ximo y v a l o r i n i c i a l / a // xImplementacin y o } // Firma : M a n e c i l l a ( i n t , i n t ) ... } // M a n e c i l l a

Es importante notar que la rma de un mtodo consiste unicamente del nombre e del mtodo junto con los tipos de los parmetros. Por lo tanto, los dos encabezados e a que se encuentran en el listado 3.11 en la siguiente pgina tienen la misma rma, a y el compilador dar un mensaje de mtodo duplicado, aunque el nombre de los a e parmetros sea distinto. a

78

Clases y objetos

Cdigo 3.11 Mtodos con la misma rma o e


p u b l i c R e l o j ( i n t h r s , i n t mins , i n t limH , i n t limM ) // Firma : R e l o j ( i n t , i n t , i n t , i n t ) public R e l o j ( i n t lim1 , i n t lim2 , i n t val1 , i n t v a l 2 ) // f i r m a : R e l o j ( i n t , i n t , i n t , i n t )

Toda clase tiene un constructor por omisin, sin parmetros, que puede ser ino a vocado, siempre y cuando no se haya declarado ningn constructor para la clase. u Esto es, si se declar, por ejemplo, un constructor con un parmetro, el construco a tor sin parmetros ya no est accesible. Por supuesto que el programador puede a a declarar un constructor sin parmetros que sustituya al que proporciona Java por a omisin. o El estado inicial que da el constructor por omisin al objeto es, bsicamente, o a cero en los atributos numricos, falso en los atributos lgicos y referencia nula e o (null) en los atributos que son objetos.

Atributos

Antes de denir la implementacin de los mtodos trabajemos con los atribuo e tos que dimos en las tarjetas. Los sustantivos debern ser atributos (variables o a constantes) datos mientras que los verbos fueron mtodos procesos o clcue a los. Lo primero que tenemos que hacer es lugar para los objetos o datos primitivos que se encuentran en cada clase. Esto lo hacemos mediante una declaracin. En o la declaracin especicamos el nombre que le queremos dar al atributo ya sea o objeto o primitivo y el tipo que va a tener entero, tipo Manecilla, etc. Veamos la sintaxis y semntica de una declaracin de atributo (dato, campo) en la a o gura 3.13 en la pgina opuesta. a Declaremos los atributos que se presentan en el programa que estamos armando en el listado 3.12 en la pgina 80 omitimos los comentarios ya presentados para a ahorrar un poco de espacio .

3.2 Programacin en Java o

79

Figura 3.13

Declaracin de un atributo o
Sintaxis: xdeclaracin de atributoy ::=xaccesoy xmodicadory xtipo y o xidenticador y(,xidenticadory)*; xmodicador y ::=nal | static | xtipo y ::=xtipo primitivoy | xidenticador de clase y Semantica: Todo identicador que se declara, como con el nombre de las clases, se le debe dar el xaccesoy y si es constante (nal) o no. Por el momento no hablaremos de static. Tambin se debe decir su tipo, que es de alguno de los e tipos primitivos que tiene Java, o bien, de alguna clase a la que se tenga acceso; lo ultimo que se da es el identicador. Se puede asociar una lista de identicadores separados entre s por una coma, con una combinacin de o acceso, modicador y tipo, y todas las variables de la lista tendrn las misa mas caracter sticas. Al declararse un atributo, el sistema de la mquina le a asigna una localidad, esto es, un espacio en memoria donde guardar valores del tipo especicado. La cantidad de espacio depende del tipo. A los atributos que se reeren a una clase se les reserva espacio para una referencia, que es la posicin en el heap donde quedar el objeto que se asocie a esa o a 3 variable . Las declaraciones de las l neas 5:, 7:, 13: y 15: son declaraciones de atributos del tipo que precede al identicador. En la l nea 5: se estn declarando dos atributos a de tipo Manecilla y acceso privado, mientras que en la l nea 13: se est declarando a un atributo de tipo entero y acceso privado. En la l nea 15: aparece el modicador nal, que indica que a este atributo, una vez asignado un valor por primera vez, este valor ya no podr ser modicado. Siguiendo las reglas de etiqueta de Java, a el identicador tiene unicamente maysculas. En el caso de los atributos de tipo u Manecilla, debemos tener claro que nada ms estamos declarando un atributo, no a el objeto. Esto quiere decir que cuando se construya el objeto de tipo Manecilla, la variable horas se referir a este objeto, esto es, contendr una referencia a un a a objeto de tipo Manecilla. Como los objetos pueden tener muy distintos tamaos n ser dif acomodarlos en el espacio de ejecucin del programa, por lo que se a cil o construyen siempre en un espacio de memoria destinado a objetos, que se llama heap 4 , y la variable asociada a ese objeto nos dir la direccin del mismo en el heap. a o
El valor de una referencia es una direccin del heap. En esa direccin se encuentra el objeto o o construido.
4

80

Clases y objetos

Cdigo 3.12 Declaracin de atributos de las clases o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: / . . . / p u b l i c c l a s s R e l o j implements S e r v i c i o s R e l o j { / R e c u e r d a l a h o r a d e l r e l o j . / private M a n e c i l l a horas , / R e c u e r d a l o s m i n u t o s d e l r e l o j . / minutos ; } // R e l o j / . . . / p u b l i c c l a s s M a n e c i l l a implements S e r v i c i o s M a n e c i l l a { / R e c u e r d a e l v a l o r de l a m a n e c i l l a . / private int valor ; / Da una c o t a s u p e r i o r p a r a e l v a l o r de l a m a n e c i l l a . / p r i v a t e f i n a l i n t LIM ; // Se e s t a b l e c e a l c o n s t r u i r l a } // M a n e c i l l a

El mtodo main e
El mtodo main corresponde a la colaboracin que queremos se d entre clases. e o e En l se dene la lgica de ejecucin. No toda clase tiene un mtodo main, ya que e o o e no toda clase va a denir una ejecucin. A veces pudiera ser nada ms un recurso o a (como es el caso de la clase Manecilla). El sistema operativo (la mquina virtual a de Java) reconoce al mtodo main y si se invoca a una clase procede a ejecutar e ese mtodo. El encabezado para este mtodo se encuentra en el listado 3.13. e e

Cdigo 3.13 Encabezado para el mtodo main o e


p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { // ximplementacin y o } // main

Se ve bastante complicado, aunque no lo es tanto. El signicado de void y public ya lo sabemos. Lleva el modicador static porque slo debe haber un mtodo de o e stos para la clase, esto es, para todos los objetos de la clase. Finalmente, el e parmetro que tiene es un arreglo (denotado por [ ]) de cadenas (String) que son a las cadenas que aparecen en la l nea de comandos cuando se invoca desde el sistema operativo (un arreglo es simplemente una sucesin de datos). La implementacin o o de este mtodo es como la de cualquier otro. Cuando veamos implementacin en e o general daremos las restricciones que presenta.

3.2 Programacin en Java o

81

3.2.2.

Alcance de los identicadores


Para estos momentos ya tenemos bastantes nombres en el programa; algunos se repiten, como el nombre de la clase en el nombre de los constructores o los identicadores de los parmetros en mtodos distintos. Es importante saber, cada a e nombre, qu alcance tiene, esto es, desde donde puede el programa referirse a l. e e

Figura 3.14

Acceso a atributos o mtodos de objetos e


Sintaxis: xReferencia a atributo o mtodoy::= e (xreferencia de objeto o clasey.) (xid de atributoy | xinvocacin a mtodoy) o e Semantica: El operador . es el de selector, y asocia de izquierda a derecha. Lo usamos para identicar, el identicador que se encuentra a su derecha, de qu objeto e forma parte. Tambin podemos usarlo para identicar a alguna clase que e pertenezca a un paquete. En el caso de un identicador de mtodo, ste e e deber presentarse con los argumentos correspondientes entre parntesis. la a e xreferencia de objetoy puede aparecer en una variable o como resultado de una funcin que regrese como valor una referencia, que se encuentre en el o alcance de este enunciado. Podemos pensar en el . como un operador del tipo referencia. La pista ms importante para esto son las parejas de llaves que abren y cierran. a Para las que corresponden a la clase, todos los nombres que se encuentran en las declaraciones dentro de la clase son accesibles desde cualquier mtodo de la misma e clase. Adicionalmente, los nombres que tengan acceso pblico o de paquete son u accesibles tambin desde fuera de la clase. e Sin embargo, hemos dicho que una clase es nada ms una plantilla para consa truir objetos, y que cada objeto que se construya va a ser construido de acuerdo a esa plantilla. Esto quiere decir que, por ejemplo en el caso de la clase Manecilla, cada objeto que se construya va a tener su atributo valor y su atributo LIM. Si ste es el caso, cmo hacemos desde fuera de la clase para saber de cul objeto e o a estamos hablando? Muy fcil: anteponiendo el nombre del objeto al del atributo, a separados por un punto. Veamos la forma precisa en la gura 3.14. Si tenemos en la clase Reloj dos objetos que se llaman horas y minutos, podremos

82

Clases y objetos

acceder a sus mtodos pblicos, como por ejemplo incrementa como se muestra en e u el listado 3.14.

Cdigo 3.14 Acceso a atributos de los objetos o


horas . incrementa () minutos . incrementa ()

Es claro que para que se puedan invocar estos mtodos desde la clase Reloj e deben tener acceso pblico o de paquete. Tambin los objetos horas y minutos u e tienen que ser conocidos dentro de la clase Reloj. Sin embargo, cuando estamos escribiendo la implementacin de algn mtodo, o u e al referirnos, por ejemplo, al atributo valor no podemos saber de cul objeto, a porque el mtodo va a poder ser invocado desde cualquier objeto de esa clase. e Pero estamos asumiendo que se invoca, forzosamente, con algn objeto. Entonces, u para aclarar que es el atributo valor del objeto con el que se est invocando, a identicamos a este objeto con this. Cuando no aparece un identicador de objeto para calicar a un atributo, dentro de los mtodos de la clase se asume entonces e al objeto this. En el cdigo que sigue las dos columnas son equivalentes para o referirnos a un atributo dentro de un mtodo de la clase. e

this.incrementa() this.valor this.horas.LIM

incrementa() valor horas.LIM

En cuanto a los parmetros de un mtodo, stos existen sola y exclusivamente a e e dentro de la implementacin del mtodo, entre las llaves. Son lo que se conoce o e como nombres o variables locales, locales al mtodo. Si en la clase existe algn e u atributo cuyo nombre sea el mismo que el del parmetro, el nombre del parmetro a a bloquea al nombre del atributo, y para referirse al atributo dentro del mtodo se e tendr que usar al selector this vase el listado 3.15 en la pgina opuesta. a e a En este listado el atributo horas de la clase Reloj se ve bloqueado por el parmetro horas, por lo que para poder ver al atributo hay que rescatar que se a trata del atributo del objeto con el que se est invocando al mtodo. e e

3.2 Programacin en Java o

83

Cdigo 3.15 Bloqueo de nombres de atributos o


p u b l i c c l a s s R e l o j implements S e r v i c i o s R e l o j { M a n e c i l l a horas , minutos ; public void s e t V a l o r ( i n t horas , i n t minutos ) this . horas . setValor ( horas ) ; t h i s . minutos . s e t V a l o r ( minutos ) ; } // R e l o j . s e t V a l o r ... } // c l a s s R e l o j

3.2.3.

Implementacin de mtodos en Java o e


La implementacin de cada uno de los mtodos nos va a decir el cmo y o e o con quin va a cubrir el objeto ese servicio. e

Figura 3.15

Sintaxis para la implementacin de un mtodo o e


Sintaxis: ximplementaciny o xLista de enunciadosy xenunciadoy xenunciado simpley ::=xLista de enunciadosy ::=xenunciado y xLista de enunciadosy | ::=xenunciado simple y; | xenunciado compuesto y ::=xdeclaracin localy | xinvocacin de mtodo y o o e | xenunciado de asignaciny o | return | return xexpresiny o

Semantica: La implementacin de un mtodo, no importa de cual categor sea, cono e a siste de una lista de enunciados entre llaves. Si queremos que el mtodo e no haga nada, entonces no ponemos ningn enunciado entre las llaves. Los u enunciados pueden ser simples o compuestos5 . Un enunciado simple puede ser una invocacin a un mtodo, puede ser una declaracin de variables o o e o puede ser una asignacin de valor a una variable. Noten que todos los enuno ciados simples terminan con punto y coma ; sin importar el contexto en el que aparecen. Es importante mencionar que aquellas variables declaradas en la implementacin de un mtodo, as como los parmetros formales, van o e a a ser accesibles (reconocidas) unicamente dentro de la implementacin del o mtodo en cuestin, a diferencia de las variables de la clase, atributos, que e o van a ser accesibles (reconocidos) en las implementaciones de cualquiera de los mtodos de la clase. e

84

Clases y objetos

En todo lo que llevamos hasta ahora simplemente hemos descrito los ingredientes de las clases y no hemos todav manejado nada de cmo hacen los mtodos a o e lo que tienen que hacer. En general un mtodo va a consistir de su encabezado e y una lista de enunciados entre llaves, como se puede ver en la gura 3.15 en la pgina anterior. a

Las declaraciones
Cuando estamos en la implementacin de un mtodo es posible que el mtodo o e e requiera de objetos o datos primitivos auxiliares dentro del mtodo. Estas variae bles auxiliares se tienen que declarar para poder ser usadas. El alcance de estas variables es unicamente entre las llaves que corresponden al mtodo. Ninguna va e riable se puede llamar igual que alguno de los parmetros del mtodo, ya que si a e as fuera, como los parmetros son locales se estar repitiendo un identicador en a a el mismo alcance. La sintaxis para una declaracin se puede ver en la gura 3.16. o

Figura 3.16

Declaracin de variables locales o


Sintaxis: xdeclaracin de variable localy ::= xtipoy xLista de identicadoresy; o Semantica: La declaracin de variables locales es muy similar a la de parmetros foro a males, excepto que en este caso s podemos declarar el tipo de varios iden ticadores en un solo enunciado. La xLista de identicadoresy es, como su nombre lo indica, una sucesin de identicadores separados entre s por una o coma (,). Hay que notar que localmente unicamente se pueden declarar variables, ya sea de tipo primitivo o referencia, y son conocidas, al igual que los parmetros, a unicamente dentro del mtodo en el que se estn declarando. No se puede declarar e a una variable que repita algn identicador usado para los parmetros, ya que los u a parmetros tambin se comportan, dentro del mtodo, como variables locales. Al a e e terminar la ejecucin del mtodo, estas variables desaparecen. o e El unico mtodo que requiere de variables auxiliares es el que muestra el reloj, e ya que queremos construir unas cadenas de carcteres para que den el mensaje a de qu hora es. Tambin requerimos de algn objeto que haga de dispositivo de e e u salida, para poder mostrar ah la hora del reloj; en otras palabras, necesitamos poder hacer entrada y salida.
No entraremos por ahora a lo que es un enunciado compuesto, ya que todav no los vamos a a usar.
5

3.2 Programacin en Java o

85

La entrada y salida de Java es un poco complicada para ser manejada por principiantes. Por ello, proporcionamos, al menos en una primera parte del curso, una clase Consola que va a permitir hacer entrada y salida de distintos objetos y datos primitivos de Java consultar la documentacin proporcionada por el o ayudante. Por el momento, todo lo que tenemos que hacer es declarar un objeto tipo Consola para que podamos ah mostrar el estado del reloj. Podemos ver estas declaraciones en el listado 3.16.

Cdigo 3.16 Declaraciones locales en el mtodo muestra de Reloj o e


1: p u b l i c c l a s s R e l o j implements S e r v i c i o s R e l o j { 2: ... 3: / 4: M ue s t ra e l e s t a d o de e s t e r e l o j . 5: / 6: public void muestra ( ) { 7: S t r i n g mensaje1 , mensaje2 , m e n s a j e 3 ; 8: Consola consola ; 9: ... 100: } // m u e s t r a 101: ... 102: } // c l a s s R e l o j

Tenemos un pequeo problema con la clase Consola y es la manera en que n nuestro programa la va a identicar. La va a encontrar sin problemas si se encuentra en el mismo subdirectorio que nuestras clases, o bien si se encuentra en alguno de los paquetes de Java. Pero no sucede ni una cosa ni la otra. Por lo tanto, tendremos que avisarle a Java que vamos a usar esta clase y el paquete en donde se encuentra. Esto se hace mediante la directiva import, seguida de toda la ruta que nos puede llevar a esta clase. Por ejemplo, en mi mquina, mis clases estn a a en un subdirectorio progs y la clase Consola con todo lo que necesita se encuentra en un subdirectorio icc1/interfaz, por lo que el enunciado import que va al inicio del archivo donde est la clase que lo usa ser cambiando las diagonales por a a, puntos selectores, import icc1.interfaz.Consola; que es la ubicacin relativa de la clase Consola. o Otro detalle con la consola es que, en realidad, no queremos una consola cada vez que le pidamos al reloj que se muestre, sino que vamos a querer que se muestre siempre en la misma consola. Por lo tanto, la declaracin no debe ser local al o mtodo muestra, sino que tiene que ser para toda la clase. De esa manera se e inicializa cuando se construye y desaparece cuando la clase ya no est presente. a

86

Clases y objetos

Tenemos una tercera opcin para la consola, y es declararla en la clase que o usa el Reloj, ya que es el usuario el que debe proporcionar la consola donde se vea el Reloj. Si ste es el caso, entonces la declaracin estar en la clase UsoReloj e o a y tendr que pasar como parmetro al mtodo muestra. Podemos ver la versin a a e o nal del encabezado y las declaraciones de este mtodo en el listado 3.17. e

Cdigo 3.17 Versin nal del encabezado y declaraciones de muestra() o o


/ M ue s t ra e l e s t a d o de e s t e r e l o j . @param c o n s o l a La c o n s o l a en que m u e s t r a e s t e r e l o j . / public void muestra ( Consola c o n s o l a ) { S t r i n g mensaje1 , mensaje2 , m e n s a j e 3 ; / I m p l e m e n t a c i n / o } // m u e s t r a

Vamos a utilizar, por el momento, unicamente dos mtodos de la clase Consola, e uno de los constructores y el que escribe en la Consola. Sus rmas se encuentran a continuacin: o
Consola () imprimeln ( String ) // Abre una c o n s o l a de 400 p o r 600 p i x e l e s // E s c r i b e l a c a d e n a en l a c o n s o l a y da un // s a l t o de l n e a .

Dado lo anterior, agregamos a la clase UsoReloj la declaracin de una Consola o para poder mostrar ah nuestro Reloj. Con lo que llevamos hecho no tenemos todav una Consola, ya que no hemos construido un ejemplar de la misma. a

El enunciado return
Cuando un mtodo est marcado para regresar un valor, en cuyo caso el tipo e a del mtodo es distinto de void, el mtodo debe tener entre sus enunciados a return e e xexpresiny. En el punto donde este enunciado aparezca, el mtodo suspende su o e funcionamiento y regresa el valor de la xexpresiny al punto donde apareci su o o invocacin. Cuando un mtodo tiene tipo void, vamos a utilizar el enunciado reo e turn para salir del mtodo justo en el punto donde aparezca este enunciado. Por e ejemplo, los mtodos de acceso lo unico que hacen es regresar el valor del atributo, e

3.2 Programacin en Java o

87

por lo que quedan como se muestra en el listado 3.18.

Cdigo 3.18 Implementacin de los mtodos de acceso de la clase Manecilla o o e


class Manecilla implements S e r v i c i o s M a n e c i l l a ... public int getValor () { return v a l o r ; } // g e t V a l o r public int getLimite () { r e t u r n LIM ; } // g e t L i m i t e ... } // M a n e c i l l a {

El enunciado de asignacin o
Figura 3.17 El enunciado de asignacin o
Sintaxis: xenunciado de asignaciny::=| xvariabley = xexpresiny o o xexpresiny ::= xvariabley| xconstantey o | new xconstructory | ( xexpresiny ) o | xoperador unarioy xexpresiny o | xexpresiny xoperador binario y xexpresiny o o | xmtodo que regresa valor y e | xenunciado de asignaciny o Semantica: Podemos hablar de que el xenunciado de asignaciny consiste de dos partes, o lo que se encuentra a la izquierda de la asignacin (=) y lo que se encuentra o a la derecha. A la izquierda tiene que haber una variable, pues es donde vamos a guardar, copiar, colocar un valor. Este valor puede ser, como en el caso del operador new, una referencia a un objeto en el heap, o un valor. El; valor puede ser de alguno de los tipos primitivos o de alguna de las clases accesibles. La expresin de la derecha se evala (se ejecuta) y el o u valor que se obtiene se coloca en la variable de la izquierda. Si la expresin o no es del mismo tipo que la variable, se presenta un error de sintaxis. Toda expresin tiene que regresar un valor. o

88

Clases y objetos

Tal vez el xenunciado simpley ms importante es el xenunciado de asignaciny, a o ya que va a ser el que me va a permitir asignarle un estado inicial a un objeto y la posibilidad de cambiarlo. Tambin es el que me permite construir objetos y e asociar una variable a cada objeto que construyo. Es conveniente recordar que las clases son unicamente plantillas para la construccin de objetos. Para que, en o efecto, se realice algo se requiere construir objetos y asociarlos a variables para que podamos pedirles que hagan algo. El xenunciado de asignaciny se muestra o en la gura 3.17 en la pgina anterior. a La sintaxis de la expresin para la construccin de objetos se encuentra en la o o gura 3.18.

Figura 3.18

Construccin de objetos o
Sintaxis: xconstruccin de objetoy ::=new xinvocacin mtodo constructory o o e Semantica: Para construir un objeto se utiliza el operador new y se escribe a continuacin de l (dejando al menos un espacio) el nombre de alguno de los o e constructores que hayamos declarado para la clase, junto con sus argumentos. El objeto queda construido en el heap y tiene todos los elementos que vienen descritos en la clase. La invocacin de un mtodo constructor o, para el caso de cualquier mtodo o e e del objeto mismo, se puede ver en la gura 3.19.

Figura 3.19

Invocacin de mtodo o e
Sintaxis: xinvocacin de mtodoy ::=xnombre del mtodoy(xArgumentosy ) o e e xArgumentosy ::=xargumentoy (,xargumentoy)* | xargumentoy ::=expresin o Semantica: Los xArgumentosy tienen que coincidir en nmero, tipo y orden con los u xParmetrosy que aparecen en la declaracin del mtodo. La sintaxis india o e ca que si la declaracin no tiene parmetros, la invocacin no debe tener o a o argumentos. Si el mtodo regresa algn valor, entonces la invocacin podr aparecer e u o a en una expresin. Si su tipo es void tendr que aparecer como enunciado o a simple.

3.2 Programacin en Java o

89

El operador new nos regresa una direccin en el heap donde qued construido o o el objeto (donde se encuentran las variables del objeto). Tengo que guardar esa referencia en alguna variable del tipo del objeto para que lo pueda usar. Si nos lanzamos a programar los constructores de la clase Reloj, lo hacemos instanciando a las manecillas correspondientes. La implementacin de estos constructores se o pueden ver en el listado 3.19.

Cdigo 3.19 Constructores de la clase Reloj o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: / I n i c i a en c e r o s / R e l o j ( i n t limH , i n t limM ) { h o r a s = new M a n e c i l l a ( limH ) ; / Usamos e l p r i m e r p a r m e t r o como argumento / a m i n u t o s = new M a n e c i l l a ( limM ) ; / Usamos e l s e g u n d o p a r m e t r o como argumento / a } // R e l o j ( i n t , i n t ) / I n i c i a en h o r a p r e e s t a b l e c i d a / R e l o j ( i n t limH , i n t limM , i n t h r s , i n t mins ) { h o r a s = new M a n e c i l l a ( limH , h r s ) ; m i n u t o s = new M a n e c i l l a ( limM , mins ) ; } // R e l o j ( i n t , i n t , i n t , i n t )

Para la clase Manecilla, que est compuesta unicamente de valores primitivos, a stos no se tienen que instanciar, por lo que la asignacin basta. La implementacin e o o se encuentra en el listado 3.20.

Cdigo 3.20 Constructores de la clase Manecillas o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: / I n i c i a en c e r o s Manecilla ( int lim ) { LIM = l i m ; } // M a n e c i l l a ( i n t ) \ /

/ I n i c i a en v a l o r p r e e s t a b l e c i d o M a n e c i l l a ( i n t lim , i n t v a l ) { LIM = l i m ; valor = val ; } // M a n e c i l l a ( i n t , i n t )

Podemos seguir con la implementacin del resto de los mtodos de la clase o e Manecilla, que son los ms sencillos. El nombre de los mtodos indica qu es lo a e e que se tiene que hacer, por lo que obviaremos los comentarios, excepto cuando

90

Clases y objetos

valga la pena aclarar algo. Para la implementacin de estos mtodos utilizareo e mos ampliamente expresiones aritmticas, para poder colocarlas del lado derecho e de una asignacin. Por ello, conviene primero revisar cmo son las expresiones o o aritmticas en Java. e

3.3 Expresiones en Java


Una expresin en Java es cualquier enunciado que nos regresa un valor. Por o ejemplo, new Manecilla(limH) es una expresin, puesto que nos regresa un objeo to de la clase Reloj. Podemos clasicar a las expresiones de acuerdo al tipo del valor que regresen. Si regresan un valor numrico entonces tenemos una expree sin aritmtica; si regresan falso o verdadero tenemos una expresin booleana; si o e o regresan una cadena de carcteres tenemos una expresin tipo String. Tambin a o e podemos hacer que las expresiones se evalen a un objeto de determinada clase. u Cuando escribimos una expresin aritmtica tenemos, en general, dos dimeno e siones en las cuales movernos: una vertical y otra horizontal. Por ejemplo, en la frmula que da la solucin de la ecuacin de segundo grado o o o x1

 b

c2
b 2a

4ac

estamos utilizando tres niveles verticales para indicar quin es el dividendo y e quin el divisor. Tambin, para indicar potencia simplemente elevamos un poco el e e nmero 2, e indicamos que la ra se reere a b2 4ac extendiendo la casita a u z que cubra a la expresin. o Cuando escribimos una expresin para un programa de computadora no cono tamos con estos niveles, sino que tenemos que linealizar la expresin: hacer que o todo se encuentre en la misma l nea vertical, pero manteniendo la asociatividad y la precedencia. La asociatividad nos habla de a quin afecta un operador dae do, mientras que la precedencia se reere al orden en que se tienen que evaluar las subexpresiones. Cada operador tiene una precedencia y asociatividad, pero se pueden alterar stas usando parntesis. Los parntesis cumplen dos propsitos: e e e o Agrupan subexpresiones, de tal manera que se asocien a un operador. Por ejemplo, para indicar que el operando de la ra es b2 4ac encerrar z amos esta subexpresin entre parntesis. o e

3.3 Expresiones en Java

91

Cambian el orden en que se evalan las subexpresiones, ya que en presencia u de parntesis las expresiones se evalan de adentro hacia afuera. Por ejemplo: e u x x{px 1q x{x 1

x1 x 1 x

Como se puede deducir del ejemplo anterior, la divisin tiene mayor preceo dencia (se hace antes) que la suma, por lo que en ausencia de parntesis se e evala como en el segundo ejemplo. Con los parntesis estamos obligando a u e que primero se evale la suma, para que pase a formar el segundo operando u de la divisin, como se muestra en el primer ejemplo. o Otra diferencia fuerte entre cuando escribimos frmulas o expresiones en papel o y cuando las escribimos en un programa es que la multiplicacin siempre debe ser o expl cita en el programa: 4ac 3px 2y q 4ac 3 px 2 y q

Finalmente, son pocos los lenguajes de programacin que tienen como operao 2 dor la exponenciacin, por lo que expresiones como b se tendrn que expresar en o a trminos de la multiplicacin de b por s misma, o bien usar algn mtodo (como e o u e el que usamos para ra cuadrada) que proporcione el lenguaje o alguna de sus z bibliotecas. La famosa frmula para la solucin de una ecuacin de segundo o o o grado quedar entonces a x1

 b

c2
b 2a

4ac

x1  pb M ath.sqrtppb bq p4 a cqqq{p2 aq

Con esta organizacin de parntesis, lo primero que se hace es calcular b b y o e 4 a c. Una vez que se tiene el resultado, se resta el segundo del primero. Una vez que se tiene el resultado, se le saca ra cuadrada (se invoca a un mtodo que z e sabe sacarla). Despus se resta este resultado de b, se obtiene el producto de 2 a e y lo ultimo que se hace es la divisin. Si no usramos parntesis, la expresin se o a e o interpretar as a :

c2
b

4ac a
2

b M ath.sqrtpb b 4 a cq{2 a

92

Clases y objetos

Otro aspecto importante de los operadores es el nmero de operandos sobre el u que trabajan. Estamos acostumbrados a operadores unarios (de un solo operando, como el o el ) y binarios (como la suma o la multiplicacin). En general, o podemos tener operadores que tengan ms de dos operandos. a A continuacin damos una lista de operadores (no incluye mtodos de la clase o e Math), listados en orden de precedencia, y con su asociatividad y nmero de u operandos indicado. En general los operadores se evalan de izquierda a derecha, u para operadores de la misma precedencia o iguales (cuando la sintaxis lo permite), excepto los operadores de asignacin que se evalan de derecha a izquierda. En el o u caso de estos operadores unicamente la ultima expresin a la derecha puede ser o algo que no sea una variable.

Cuadro 3.4

Operadores de Java
Operandos posjo unario posjo unario prejo n-ario posjo unario posjo unario unario prejo unario prejo unario prejo unario prejo unario prejo unario prejo unario prejo unario prejo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo

rs pxparmetrosyq a xvariabley xvariabley xvariabley xvariabley xexpresiny o xexpresiny o xexpresiny o ! xexpresiny o new xconstructory pxtipoyq xexpresiny o {
%

S mbolo

Descripcin o arreglos selector de clase lista de parmetros a auto post-incremento auto post-decremento auto pre-incremento auto pre-decremento signo positivo signo negativo complemento en bits negacin booleana o instanciador casting multiplicacin o divisin o mdulo o suma rest corrimiento de bits a la izquierda llenando con ceros corrimiento de bits a la derecha propangado el sign corrimiento de bits a la derecha llenando con cero

Prec 1

2 3 4

5 6

3.3 Expresiones en Java

93

Cuadro 3.4

Operadores de Java. . . . . . (contina) u


Operandos binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo ternario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo binario injo

 

S mbolo

instanceof

 !
&

|| xexp logy?xexpy :xexpy     { %   


&

&&

 |

Descripcin o Prec relacional menor que 7 relacional menor o igual que relacional mayor que relacional mayor o igual que relacional ejemplar de relacional, igual a 8 relacional, distinto d AND de bits 9 XOR de bits 10 OR de bits 11 AND lgico o 12 OR lgico o 13 Condicional aritmtica e 14 asignacin o 15 autosuma y asignacin o autoresta y asignacin o 15 autoproducto y asignacin o autodivisin y asignacin o o automdulo y asignacin o o autocorrimiento derecho con propagacin y asignacin o o autocorrimiento izquierdo y asignacin o autocorrimiento derecho llenando con ceros y asignacin o auto-AND de bits y asignacin o auto-XOR de bits y asignacin o auto-OR de bits y asignacin o

Sabemos que sta es una lista extens e sima de operadores. Conforme vayamos entrando a cada uno de los temas , y requiramos de los operadores, aclararemos ms su uso y su signicado. a

94

Clases y objetos

Estamos ya en condiciones de escribir prcticamente las implementaciones de a todos los mtodos en nuestro programa. Lo haremos, siguiendo el mismo orden e que utilizamos para escribir los encabezados.

Implementacin de los constructores o


Lo unico que deseamos hacer en los constructores de las clases Manecilla y Reloj es la de asignar valores iniciales a los atributos. Estos valores vienen como argumentos de los constructores. La programacin se puede ver en los listados 3.22 o y 3.21. Omitimos los comentarios de JavaDoc.

Cdigo 3.21 Implementacin de constructores de la clase Manecilla o o


/ C o n s t r u c t o r que i n i c i a l i z a e l l m i t e / public Manecilla ( int lim ) { LIM = l i m ; / Toma e l v a l o r d e l argumento / } // M a n e c i l l a ( i n t ) / C o n s t r u c t o r que i n i c i a l i z a l m i t e y v a l o r / public M a n e c i l l a ( i n t lim , i n t v a l ) { / Toma v a l o r de p r i m e r argumento / LIM = l i m ; / Toma v a l o r de s e g u n d o argumento / valor = val ; } // M a n e c i l l a ( i n t , i n t )

Cdigo 3.22 Implementacin de constructores de la clase Reloj o o


/ E s t a b l e c e l o s l m i t e s de l a s m a n e c i l l a s / p u b l i c R e l o j ( i n t limH , i n t limM ) { / C o n s t r u y e e l o b j e t o h o r a s , i n v o c a n d o a uno de s u s c o n s t r u c t o r e s / horas = new M a n e c i l l a ( limH ) ; / Ahora c o n s t r u y e a l o b j e t o m i n u t o s / m i n u t o s = new M a n e c i l l a ( limM ) ; } // R e l o j ( i n t , i n t ) / E s t a b l e c e l o s l m i t e s y e l v a l o r i n i c i a l de l a s m a n e c i l l a s / p u b l i c R e l o j ( i n t limH , i n t limM , i n t h r s , i n t mins ) { / C o n s t r u y e e l o b j e t o h o r a s , e s t a b l e c i e n d o e l l m i t e y e l e s t a d o i n i c i a l / horas = new M a n e c i l l a ( limH , h r s ) ; / Ahora c o n s t r u y e a l o b j e t o con e s t a d o i n i c i a l / m i n u t o s = new M a n e c i l l a ( limM , mins ) ; } // R e l o j ( i n t , i n t , i n t , i n t )

Hay que recordar que estos mtodos, por ser constructores, de hecho regresan e

3.3 Expresiones en Java

95

un objeto de la clase de la que son constructores.

Implementacin de los mtodos de acceso o e


Los mtodos de acceso, como ya mencionamos, regresan un valor del tipo del e atributo que deseamos observar. En el listado 3.23 se encuentran las implementaciones de los mtodos de acceso de la clase Manecilla a la clase Reloj no le e declaramos ningn mtodo de acceso. u e

Cdigo 3.23 Mtodos de acceso de la clase Manecilla o e


/ I n f o r m a l m i t e de m a n e c i l l a @ r e t u r n c o n t e n i d o de LIM / public int getLimite () { r e t u r n LIM ; } // M a n e c i l l a . g e t L i m i t e ( ) / I n f o r m a v a l o r de m a n e c i l l a R e g r e s a c o n t e n i d o de v a l o r / public int getValor () { return v a l o r ; } // M a n e c i l l a . g e t V a l o r ( )

Implementacin de los mtodos de manipulacin o e o


En estos mtodos tenemos todos aquellos que que cambian el estado de los e objetos. Los que corresponden a la clase Reloj se encuentran en el listado 3.24 y las de la clase Manecilla en el listado 3.25 en la siguiente pgina. a

Cdigo 3.24 Mtodos de manipulacin de la clase Reloj o e o


/ Cambia e l e s t a d o de l a s d o s m a n e c i l l a s @params h r s e l nuevo v a l o r p a r a h o r a s @params mins e l nuevo v a l o r p a r a m i n u t o s p u b l i c v o i d s e t V a l o r ( i n t h r s , i n t mins ) { horas . setValor ( hrs ) ; / p i d e a h o r a s que cambie / m i n u t o s . s e t V a l o r ( mins ) ; / p i d e a m i n u t o s que cambie / } // R e l o j . s e t V a l o r ( i n t , i n t )

1/2

96

Clases y objetos 2/2

Cdigo 3.24 Mtodos de manipulacin de la clase Reloj o e o


/ I n c r e m e n t a 1 minuto y ve s i s e r e q u i e r e incrementa horas / public void incrementa ( ) { horas . s e t V a l o r ( horas . getValor + minutos . incrementa ( ) ) ; / M a n e c i l l a s . i n c r e m e n t a d a r 1 s i d i o l a v u e l t a a y 0 s i no / } // R e l o j . i n c r e m e n t a ( )

Cdigo 3.25 Mtodos de manipulacin de la clase Manecilla o e o


/ C o p i a e l v a l o r c u i d a n d o que e s t en r a n g o s / e public void s e t V a l o r ( i n t v a l ) { v a l o r = v a l % LIM ; / C a l c u l a m dulo LIM / o } // M a n e c i l l a . s e t V a l o r ( i n t ) / I n c r e m e n t a en 1 e l v a l o r , c u i d a n d o de que quede en r a n g o s . R e g r e s a 1 s i l l e g a l LIM y 0 s i no / o public int incrementa () { v a l o r ++; / i n c r e m e n t a en 1 / v a l o r %= LIM ; / V e r i f i c a s i a l c a n z LIM / o r e t u r n ( v a l o r == 0 ) ? 1 : 0 ; / S i v a l o r e s c e r o , a l c a n z a o LIM , p o r l o que debe r e g r e s a r 1 ; s i no e s a s , r e g r e s a 0 / } // M a n e c i l l a . i n c r e m e n t a ( )

Implementacin de mtodos de implementacin o e o


El unico mtodo que tenemos de implementacin es, como ya dijimos, el que e o muestra el reloj, que se encuentra en el listado 3.26.

Cdigo 3.26 Mtodos de implementacin de la clase Reloj o e o


public void muestra ( Consola c o n s o l a ) { / E l u s u a r i o d i c e d nde m o s t r a r / o S t r i n g mensaje1 , mensaje2 , m e n s a j e 3 ; m e n s a j e 1 = "Son las " ; m e n s a j e 2 = " horas con " ; m e n s a j e 1 = " minutos ." ; c o n s o l a . i m p r i m e ( m e n s a j e 1+h o r a s . g e t V a l o r ( ) + m e n s a j e 2+m i n u t o s . g e t V a l o r ()+ m e n s a j e 3 ) ; / E l o p e r a d o r + con c a d e n a s f u e r z a a l o s e n t e r o s g e t V a l o r ( ) a c o n v e r t i r s e a c a d e n a s / } // R e l o j . m u e s t r a ( C o n s o l a )

3.3 Expresiones en Java

97

Tenemos ya las clases terminadas. Ahora tendr amos que tener un usuario que comprara uno de nuestros relojes. Hagamos una clase cuya unica funcin sea o probar el Reloj. La llamaremos UsoReloj. Se encuentra en el listado 3.27.

Cdigo 3.27 Clase usuaria de la clase Reloj o


import i c c 1 . i n t e r f a z . C o n s o l a ; public class UsoReloj { p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { / D e c l a r a c i o n e s : / d e c l a r a c i n de una v a r i a b l e t i p o R e l o j / o Reloj r e l o j i t o ; / D nde m o s t r a r e l r e l o j / o Consola c o n s o l i t a ; / C o n s t r u c c i n de l o s o b j e t o s : o V a l o r e s i n i c i a l e s / r e l o j i t o = new R e l o j ( 1 2 , 6 0 , 1 1 , 5 8 ) ; / E l c o n s t r u c t o r s i n p a r m e t r o s / a c o n s o l i t a = new C o n s o l a ( ) ; / M a n i p u l a c i n d e l r e l o j i t o / o r e l o j i t o . incrementa ( ) ; r e l o j i t o . muestra ( c o n s o l i t a ) ; r e l o j i t o . incrementa ( ) ; r e l o j i t o . muestra ( c o n s o l i t a ) ; r e l o j i t o . setValor (10 ,59); r e l o j i t o . muestra ( c o n s o l i t a ) ; r e l o j i t o . incrementa ( ) ; r e l o j i t o . muestra ( ) ; } // main } // U s o R e l o j

Se estarn preguntando por qu no se declar a relojito y consolita como atria e o butos de la clase. La razn es que un mtodo esttico de la clase no puede tener o e a acceso a atributos de esa clase. Por ello hay que declararlo en el mtodo. De cuale quier forma, como la clase Reloj es pblica, cualquiera puede pedir constructores u de esa clase.

3.3.1.

Declaracin y denicin simultneas o o a


No hemos mencionado que en Java se permite asignar valor inicial a los atributos y a las variables locales en el momento en que se declaran. Esto se consigue

98 simplemente con el operador de asignacin y una expresin: o o


public int valor = 0; R e l o j r e l o j i t o = new R e l o j ( 1 2 , 6 0 ) ;

Clases y objetos

Para el caso de los atributos de un objeto, no se le ve mucho caso asignar estado inicial a los atributos, excepto cuando queramos que todos los objetos de esa clase compartan el estado inicial. Por ejemplo, en el caso de los objetos de la clase Reloj es posible que queramos que todos los objetos empiecen con las 0 horas y 0 minutos; pero en el caso de los objetos de la clase Manecilla, si le diramos e valor inicial al atributo Lim despus ya no podr e amos volverle a asignar un valor, y todos los objetos tendr el mismo l an mite, algo que no queremos que suceda.

Manejo de cadenas y expresiones

Uno de los ingredientes que ms comnmente vamos a usar en nuestros proa u gramas son las expresiones. Por ello, dedicaremos este cap tulo a ellas.

4.1 Manejo de cadenas en Java


Una expresin es cualquier sucesin de operadores y operandos que producen o o (regresan) un valor al evaluarse. El valor puede ser numrico, de cadenas, una e referencia a un objeto, booleano, o de cualquier otra clase accesible al mtodo en e la que se encuentra la expresin. o Las cadenas de caracteres van a ser de lo que ms vamos a usar en nuestro a desarrollo profesional. Prcticamente todo el manejo que hagamos involucrar caa a denas, ya sea como t tulos, o como objeto de bsquedas, de agrupamiento, etc. u Las cadenas en Java son una clase que nos proporciona el paquete Java.Lang y est accesible sin necesidad de importarlo. Cada vez que declaramos una cadena, a mediante el identicador de tipo String, reservamos espacio unicamente para la referencia, ya que se trata de objetos. Sin embargo, por lo comn que son las u

100

Manejo de cadenas y expresiones

cadenas la sintaxis de Java es mucho ms exible para la creacin de cadenas que a o de objetos en general y nos permite cualquiera de los siguientes formatos:
I.

En la declaracin. Simplemente inicializamos la variable con una cadena: o


S t r i n g c a d e n a = "Esta es una cadenita " ;

II.

En una asignacin. Se asigna una cadena a una variable tipo String: o


S t r i n g cadenota ; c a d e n o t a = "Una cadena "+ " muy larga " ;

III.

Al vuelo. Se construye una cadena como una expresin, ya sea directamente o o mediante funciones de cadenas:
" Cadena Muy Larga " . t o L o w e r C a s e ( )

Es importante mencionar que las cadenas, una vez creadas, no pueden ser modicadas. Si se desea modicar una cadena lo que se debe hacer es construir una nueva con las modicaciones, y, en todo caso, reasignar la nueva. Por ejemplo, si queremos pasar a maysculas una cadena, podr u amos tener la siguiente sucesin o de enunciados:
S t r i n g m i n u s c = "est en min sculas " ; a u minusc = minusc . toUpperCase ( ) ;

Ntese que en el primer rengln de este cdigo la cadena contiene unicamente o o o minsculas. En el lado derecho de la asignacin en el segundo rengln se construye u o o una cadena nueva que es la cadena minusc pero pasada a maysculas; lo ultimo u que se hace es reasignar la referencia de minusc a que ahora apunte a esta nueva cadena. Lo distinto cuando a manejo de cadenas se reere es que no necesitamos el operador new aunque lo podemos usar con alguno de los mtodos constructores e para construir un objeto tipo String. La clase String proporciona much simos mtodos para trabajar con cadenas. No e mostramos todos, pues algunos de ellos tienen parmetros o entregan valores que a no hemos visto todav A continuacin se encuentra una tabla con los mtodos a. o e que podemos querer usar de la clase String.

4.1 Manejo de cadenas en Java

101

Cuadro 4.1

Mtodos de la clase e
Firma Constructores:
String ()

String

Descripcin o

String ( String )

Construye una nueva cadena nula en el primer caso, y otra que es copia de la primera en el segundo. En ambos casos regresa un apuntador al heap.

Mtodos para crear nuevas cadenas: e String concat( String ) Crea una nueva cadena que es a la que se le solicita el mtodo, seguida del e argumento.
String replace (char, char)

Crea una nueva cadena en la que reemplaza las apariciones del primer carcter por el segundo. a Crea una nueva cadena en la que reemplaza las apariciones de la primera cadena por la segunda. Crean una nueva cadena que es una subcadena de la cadena. La subcadena empieza en el primer entero y termina, en el primer caso al nal de la cadena y en el segundo en el segundo entero. Crea una nueva cadena convirtiendo todos los carcteres a minsculas. a u Crea una nueva cadena convirtiendo todos los carcteres a maysculas. a u Crea una nueva cadena quitando los blancos del principio y nal

String

replace ( String , String )

String String

substring (int) substring (int , int)

String

toLowerCase()

String

toUpperCase()

String

trim()

Mtodos para crear nuevas cadenas: e static String valueOf(boolean) Crea una cadena con el valor que corresponde al tipo del dato. Como es static String valueOf(char) esttica se puede llamar desde la clase: a static String valueOf(int) String .valueOf( valor ) .

102

Manejo de cadenas y expresiones

Cuadro 4.1

Mtodos de la clase e
Firma

String

(contina) u

Descripcin o

Mtodos para crear nuevas cadenas: e (contina) u static String valueOf(long) Crea una cadena con el valor que corresponde al tipo del dato. Como es static String valueOf( oat ) esttica se puede llamar desde la clase: a static String valueOf(double) String .valueOf( valor ) . Mtodos de comparacin: e o int compareTo(String) Compara dos cadenas en el orden del cdigo Unicode. o
6 90 9 9 9 9 0 9 8

boolean

equals(Object)

si las cadenas son idnticas e si xcad1y va despus en el orden e que xcad2y. 9 9 9 0 si xcad1y va antes en el orden 9 9 9 7 que xcad2y. Dice si la cadena en el parmetro es a idntica a la que invoca. e Dice si las cadenas son iguales, ignorando diferencias entre maysculas y u minsculas. u Dice si la cadena con la que se invoca termina con la cadena en el parmetro. a El primer entero corresponde al cdio go de un carcter en Unicode (se puea de pasar como argumentos tambin un e carcter). La cadena se reere a una a subcadena. En las 4 versiones, regresa la primera posicin en la cadena dono de se encuentra el primer parmetro. Si a se da un segundo parmetro, ste india e ca que se busque a partir de esa posicin. Regresa -1 si no encuentra lo que o est buscando. a

boolean

equalsIgnoreCase( String )

Mtodos de b squeda: e u
boolean endsWith(String)

int int int int

indexOf(int) indexOf(int , int) indexOf(String ) indexOf(String , int)

4.1 Manejo de cadenas en Java

103

Cuadro 4.1

Mtodos de la clase e
Firma
boolean boolean

String

(contina) u

Descripcin o
startsWith ( String ) startsWith ( String , int)

int int int int

lastIndexOf (char) lastIndexOf (char, int) lastIndexOf ( String ) lastIndexOf ( String , int)

boolean boolean

regionMatches (int , String , int , int) regionMatches (boolean,int, String , int , int)

Determina si es que la cadena empieza con la cadena que trae como argumento. En la segunda versin, ve a partir o del carcter denotado por el argumena to entero. El carcter corresponde a un carcter a a (se puede pasar como argumentos tambin un cdigo entero de un carcter e o a en Unicode). La cadena se reere a una subcadena. En las 4 versiones, regresa la ultima posicin en la cadena donde se o encuentra el primer parmetro. Si se da a un segundo parmetro, ste indica que a e se busque a partir de esa posicin. Reo gresa -1 si no encuentra lo que est busa cando. Determina si una regin de la cadeo na es igual a una regin de la cadeo na en el argumento. La segunda versin, si el argumento booleano es vero dadero, compara ignorando diferencias entre maysculas y minsculas. El priu u mer entero es la posicin de la regin o o en la cadena que invoca. el segundo entero es loa posicin inicial en la cadena o del argumento. La tercera posicin es el o nmero de carcteres a comparar. u a Regresa el carcter que se encuentra en a la posicin dada por el argumento. o Genera la representacin en cadena del o objeto con el que se le invoca. Regresa el tamao de la cadena, el n nmero de carcteres. u a

Mtodos de conversin e o
char String charAt(int) toString ()

Otros Mtodos e
int length ()

104

Manejo de cadenas y expresiones

4.2 Implementacin de una base de datos o


Supongamos que tenemos un conjunto de cadenas almacenadas de alguna forma (nos preocuparemos de la implementacin despus). Por ejemplo, tengo los o e nombres de los estudiantes del grupo con su carrera y quiero poder extraer datos de all Tenemos, entonces, lo que se conoce como una base de datos. Identique. mos las operaciones que deseamos poder hacer con esa base de datos:

Problema: Mantener una base de datos con listas de cursos.

Descripcin: o Cada objeto del curso consiste del nmero del grupo (una cadena), la lista de u alumnos y el nmero de alumnos. La lista de alumnos consiste de alumnos, u donde para cada alumno tenemos su nombre completo, su nmero de cuenta, u la carrera en la que estn inscritos y su clave de acceso a la red. a Las operaciones que queremos se puedan realizar son: (a) Localizar a un estudiante, proporcionando cualquiera de sus datos, que lo distingan de los otros estudiantes. (b) Dado un dato particular de un estudiante, recuperar su nombre, clave, cuenta o carrera. (c) Agregar estudiantes. (d) Quitar estudiantes. (e) Poder emitir la lista de todo el grupo. (f) Emitir la sublista de los que contienen cierto valor en alguno de sus campos.

Entonces, nuestra tarjeta de responsabilidades, en cuanto a la parte pblica se u reere, se puede ver en la gura 4.1.

4.2 Implementacin de una base de datos o

105

Figura 4.1

Tarjeta de responsabilidades para Curso.

Clase: Curso
P u b l i c o
Constructores daNombre daCarrera daClave daCuenta agregaEstudiante quitaEstudiante listaCurso losQueCazanCon armaRegistro

Responsabilidades
A partir de una base de datos inicial y a partir de cero. Regresa el nombre completo de un estudiante Regresa la carrera de un estudiante Regresa la clave de acceso de un estudiante Regresa el nmero de cuenta de un estudiante u Agrega a un estudiante, proporcionando los datos correspondientes. Elimina al estudiante identicado para eliminar. Lista todos los estudiantes del curso Lista a los estudiantes que cazan con algn criterio u espec co Regresa el registro bonito para imprimir

De esto, podemos denir ya una interfaz de Java que se encargue de denir estos servicios. La podemos ver en el Listado 4.1.

Cdigo 4.1 Interfaz para el manejo de una base de datos o


1: / 2: Da e l manejo de una b a s e de d a t o s s o b r e l a que s e 3: d e s e a n h a c e r c o n s u l t a s . 4: / 5: 6: p u b l i c i n t e r f a c e C o n s u l t a s { 7: / 8: P ro duc e l a l i s t a de t o d o s l o s r e g i s t r o s en l a 9: b a s e de d a t o s . 10: @ r e t u r n Una c a d e n a con l a l i s t a e d i t a d a . 11: / 12: public String l i s t a C u r s o ( ) ;

1/3

106

Manejo de cadenas y expresiones 2/3

Cdigo 4.1 Interfaz para el manejo de una base de datos o


13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: / Dada l a p o s i c i n d e l r e g i s t r o , r e g r e s a e l nombre o d e l alumno en e s a p o s i c i n . o @param c u a l La p o s i c i n d e l r e g i s t r o . o @ r e t u r n Una c a d e n a que c o n t i e n e e l nombre c o m p l e t o d e l r e g i s t r o en e s a p o s i c i n . o / p u b l i c S t r i n g daNombre ( i n t c u a l ) ; / Dada l a p o s i c i n d e l r e g i s t r o , r e g r e s a l a c a r r e r a o d e l alumno en e s a p o s i c i n . o @param c u a l La p o s i c i n d e l r e g i s t r o . o @ r e t u r n Una c a d e n a que c o n t i e n e e l nombre de l a c a r r e r a d e l r e g i s t r o en e s a p o s i c i n . o / public String daCarrera ( int cual ) ; / Dada l a p o s i c i n d e l r e g i s t r o , r e g r e s a l a c a r r e r a o d e l alumno en e s a p o s i c i n . o @param c u a l La p o s i c i n d e l r e g i s t r o . o @ r e t u r n Una c a d e n a que c o n t i e n e l a c a r r e r a d e l r e g i s t r o en e s a p o s i c i n . o / public String daCarrera ( int cual ) ; / Dada l a p o s i c i n d e l r e g i s t r o , r e g r e s a e l n mero o u de c u e n t a d e l alumno en e s a p o s i c i n . o @param c u a l La p o s i c i n d e l r e g i s t r o . o @ r e t u r n Una c a d e n a que c o n t i e n e e l n mero de c u e n t a u d e l r e g i s t r o en e s a p o s i c i n . o / p u b l i c S t r i n g daCuenta ( i n t c u a l ) ; / Dada l a p o s i c i n d e l r e g i s t r o , r e g r e s a l a c l a v e o de a c c e s o d e l alumno en e s a p o s i c i n . o @param c u a l La p o s i c i n d e l r e g i s t r o . o @ r e t u r n Una c a d e n a que c o n t i e n e l a c l a v e de a c c e s o d e l r e g i s t r o en e s a p o s i c i n . o / public S t r i n g daClave ( int cual ) ;

4.2 Implementacin de una base de datos o

107
3/3

Cdigo 4.1 Interfaz para el manejo de una base de datos o


57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: / Dada l a p o s i c i n d e l r e g i s t r o , r e g r e s a una c a d e n a que o c o n t i e n e e l r e g i s t r o c o m p l e t o d e l alumno en e s a p o s i c i n . o @param c u a l La p o s i c i n d e l r e g i s t r o . o @return Una c a d e n a que c o n t i e n e e l n mero de u c u e n t a d e l r e g i s t r o en e s a p o s i c i n . o / public String armaRegistro ( int cual ) ; / L i s t a a l o s e s t u d i a n t e s cuyo r e g i s t r o c o n t i e n e como s u b c a d e n a a l a c a d e n a en e l p a r m e t r o . a @param s u b c a d La s u b c a d e n a que buscamos . @return Una c a d e n a con un r e g i s t r o p o r r e n g l n . o / p u b l i c S t r i n g losQueCazanCon ( S t r i n g s u b c a d ) ;

En la interfaz que acabamos de dar, casi todos los mtodos que hacen la cone sulta trabajan a partir de saber la posicin relativa del registro que queremos. Sin o embargo, una forma comn de interrogar a una base de datos es proporcionndole u a informacin parcial, como pudiera ser alguno de los apellidos, por lo que conviene o agregar un mtodo al que le proporcionamos esta informacin y nos deber dee o a cir la posicin relativa del registro que contiene esa informacin. Este mtodo lo o o e podemos ver en el Listado 4.2.

Cdigo 4.2 Posicin de un registro que contenga una subcadena o o


75: 76: 77: 78: 79: 80: 81: 82: 83: 84: / Dada una c a d e n a con e l nombre d e l alumno , r e g r e s a l a p o s i c i n d e l r e g i s t r o que c o n t i e n e e s e nombre . o @param nombre E l nombre d e l alumno que buscamos . @ r e t u r n Un e n t e r o que c o r r e s p o n d e a l a p o s i c i n o r e l a t i v a d e l r e g i s t r o que c o n t i e n e a l nombre . / p u b l i c i n t d a P o s i c i o n ( S t r i n g nombre ) ;

(Consultas)

Sin embargo, pudiramos buscar una porcin del registro que se repite ms de e o a una vez, y quisiramos que al interrogar a la base de datos, sta nos diera, uno tras e e

108

Manejo de cadenas y expresiones

otro, todos los registros que tienen esa subcadena. Queremos que cada vez que le pidamos no vuelva a empezar desde el principio, porque entonces nunca pasar a del primero. Le agregamos entonces un nuevo parmetro para que la bsqueda a u sea a partir de un posicin. El encabezado de este mtodo se puede ver en el o e Listado 4.3.

Cdigo 4.3 Posicin de un registro a partir de otra posicin o o o


85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102:

(Consultas)

/ Dados una c a d e n a con e l nombre d e l alumno y a p a r t i r de c u l p o s i c i n b u s c a r , r e g r e s a l a p o s i c i n d e l r e g i s t r o a o o que c o n t i e n e e s e nombre , s i n e x a m i n a r a l o s que e s t n a n t e s a de l a p o s i c i n dada . o @param nombre E l nombre d e l alumno que buscamos . @param d e s d e A p a r t i r de donde s e va a h a c e r l a b u s q u e da . @ r e t u r n Un e n t e r o que c o r r e s p o n d e a l a p o s i c i n o r e l a t i v a d e l r e g i s t r o que c o n t i e n e a l nombre , b u s c a d o a p a r t i r de desde . / p u b l i c i n t d a P o s i c i o n ( S t r i n g nombre , i n t d e s d e ) ;

Denida ya la interfaz, procedemos a disear una implementacin para la n o misma. Siguiendo la metodolog que tenemos para denir las clases, una vez a denidas las responsabilidades debemos decidir cul es la informacin que requiere a o la clase para poder brindar los servicios anunciados en la interfaz. Lo primero que necesitamos es la informacin que corresponde a la base de datos y, de alguna o manera, la descripcin de qu contiene cada registro. La manera como he decidido o e guardar esta base de datos es en una cadena enorme, pero subdividida en pedazos del mismo tamao. A cada uno de estos pedazos lo vamos a manejar como un n registro. Adems, cada registro lo vamos a dividir en campos, donde cada campo a corresponde a una unidad de informacin; por ejemplo, el nombre del estudiante o corresponde a un campo, as como la clave a otro, el nmero de cuenta a un tercero u y as sucesivamente. Esto nos facilita ver a la base de datos como si fuera una tabla, donde cada rengln de la tabla corresponde a un registro y cada columna de la o tabla a un campo (o atributo). Supongamos que tenemos una lista de alumnos como la que se muestra en la Tabla 4.2 en la pgina opuesta: a

4.2 Implementacin de una base de datos o

109

Cuadro 4.2

Listado del contenido de nuestra base de datos


Nombre: Aguilar Sol Aries Olaf s Cruz Cruz Gil No e Garc Villafuerte Israel a Hubard Escalera Alfredo Tapia Vzquez Rogelio a ... Carrera: Matemtico a Computacin o Computacin o Computacin o Actuar a ... Cuenta: 97541219-1 99036358-4 02598658-3 00276238-7 02639366-8 ... Clave: aguilarS cruzCruz garciaVi hubardE tapiaV ...

Como ya mencionamos, vamos a representar a la base de datos con una cadena en la que colocaremos a todos los registros. Si no forzamos a que cada registro ocupe el mismo nmero de posiciones no podr u amos decir de manera sencilla dnde o termina un registro y empieza el siguiente. En cada uno de los registros, el nmeu ro de posiciones que ocupa, por ejemplo, el nombre tambin debe ser el mismo, e tambin para que podamos calcular, de manera sencilla, la posicin en la cadena e o donde empieza cada campo dentro de cada registro. La declaracin con valores o iniciales para esta lista se ver entonces, como sigue: a,
private String lista = " Aguilar Sol s Aries Olaf " "Cruz Cruz Gil No e " " Garc a Villafuerte Israel " " Hubard Escalera Alfredo " " Tapia V zquez Rogelio a "

+ + + + +

"Mate" "Comp" "Comp" "Comp" "Actu"

+ + + + +

" 975412191 " " 990363584 " " 025986583 " " 002762387 " " 026393668 "

+ + + + +

" aguilarS " + " cruzCruz " + " garciaV " + " hubardE " + " tapiaV " ;

Recuerden que podemos construir una cadena concatenando cadenas, usando el operador +. Adems, aparecen los carcteres blancos dentro de las cadenas a a , para que podamos ver fcilmente el nmero de posiciones que ocupa. a u con Dividimos la cadena arbitrariamente en registros, uno por rengln, y cada o registro en campos. Noten que cada nombre ocupa 25 posiciones y cada carrera 4; el nmero de cuenta ocupa 9 posiciones y la clave de acceso 8 posiciones. Cada u elemento de mi lista ocupa, entonces, 46 posiciones. Ntese tambin que elegimos o e codicar la carrera, pues con cuatro carcteres tengo suciente para reconocer a la carrera; para el nmero de cuenta usamos 9 posiciones, pues la que estar u a entre el octavo d gito y el noveno siempre contiene un guin; por ultimo, para la o

110

Manejo de cadenas y expresiones

clave de usuario utilizamos 8 posiciones, completando cuando es necesario como lo hicimos en el nombre. La lista empieza en 0, y tiene, en total, 5 46  230 posiciones (de la 0 a la 229). El primer registro empieza en la posicin 0; el segundo registro empieza en o la posicin 46. En general, el i-simo registro empieza en la posicin pi 1q 46. o e o Por qu i 1? Porque hay que saltar i 1 registros para llegar a donde empieza e el i-simo. e El primer nombre empieza donde el primer registro; el segundo nombre donde el segundo registro y as sucesivamente. La carrera que corresponde al primer registro empieza en la posicin 25, una vez saltadas las primeras 25 posiciones (de o la 0 a la 24) que corresponden al nombre. La del segundo registro empieza en la posicin 71 (46+25), que corresponde a saltar las primeras 46 posiciones (de la o 0 a la 45) que corresponden al primer registro, ms las primeras 25 (de la 0 a la a 24) posiciones que corresponden la nombre del segundo registro. En general, la posicin de la carrera del i-simo registro empieza en la posicin pi 1q 46 25, o e o donde 46 es el nmero de posiciones que hay que saltar por cada elemento de u la tabla que se encuentra antes que el que queremos, y 25 es el desplazamiento (oset) del campo que deseamos a partir del principio del elemento. En general, si se desea el j-simo campo del i-simo registro, se obtiene la posicin inicial del e e o i-simo registro (pi 1q 46) y a eso se le suma el total de las posiciones que e ocupan los campos desde el primero hasta el j 1. Construyamos una clase para manejar listas de cursos. Ya tenemos, de la interfaz, los mtodos pblicos que vamos a requerir; ahora hay que decidir que atributos e u requiere la clase para poder dar esos servicios. Si estamos hablando de un grupo en la Facultad de Ciencias es conveniente que se guarde el nmero del grupo. u Tambin es conveniente que cada base de datos me pueda responder, de manera e sencilla, el nmero de registros que tiene en ese momento. Estos tres atributos son u privados y en todo caso se tiene acceso a ellos a travs de mtodos de acceso. La e e tarjeta de responsabilidades, incluyendo a estos atributos privados se encuentra en la gura 4.2. Como parte de la informacin que requiere la clase es conveniente declarar los o tamaos del registro y de los campos como constantes para poder dar expresiones n aritmticas en trminos de estas constantes. De esa manera si decidimos cambiar el e e tamao de alguno de los campos unicamente tenemos que localizar la declaracin n o de la constante para hacerlo. El inicio de la codicacin la podemos ver en el o listado 4.4 en la pgina opuesta. a

4.2 Implementacin de una base de datos o

111

Figura 4.2

Tarjeta de responsabilidades para Curso. Clase: Curso


Constructores P u b l i c o quitaEstudiante listaCurso losQueCazanCon armaRegistro P r i v a d o lista grupo nmero de registros u daNombre daCarrera daClave daCuenta agregaEstudiante

Responsabilidades
A partir de una base de datos inicial y a partir de cero. Regresa el nombre completo de un estudiante Regresa la carrera de un estudiante Regresa la clave de acceso de un estudiante Regresa el nmero de cuenta de un estudiante u Agrega a un estudiante, proporcionando los datos necesarios. Elimina a un estudiante despus de identie carlo. Lista todos los estudiantes del curso Lista a los estudiantes que cazan con algn u criterio espec co Regresa el registro bonito para imprimir Base de datos con los alumnos inscritos Nmero que identica al grupo u En cada momento, el nmero de registros que u contiene el grupo

Cdigo 4.4 Clase que maneja listas de cursos o

(Curso) 1/2

1: import i c c 1 . i n t e r f a z . C o n s o l a 2: / 3: Base de d a t o s , a b a s e de c a d e n a s , que emula l a l i s t a de un c u r s o 4: de l i c e n c i a t u r a . T i e n e l a s o p c i o n e s n o r m a l e s de una b a s e de 5: d a t o s y f u n c i o n a m e d i a n t e un Men u 6: / 7: c l a s s C u r s o implements C o n s u l t a s { 8: private String l i s t a ; / Base de d a t o s / 9: private S t r i n g grupo ; / C l a v e d e l g r u p o / 10: p r i v a t e i n t numRegs ; / N mero t o t a l de r e g i s t r o s / u

112

Manejo de cadenas y expresiones

Cdigo 4.4 Clase que maneja listas de cursos. o


11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:

(Curso)2/2

/ TAM XXXX : Tamao d e l campo n OFF XXXX : D i s t a n c i a d e l campo XXXX a l p r i n c i p i o d e l r e g i s t r o / p r i v a t e s t a t i c f i n a l i n t TAM REG = 46; p r i v a t e s t a t i c f i n a l i n t TAM NMBRE = 2 5 ; p r i v a t e s t a t i c f i n a l i n t TAM CARRE = 4 ; p r i v a t e s t a t i c f i n a l i n t TAM CTA = 9; p r i v a t e s t a t i c f i n a l i n t TAM CLVE = 8 ; p r i v a t e s t a t i c f i n a l i n t OFF NMBRE = 0 ; p r i v a t e s t a t i c f i n a l i n t OFF CARRE = 2 5 ; p r i v a t e s t a t i c f i n a l i n t OFF CTA = 29; p r i v a t e s t a t i c f i n a l i n t OFF CLVE = 3 8 ;

En esta pequea prueba estamos utilizando unicamente una funcin de can o denas (String), substring, que me entrega la subcadena que empieza en el primer parmetro y termina en el segundo parmetro, ambos enteros. Este mtodo lo a a e invocamos desde la lista de nombres y datos adicionales. Una tabla de mtodos e relevantes de la clase String en esta etapa se encuentran en la tabla 4.1 en la pgia na 101. Tenemos un mtodo que nos encuentra la primera posicin del i-simo e o e registro, daPosI(int i), y a partir de ah nos saltamos los campos que van antes del que queremos. As el campo que corresponde al nombre est en la posicin 0 de , a o cada registro - OFF NMBRE = 0 mientras que para llegar al campo con la clave de usuario hay que saltar el tamao del nombre, ms el tamao de la cuenta ms n a n a el tamao de la carrera OFF CLVE = 38 = 25 + 4 + 9. Similarmente localizamos n el inicio de cada uno de los otros campos. Veamos ahora la implementacin de los constructores en el diagrama de la o gura 4.3. En este diagrama vemos que lo que tenemos que hacer en cada uno de los constructores es darle valor inicial a los datos privados de la clase.

Figura 4.3

Diagrama de Warnier-Orr para los constructores.


6 9Construye lista inicial 8

Constructor

9 7

Registra nmero de estudiantes u Registra clave del grupo

Hab amos comentado que queremos dos constructores, uno que trabaje a partir de una lista que d el usuario, y otro que inicie con una lista vac y vaya agregando e a

4.2 Implementacin de una base de datos o

113

nombres conforma el usuario los va dando. El cdigo para ambos casos se puede o ver en el listado 4.5.

Cdigo 4.5 Constructores para la clase Curso o


24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52:

(Curso)

/ C o n s t r u y e una b a s e de d a t o s a p a r t i r de l o s d a t o s que de e l usuario . @param g r u p o La c l a v e d e l g r u p o @param l i s t a La l i s t a b i e n armada d e l g r u p o @param c u a n t o s E l n mero de r e g i s t r o s que s e r e g i s t r a n u / p u b l i c C u r s o ( S t r i n g grupo , S t r i n g l i s t a ) { t h i s . l i s t a = l i s t a == n u l l ? "" : lista ; t h i s . g r u p o = g r u p o == n u l l ? "????" : grupo ; numRegs = l i s t a . l e n g t h ()==0 ?0 : l i s t a . l e n g t h ( ) /TAM REG + 1 ; } / C o n s t r u y e una b a s e de d a t o s v a ca p e r o con n mero de g r u p o u @param g r u p o N mero de g r u p o u / public Curso ( S t r i n g grupo ) { t h i s . l i s t a = "" ; t h i s . g r u p o = g r u p o == n u l l ? "????" : grupo ; numRegs = 0 ; }

El mtodo que da la lista completa es muy sencillo, ya que unicamente regresa e la lista. Lo podemos ver en el listado 4.6.

Cdigo 4.6 Mtodo que regresa toda la lista o e


53: 54: 55: 56: 57: 58: 59: / Da a c c e s o a l a b a s e de d a t o s c o m p l e t a @return toda l a l i s t a / public String daLista () { return l i s t a ; }

(Curso)

114

Manejo de cadenas y expresiones

Para los mtodos que regresan un determinado campo de un registro tenemos e el algoritmo que se muestra en la gura 4.4.

Figura 4.4

Diagrama de Warnier-Orr para regresar el contenido de un campo.


6 9Da el inicio del i-simo registro e 9 9 9 9 9 9 9 9 9apunta al inicio del campo en ese registro 9 8

Da campo del i-simo registro e

9 9 9Apunta al nal del campo 9 9 9 9 9 9 9 9 7

Toma la subcadena correspondiente

Como siempre nos vamos a estar moviendo al inicio del i-simo registro vamos e a elaborar un mtodo, privado, que me d el carcter en el que empieza el i-simo e e a e registro, mediante la frmula o posicin o

 pi 1q T AM

REG.

Hay que tomar en cuenta ac al usuario, que generalmente va a numerar los a registros empezando desde el 1 (uno), no desde el 0 (cero). Con esto en mente y de acuerdo a lo que es la que discutimos al inicio de este tema, el mtodo queda e como se puede observar en el listado 4.7.

Cdigo 4.7 Clculo de la posicin donde empieza el i-simo registro o a o e


60: 61: 62: 63: 64: 65: 66: 67: 68:

(Curso)

/ Da e l n mero de c a r c t e r en e l que e m p i e z a e l i s i m o u a e registro . @param i e l o r d i n a l d e l r e g i s t r o @ r e t u r n e l c a r c t e r en e l que e m p i e z a a / private int daPosI ( int i ) { r e t u r n ( i 1) TAM REG ; }

Los mtodos que regresan un campo siguen todos el patrn dado en la gura 4.4 e o y su implementacin se puede ver en el listado 4.8 en la pgina opuesta. o a

4.2 Implementacin de una base de datos o

115 (Curso)

Cdigo 4.8 Mtodos que regresan el contenido de un campo o e


69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113:

/ R e g r e s a e l nombre c o m p l e t o d e l i s i m o r e g i s t r o . e @param e l o r d i n a l d e l r e g i s t r o @ r e t u r n l a c a d e n a que c o r r e s p o n d e a l nombre / p u b l i c S t r i n g daNombre ( i n t i ) { i n t empza , t e r m i n a ; empza = d a P o s I ( i ) + OFF NMBRE ; t e r m i n a = d a P o s I ( i ) + OFF NMBRE + TAM NMBRE; r e t u r n l i s t a . s u b s t r i n g ( empza , t e r m i n a ) ; } / R e g r e s a l a c a r r e r a d e l i s i m o r e g i s t r o e @param i e l o r d i n a l d e l r e g i s t r o @ r e t u r n l a s u b c a d e n a que c o r r e s p o n d e a l a c a r r e r a / public String daCarrera ( int i ) { i n t empza , t e r m i n a ; empza = d a P o s I ( i ) + OFF CARRE ; t e r m i n a = d a P o s I ( i ) + OFF CARRE + TAM CARRE ; S t r i n g c l a v e = l i s t a . s u b s t r i n g ( empza , t e r m i n a ) ; i n t c u a l C l a v e = " actbiofismatcom " } / R e g r e s a e l n mero de c u e n t a d e l i s i m o r e g i s t r o u e @param i e l o r d i n a l d e l r e g i s t r o @ r e t u r n l a s u b c a d e n a que c o r r e s p o n d e a l n mero de c u e n t a u / p u b l i c S t r i n g daCuenta ( i n t i ) { i n t empza , t e r m i n a ; empza = d a P o s I ( i ) + OFF CTA ; t e r m i n a = d a P o s I ( i ) + OFF CTA + TAM CTA ; r e t u r n l i s t a . s u b s t r i n g ( empza , t e r m i n a ) ; } / R e g r e s a l a c l a v e de a c c e s o d e l i s i m o r e g i s t r o e @param i e l o r d i n a l d e l r e g i s t r o @ r e t u r n l a s u b c a d e n a que c o r r e s p o n d e a l a c l a v e de a c c e s o / public S t r i n g daClave ( int i ) { i n t empza , t e r m i n a ; empza = d a P o s I ( i )+OFF CLVE ; t e r m i n a = d a P o s I ( i )+OFF CLVE + TAM CLVE ; r e t u r n l i s t a . s u b s t r i n g ( empza , t e r m i n a ) ; }

116

Manejo de cadenas y expresiones

Los mtodos que acabamos de programar asumen que se sabe el nmero de e u registro que se est buscando, por lo que tenemos que dar un mtodo que dada a e una subcadena regrese el nmero del registro que se est buscando. Como existe la u a posibilidad de que no se encuentre la subcadena, se debe vericar esa posibilidad. El algoritmo para este mtodo se muestra en la gura 4.5. e

Figura 4.5

Encontrar el nmero de registro al que pertenece una subcadena. u


6 9lugar la posicin de la subcadena o 9 5 9 9 9 9 inicio la posicin del inicio del registro o 9 o 9 Determinar 9Se encontr? lugar 8 Calcular el nmero de registro u nmero u 9 3 9 de registro 9 9 9Se encontr? 9 o 9 9 9 7

Regresar lugar

Para encontrar la posicin de la subcadena simplemente usamos mtodos de o e la clase String. Para encontrar el inicio del registro, simplemente le quitamos lo que sobre del mltiplo ms cercano del tamao del registro. Y nalmente vemos u a n cuntos registros caben en ese nmero. a u

Cdigo 4.9 Mtodo que da el primer registro con subcadena o e


114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125:

(Curso)

/ Da e l o r d i n a l que c o r r e s p o n d e a l p r i m e r o r e g i s t r o que c o n t i e n e a l a subcadena @param nombre s u b c a d e n a a b u s c a r @ r e t u r n e l o r d i n a l d e l r e g i s t r o , o 1 s i no hay / p u b l i c i n t d a P o s i c i o n ( S t r i n g nombre ) { i n t l u g a r = l i s t a . t o L o w e r C a s e ( ) . i n d e x O f ( nombre . t o L o w e r C a s e ( ) ) ; i n t s o b r a n = ( l u g a r >= 0 ) ? ( l u g a r % TAM REG) : 0 ; r e t u r n ( l u g a r >= 0 ) ? ( ( l u g a r s o b r a n ) / TAM REG) +1 : lugar ; }

Supongamos ahora que no queremos al primero que contenga la subcadena, sino uno que est despus de cierta posicin. El algoritmo es prcticamente el e e o a mismo, excepto que usamos otra rma de la funcin indexOf, la que toma en cuenta o

4.2 Implementacin de una base de datos o

117

una posicin inicial a partir de donde buscar. Le damos al mtodo daPosicion otra o e rma que tome en cuenta este parmetro adicional. La programacin se encuentra a o tambin en los listados 4.9 en la pgina opuesta y 4.10. e a

Cdigo 4.10 Mtodo que da el siguiente registro con subcadena o e


126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140:

(Curso)

/ Da e l o r d i n a l que c o r r e s p o n d e a l r e g i s t r o que c o n t i e n e a l a s ub c a d e na , a p a r t i r de l a p o s i c i n dada o @param nombre s u b c a d e n a a b u s c a r @param d e s d e n mero de c a r c t e r a p a r t i r d e l c u a l b u s c a r u a @ r e t u r n e l o r d i n a l d e l r e g i s t r o , o 1 s i no hay / p u b l i c i n t d a P o s i c i o n ( S t r i n g nombre , i n t d e s d e ) { i n t nvoReg = ( d e s d e ) TAM REG ; i n t l u g a r = l i s t a . t o L o w e r C a s e ( ) . i n d e x O f ( nombre . t o L o w e r C a s e ( ) , nvoReg ) ; i n t s o b r a n = l u g a r % TAM REG ; r e t u r n ( l u g a r >= 0 ) ? ( ( l u g a r s o b r a n ) / TAM REG) +1 : lugar ; }

El unico mtodo que nos falta de los que trabajan con un registro particular e es el que arma un registro para mostrarlo. El algoritmo es sumamente sencillo, y lo mostramos en la gura 4.6. Lo unico relevante es preguntar si el registro que nos piden existe o no.

Figura 4.6

Edicin del i-simo registro, si es que existe. o e


3 6 9Existe el registro i? Arma el registro i 9 8 Regresar el i-simo e 3 9 registro 9 7Existe el registro i? D que no existe

La programacin correspondiente se encuentra en el listado 4.11 en la siguiente o pgina. El carcter zt que aparece entre cada dos elementos del listado es un a a tabulador, que lo que hace es alinear bien los campos para listarlos bonito.

118

Manejo de cadenas y expresiones

Cdigo 4.11 Edicin de un registro individual o o


141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152:

(Curso)

/ Arma e l r e g i s t r o p a r a m o s t r a r que s e e n c u e n t r a en l a i s i m a e posicin . o @param i l a p o s i c i n d e l r e g i s t r o o @ r e t u r n e l r e g i s t r o armado o un m e n s a j e de que no e x i s t e / public String armaRegistro ( int i ) { return ( i > 0) ? daNombre ( i ) + "\t" + d a C a r r e r a ( i ) + "\t" + daCuenta ( i ) + "\t" + d a C l a v e ( i ) : "No se encontr al nombre buscado " ; o }

De las funciones ms comunes a hacer con una lista de un curso es listar toda a la lista completa. Sabemos cuntos registros tenemos, todo lo que tenemos que a hacer es recorrer la lista e ir mostrando uno por uno. A esto le llamamos iterar sobre la lista. El algoritmo podr ser el que se ve en la gura 4.7. a

Figura 4.7

Algoritmos para listar el curso.


6 9 9 9 9 9 9 8 6 9 9 9 9 9arma el registro i 9 8

lista curso

9 9 9 9 9 9 7

muestra i-simo registro e pi  1, . . . , numRegsq

9 9 9avanza 9 9 9 7

En Java tenemos varios enunciados compuestos que iteran. El ms general de a ellos es de la forma
6 9 enunciado simple 9 9 8 9 7

Ejecuta enunciado compuesto (mientras se cumpla xcondiciny) 9. . . o 9

x y xenunciado simpley

xenunciado simpley

y su sintaxis es como se muestra en la gura 4.8 en la pgina opuesta. a

4.2 Implementacin de una base de datos o

119

Figura 4.8

Enunciado compuesto while.


Sintaxis: xenunciado compuesto whiley::= while ( xexpresin booleanay ) { o xenunciado simple o compuestoy xenunciado simple o compuestoy ... xenunciado simple o compuestoy } Semantica: Lo primero que hace el programa es evaluar la xexpresin booleanay. Si sta o e se evala a verdadero, entonces se ejecutan los enunciados que estn entre u a las llaves, y regresa a evaluar la xexpresin booleanay. Si se evala a falso, o u brinca todo el bloque y sigue con el enunciado que sigue al while. En cualquier iteracin que utilicemos, y en particular en la que acabamos de o presentar, hay varios aspectos que hay que tener presentes: I. Cul es el estado de las variables involucradas cuando se llega por primera a vez a evaluar la condicin de la iteracin? o o II. Cul es el m a nimo nmero de veces que se va a ejecutar el cuerpo de la u iteracin? o III. Qu es lo que se hace dentro del cuerpo de la iteracin que obliga a la e o iteracin a terminar? o En el caso de la iteracin while, se debe llevar a cabo un proceso de inicialio zacin, que consiste en, de ser necesario declarar y, asignar valores iniciales que o garanticen y dejen claro el estado al llegar a la cabeza de la iteracin. Esta iterao cin puede no ejecutarse, ya que la condicin puede no cumplirse desde la primera o o vez que se intenta iterar; por ultimo, en el cuerpo de la iteracin se debe cambiar o el estado de una o ms de las variables involucradas en la condicin, para que a o haya posibilidad de salir de la iteracin. o Un buen ejemplo del uso de esta iteracin es, por ejemplo, encontrar el l o mite de una sucesin dado un margen de error. Lo que haremos ser calcular sucesivamente o a trminos, hasta que la diferencia entre el ultimo trmino calculado y el actual sea e e menor que una cierta psilon. El algoritmo para ello se encuentra en la gura 4.9 e en la siguiente pgina. a

120
1 , 2n

Manejo de cadenas y expresiones

Figura 4.9

Encontrar el l mite de

dado
6 9 = .001 8 9 7

Calcular l mite de
1 2n

6 9 9 9 9 9Inicializar 9 9 9 9 9 9 9 9 9 8

double f Actl 1{2 double f Ant 0 f Ant f Actl f Actl f Ant{2

e 9 Calcular siguiente trmino 9 9 9 9(mientras |fant factl | ) 9 9


9 9 9 9 9 9 9 7Final

Reporta f Actl

La implementacin de este pequeo mtodo se muestra en el listado 4.12. o n e

Cdigo 4.12 Clculo del l o a mite de una sucesin o


p u b l i c double l i m i t e ( double v a r e p s i l o n ) { / I n i c i a l i z a r / double e p s i l o n = v a r e p s i l o n ; double f A c t l = 1 / 2 ; double f A n t = 1 ; / I t e r a c i n / o w h i l e ( Math . a b s ( f A c t l f A n t ) < e p s i l o n ) fAnt = FActl ; fActl = fActl / 2; } // f i n d e l w h i l e / F i n a l / return f A c t l ; }

Como se puede ver, esta iteracin es ideal cuando no tenemos claro el nmero o u de iteraciones que vamos a llevar a cabo y deseamos tener la posibilidad de no ejecutar el cuerpo ni siquiera una vez. Por ejemplo, si el valor de que nos pasaran como parmetro fuera mayor que 1{2, la iteracin no se llevar a cabo ni una a o a vez. Hay ocasiones en que deseamos que un cierto enunciado se ejecute al menos una vez. Supongamos, por ejemplo, que vamos a sumar nmeros que nos den desde u la consola hasta que nos den un 1. El algoritmo se puede ver en la gura 4.10.

4.2 Implementacin de una base de datos o

121

Figura 4.10

Sumar nmero mientras no me den un u


6 9 9 9Inicializar 9 9 9 9 9 9 8 Sumar nmeros u

1
5

5 Sumar una lista Sumar nmero le u do u 9(mientras nmero de nmeros u 9 9 Leer siguiente nmero u 9 9 $ -1) 9 9 3 9 9 7

suma 0 numro 0

Final

Entregar resultado

Veamos la descripcin de este enunciado compuesto de iteracin en la guo o ra 4.11.

Figura 4.11

Enunciado compuesto do . . . while


Sintaxis: xenunciado compuesto do. . . whiley::= do { xenunciado simple o compuestoy xenunciado simple o compuestoy ... xenunciado simple o compuestoy } while ( xexpresin booleanay ); o Semantica: Lo primero que hace el enunciado al ejecutarse es ejecutar los enunciados que se encuentran entre el do y el while. Es necesario aclarar que estos enunciados no tienen que estar forzosamente entre llaves (ser un bloque) pero las llaves me permiten hacer declaraciones dentro del enunciado, mientras que sin las llaves, como no tengo un bloque, no puedo tener declaraciones locales al bloque. Una vez ejecutado el bloque procede a evaluar la xexpresin o booleanay. Si esta se evala a verdadero, la ejecucin contina en el primer u o u enunciado del bloque; si es falsa, sale de la iteracin y sigue adelante con el o enunciado que sigue al do . . . while. Tambin en esta iteracin tenemos que tener cuidado en inicializar y declarar e o

122

Manejo de cadenas y expresiones

variables necesarias antes de entrar a la iteracin. No podemos declararlas dentro o porque entonces no las conoce en la xexpresin booleanay. Pero como primero o hace y despus pregunta, podemos hacer la inicializacin como parte del bloque. e o Veamos cmo queda el pequeo algoritmo que se muestra en la gura 4.11 en el o n listado 4.13.

Cdigo 4.13 Suma de nmeros le o u dos


public i n t sumaLeidos ( Console cons ) i n t suma = 0 ; i n t numero = 0 ; do { suma += numero ; numero = c o n s . l e e I n t ( ) ; } w h i l e ( numero != 1); r e t u r n suma ; } {

Es claro que lo que se puede hacer con un tipo de iteracin se puede hacer con la o otra. Si queremos que el bloque se ejecute al menos una vez usando un while, lo que hacemos es colocar el bloque inmediatamente antes de entrar a la iteracin. Esto o nos va a repetir el cdigo, pero el resultado de la ejecucin va a ser exactamente o o el mismo. Por otro lado, si queremos usar un do. . . while pero queremos tener la posibilidad de no ejecutar ni una vez, al principio del bloque ponemos una condicional que pruebe la condicin, y como cuerpo de la condicional colocamos el o bloque original. De esta manera si la condicin no se cumple al principio el bloque o no se ejecuta. Esto quiere decir que si un lenguaje de programacin unicamente o cuenta con una de estas dos iteraciones, sigue teniendo todo lo necesario para elaborar mtodos pensados para la otra iteracin. e o Tenemos una tercera iteracin conocida como for, que resulta ser el can o no de las iteraciones, en el sentido de que en un solo enunciado inicializa, evala u una expresin booleana para saber si entra a ejecutar el enunciado compuesto e o incrementa al nal de la ejecucin del enunciado compuesto. Lo veremos cuando o sea propicio su uso. Por lo pronto volveremos a nuestro problema de manejar una pequea base de datos. n La lista del curso debemos mostrarla en algn medio. Para usar dispositivos u de entrada y salida utilizaremos por lo pronto una clase construida especialmente para ello, se llama Consola y se encuentra en el paquete icc1.interfaz. Tenemos que crear un objeto de tipo Consola, y a partir de ese momento usarlo para mostrar y/o recibir informacin del usuario. Veamos los principales mtodos que vamos a o e usar por el momento en la tabla 4.3 en la pgina opuesta. a

4.2 Implementacin de una base de datos o

123

Podemos regresar ahora al problema de listar todo el curso en una pantalla proporcionada por el usuario, usando la clase Consola y el enunciado compuesto while. La programacin se encuentra en el listado 4.14. o

Cdigo 4.14 Mtodo que lista todo el curso o e


153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: / L i s t a t o d o e l c o n t e n i d o de l a b a s e de d a t o s @param c o n s o l a e l f l u j o de s a l i d a donde va a l i s t a r / public void l i s t a C u r s o ( Consola c o n s o l a ) { i n t i =1; w h i l e ( i <= numRegs ) { consola . imprimeln ( armaRegistro ( i ) ) ; i ++; } // w h i l e } // l i s t a C u r s o

(Curso)

Cuadro 4.3

Mtodos de la clase Consola. e


Firma Constructores Consola() Consola(String) Consola(int, int) Consola(int, int, String) Descripcin o Construyen una pantalla para interaccionar con el programa. El valor por omisin (si no tiene argumeno tos enteros) es de 400600 en pixeles, o del tamao n que se le indique si se dan argumentos enteros. Si se le proporciona una cadena, esta cadena aparece como t tulo de la pantalla. La pantalla permanece hasta que se destruya con el botn correspondiente. Ningn otro o u proceso, ms que el programa, dar atencin mientras a a o la consola est activa. Cuidado porque lo que se tee clee se guarda para cuando desparezca la consola! El primero coloca la cadena en la pantalla que corresponde al objeto Consola (lo escribe). El segundo, una vez que termin de escribir la cadena, manda o un carcter de n de l a nea. En el primero, una segunda escritura se har a partir de donde termin la pria o mera, mientras que con la segunda rma, la siguiente escritura se har empezando rengln nuevo. a o

Escritura void imprime(String) void imprimeln(String)

124

Manejo de cadenas y expresiones

Cuadro 4.3

Mtodos de la clase Consola. e


Firma

(contina) u

Descripcin o Hay algunos carcteres que toman un signicado esa pecial. Ya vimos uno de ellos, el tabulador. Veamos algunos ms que pueden ser utiles. a zn l nueva nea z zz Una sola z z comillas dentro del texto z Apstrofe dentro del texto o

Lectura Abre una pequea pantallita en la que pide una caden String leeString() o String leeString(String) na. Se selecciona con el ratn la pantallita y se teclea la cadena, terminando con Enter . En la segunda rma se coloca el mensaje como t tulo de la pantallita. Esta desaparece cuando se termina de teclear la cadena. El proceso de agregar un estudiante es bastante simple, ya que en las especicaciones del problema no se menciona que la lista se tenga que mantener en orden. Simplemente armamos el registro y lo agregamos al nal. El unico problema es garantizar el tamao de los campos, pues el usuario puede no tomarlo en cuenta. n El algoritmo se muestra en la gura 4.12.

Figura 4.12

Algoritmo para agregar un estudiante.


6 9 9 9 9 9Ajusta tamao de nombre 9 n 9 9 9 9Ajusta tamao de clave 9 n 8

Agrega registro

9 9 9Ajusta tamao de nmero de cuenta n u 9 9 9 9 9Agrega la cadena completa a la lista 9 9 9 7

Ajusta tamao de carrera n

Para ajustar los tamaos de las cadenas, primero les agregamos al nal un n montn de blancos, para luego truncarla en el tamao que debe tener. La prograo n macin se encuentra en el listado 4.15 en la pgina opuesta. o a

4.2 Implementacin de una base de datos o

125 (Curso)

Cdigo 4.15 Mtodo que agrega un estudiante a la lista o e


164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185:

/ Agrega un e s t u d i a n t e a l a b a s e de d a t o s . @param nombre @param c u e n t a @param c l v e @param c a r r e / p u b l i c v o i d a g r e g a E s t ( S t r i n g nombre , S t r i n g c u e n t a , String clve , String carre ) { String blancos = " "; S t r i n g defNmbre = nombre . t r i m ( ) . c o n c a t ( b l a n c o s ) . s u b s t r i n g ( 0 ,TAM NMBRE ) ; S t r i n g defCuenta = cuenta . trim ( ) . concat ( blancos ) . s u b s t r i n g ( 0 ,TAM CTA ) ; String defClave = c l v e . trim ( ) . concat ( blancos ) . s u b s t r i n g ( 0 , TAM CLVE ) ; String defCarre = c a r r e . t r i m ( ) . c o n c a t ( b l a n c o s ) . s u b s t r i n g ( 0 ,TAM CARRE ) ; S t r i n g e s t u d n t e = defNmbre + d e f C a r r e + d e f C u e n t a + d e f C l a v e ; l i s t a = l i s t a . concat ( estudnte ) ; numRegs++; }

Figura 4.13

Posibles situaciones para eliminar a un registro.

Caso 1

loooooooooooooooooooooooooooooooooooooooooooooomoooooooooooooooooooooooooooooooooooooooooooooon

loooooooooooooooooomoooooooooooooooooon

Caso 2

looooooooooooooooooooooooomooooooooooooooooooooooooon

loooooooooooooooooooooooooooooooooooooooooooooomoooooooooooooooooooooooooooooooooooooooooooooon

Caso 3

126

Manejo de cadenas y expresiones

Para eliminar a un estudiante de la lista, usando subcadenas, no es tan fcil. a Si el estudiante que queremos quitar es el primero, la lista se convierte en lo que queda de la cadena al quitar el primero; si es el ultimo el que deseamos eliminar, la lista nueva consiste de la parte hasta donde empieza el ultimo; pero en cambio, si al que deseamos eliminar se encuentra en una posicin intermedia, tenemos que o partir la lista en tres pedazos: lo que va antes, el registro que deseamos eliminar y lo que va despus ver gura 4.13 en la pgina anterior. De la gura podemos e a ver que tenemos que hacer un anlisis por casos. El algoritmo se muestra en la a gura 4.14.

Figura 4.14

Algoritmo para eliminar a un estudiante de la lista.


6 3 9Es el primero Se queda del segundo al nal 9 9 9 9 9 9 9 8 3

Eliminar el Es el ultimo registro i 9 9 9

Se queda del primero al penltimo u

9 9 3 9 9 9 7Est en medio Juntar del 1 al i-1 y del i+1 al nal a

Conocemos de cual de estas tres situaciones se trata (no hay otra posibilidad) dependiendo de la posicin del registro: o Es el primero si i vale 1. Es el ultimo si i vale NUM REGS. Est en medio si 1 i NUM REGS. a

Para este tipo de enunciado es conveniente que introduzcamos el xenunciado compuesto condicionaly cuya sintaxis y semntica se encuentra en la gura 4.15 en a la pgina opuesta. Con esto podemos ya pasar a programar el mtodo que elimina a e al i-simo registro de nuestra lista, en el listado 4.16 en la pgina opuesta. e a El unico mtodo que nos falta, para terminar esta seccin, es el que arma una e o lista con todos los registros que contienen una subcadena. En este caso todo lo que tenemos que hacer es, mientras encontremos la subcadena buscada, seguimos buscando, pero a partir del siguiente registro. El algoritmos lo podemos ver en la gura 4.16 en la pgina 128. a

4.2 Implementacin de una base de datos o

127

Figura 4.15

Enunciado compuesto condicional


Sintaxis: xenunciado compuesto condicionaly::= if ( xexpresin booleanay ) { o xenunciado simple o compuestoy ... xenunciado simple o compuestoy } else { xenunciado simple o compuestoy ... xenunciado simple o compuestoy } Semantica: Lo primero que hace el programa es evaluar la xexpresin booleanay. Si o sta se evala a verdadero, entonces se ejecutan los enunciados que estn e u a entre las primeras llaves y si se evala a falso se ejecutan los enunciados u que estn a continuacin del else. Pudiramos en ambos casos tener un a o e unico enunciado, en cuyo caso se pueden eliminar las llaves correspondien tes. Tambin podemos tener que no aparezca una clusula else, en cuyo e a caso si la expresin booleana se evala a falso, simplemente se contina la o u u ejecucin con el enunciado que sigue al if. o

Cdigo 4.16 Mtodo que elimina al registro i o e


186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197:

(Curso)1/2

/ Q u i t a a l e s t u d i a n t e que s e e n c u e n t r a en e l p o s i c i n i o D i s t i n g u e e n t r e q u i t a r a l p r i m e r o , a l u l t i m o o a l g u n o de en medio . @param i p o s i c i n d e l e s t u d i a n t e en l a l i s t a o / public void q u i t a E s t ( i n t i ) { int anteriores ; int posteriores ; a n t e r i o r e s = ( i 1 ) TAM REG ; // t e r m i n a n en p o s t e r i o r e s = i TAM REG ; // e m p i e z a n en

128

Manejo de cadenas y expresiones

Cdigo 4.16 Mtodo que elimina al registro i. o e


198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210:

(Curso)2/2

boolean noHay = ( i <= 0 | | i > numRegs ) ; // p o s i c i n i n v l i d a o a i f ( noHay ) { return ; } boolean e l P r i m e r o = ( i == 1 ) ; boolean e l U l t i m o = ( i == numRegs ) ; i f ( elPrimero ) l i s t a = l i s t a . substring ( posteriores ); else i f ( elUltimo ) l i s t a = l i s t a . substring (0 , a n t e r i o r e s ) ; else l i s t a = l i s t a . s ub s t r i n g (0 , a n t e r i o r e s ) + l i s t a . substring ( posteriores ); numRegs ;

Figura 4.16

Mtodo que encuentra TODOS los que contienen a una subcadena. e


6 9 9 9 9 9 9 9 9 9 9 9 9P rincipio 9 9 9 9 9 9 9 9 9 9 9 9 9 8 6 9 9 9 9 9donde 9 9 9 8

Primer registro que caza.

9cazaCon 9 9 9 9 9 9 9 7 6 9 9 9 9 9cazaCon 9 9 9 8 9 9 9 9 9donde 9 9 9 7

(cadena vac a)

Arma cadena

9 9 9 9 9 9 9 9 9 9 Arma siguiente 9 9 9 9(mientras hayas 9 9 9 9 9 encontrado) 9 9 9 9 9 9 7

cazaCon armaRegistropdondeq

Siguiente registro que caza

Lo unico importante en este caso es darnos cuenta que ya tenemos dos ver siones que nos dan la posicin de una subcadena, as que la programacin es o o prcticamente directa. La podemos ver en el listado 4.17 en la pgina opuesta. a a

4.3 Una clase Menu

129 (Curso)

Cdigo 4.17 Mtodo que lista a los que cazan con . . . o e


211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225:

/ C o n s t r u y e una l i s t a p a r a m o s t r a r con t o d o s l o s r e g i s t r o s que t i e n e n como s u b c a d e n a a l p a r m e t r o . a @param s u b c a d Lo que s e b u s c a en cada r e g i s t r o @ r e t u r n Una c a d e n a que c o n t i e n e a l o s r e g i s t r o s que c a z a n / p u b l i c S t r i n g losQueCazanCon ( S t r i n g s u b c a d ) { S t r i n g cazanCon = "" ; i n t donde = d a P o s i c i o n ( s u b c a d ) ; w h i l e ( donde != 1) { cazanCon = cazanCon . c o n c a t ( a r m a R e g i s t r o ( donde)+"\n" ) ; donde = d a P o s i c i o n ( subcad , donde ) ; } // w h i l e e n c u e n t r e s r e t u r n cazanCon ; }

Podr amos, para probar que nuestros mtodos funcionan, programar el mtodo e e main, pero eso es aburrido. Mejor hagamos una clase que maneje un men para u esta base de datos en la siguiente seccin. o

4.3 Una clase Menu


Contar con una base de datos que tiene las funcionalidades usuales no es suciente. Debemos contar con algn medio de comunicacin con la misma, como u o pudiera ser un lenguaje de consulta (query language) o, simplemente, un men que u nos d acceso a las distintas operaciones que podemos realizar sobre la base de e datos. Construiremos un men para tal efecto. u El funcionamiento de un men queda esquematizado en el diagrama de la u gura 4.17 en la siguiente pgina. Bsicamente, mientras el usuario lo desee, dea a bemos mostrarle una lista de las opciones que tiene, dejarle que elija una de ellas y proceder a hacer lo que corresponde a la opcin. o

130

Manejo de cadenas y expresiones

Figura 4.17

Men para uso de la clase Curso. u


6 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 Maneja Men 9 u 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 7 6 u 9Muestra el men 9 9 9 9Pide opcin al usuario o 9 9 3 9 9 9opcin == Termina Salir 9 o 9 9 9 9 9 9 9 5 9 9 9 9 9opcin == Agrega Pide registro o 9 9 9 Agrega registro 9 9 9 9 9 9 9 6 9 9 9 9Pregunta a quin e 9 9 9 9 9 9 9 9Bscalo 9 9 u 9 9 3 9 8 9 9 9opcin == Quita Encontrado Qu talo 9 o 9 9 9 9 9 9 9 9 9 9 9 3 8 9 9 7Encontrado Reporta No encontrado (mientras desee 9 9 9 el usuario) 9 9 6 9 9 9 9Pregunta a quin e 9 9 9 9 9 9 9 9Bscalo 9 9 u 9 9 9 3 8 9 9 9opcin == Busca Encontrado Reprtalo o 9 o 9 9 9 9 9 9 9 9 9 9 9 3 9 9 9 9 9 7Encontrado Reporta No encontrado 9 9 9 9 9 9 9 9 3 9 9 9opcin == Lista 9 o Lista todo el curso 9 9 9 9 9 9 9 5 9 9 9 opcin == Todos los o Pregunta subcadena 9 9 9 7

Men u para Cursos

que cazan

Arma la lista

El men que requerimos corresponde a una clase que va a hacer uso de la clase u Curso que ya diseamos. Por lo tanto, podemos empezar ya su codicacin. El n o hacerlo en una clase aparte nos permitir en un futuro, tal vez manejar nuestra a, base de datos con un lenguaje de consulta o hasta, posiblemente, con un lenguaje de programacin. o

4.3 Una clase Menu

131

Para construir el cdigo correspondiente al del diagrama de la Figura 4.17 o tenemos en Java una condicional calculada, en la cual, como su nombre lo indica, elige una opcin entre varias dependiendo del valor de la variable selectora. La o sintaxis de este enunciado se encuentra en la Figura 4.18.

Figura 4.18

Enunciado switch.
Sintaxis: switch( xexpresiny ) { o case xvalor1 y: xlista de enunciados simples o compuestosy; case xvalor2 y: xlista de enunciados simples o compuestosy; case . . . xlista de enunciados simples o compuestosy; default: xlista de enunciados simples o compuestosy; } Semantica: Los valores valor1 , . . . deben ser del mismo tipo que la expresin, a la o que llamamos la selectora del switch y deben ser constantes de ese tipo. El enunciado switch elige un punto de entrada al arreglo de enunciados de la lista. Evala la expresin y va comparando contra los valores que aparecen u o frente a la palabra case. El primero que coincide, a partir de ese punto ejecuta todos los enunciados desde ah hasta que se encuentre un enunciado break, o el nal del switch, lo que suceda antes. Se acostumbra colocar un enunciado break al nal de cada opcin para que unicamente se ejecuten los o enunciados relativos a esa opcin. El switch tiene una clusula de escape, o a que puede o no aparecer. Si aparece y la expresin no toma ninguno de o los valores expl citamente listados, se ejecuta el bloque correspondiente a default. Si no hay clusula de escape y el valor de la expresin no caza con a o ninguno de los valores en los cases, entonces el programa abortar dando a un error de ejecucin. o El comando break que mencionamos en la gura 4.18 es muy sencillo, y lo unico que hace es sacar el hilo de ejecucin del programa hacia afuera del switch. Su o sintaxis y semntica se encuentran denidas con ms precisin en la gura 4.19. a a o Veamos algunos ejemplos de la forma que podr tomar distintos switches. an

132

Manejo de cadenas y expresiones

Figura 4.19

Enunciado break.
Sintaxis: xenunciado breaky ::= break Semantica: Hace que el hilo de ejecucin del programa no siga con el siguiente enuno ciado, sino que salga del enunciado compuesto en el que est, en este caso a el switch.
boolean e s t a = i != 1; switch ( e s t a ) { case f a l s e : c o n s o l a . i m p r i m e l n ( "NO est " ) ; a break ; case t r u e : c o n s o l a . i m p r i m e l n ( "SI est " ) ; a }

Como las unicas dos posibilidades para el selector del switch, una expresin o booleana, es falso o verdadero, no hay necesidad de poner una clusula de escape, a ya que no podr tomar ningn otro valor. Este ejemplo es realmente un enunciado a u if disfrazado, ya que si la expresin se evala a verdadero (true) se ejecuta lo que o u corresponde al caso etiquetado con true, mientras que si se evala a falso, se ejecuta u el caso etiquetado con false. Programado con ifs queda de la siguiente manera:
boolean e s t a = i != 1; i f ( esta ) c o n s o l a . i m p r i m e l n ( "SI est " ) ; a else c o n s o l a . i m p r i m e l n ( "SI est " ) ; a

Otro ejemplo ms apropiado, ya que tiene ms de dos opciones, es el siguiente: a a supongamos que tenemos una clave que nos dice el estado civil de una persona y que queremos convertirlo a la cadena correspondiente. Las claves son: s: soltero c: casado d: divorciado v: viudo u: unin libre o En una variable de tipo carcter tenemos una de estas opciones, y deberemos a

4.3 Una clase Menu

133

proporcionar una cadena con el texto adecuado. La programacin quedar algo o a parecido a lo que se observa en el listado 4.18.

Cdigo 4.18 Ejemplo de identicacin del estado civil de un individuo o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: char e s t a d o = d a E s t a d o ( ) ; / d a E s t a d o ( ) a s i g n a v a l o r a e s t a v a r i a b l e / String texto ; switch ( estado ) { / E l e g i m o s de a c u e r d o a l c o n t e n i d o de estado / case s : t e x t o = " soltero " ; break ; case c : t e x t o = " casado " ; break ; case d : t e x t o = " divorciado " ; break ; case u : t e x t o = " uni n libre " ; o break ; default : t e x t o = "no definido " ; break ; } // En l a v a r i a b l e texto queda e l m e n s a j e que c o r r e s p o n d e

En general, la estructura switch lo que hace es ir comparando el valor del selector con cada uno de las constantes que se encuentran alrededor de la palabra case. Ms de una etiqueta, se pueden aparecer frente a un bloque de cdigo, para a o referirse a que esa es la accin a realizarse en ms de un caso: o a

case A : case B : case C : r e t u r n " Primeros tres casos

Regresamos ahora a la programacin de nuestro problema. La parte correso pondiente al manejo del men se da en un mtodo, para no atiborrar al mtodo u e e principal (main) de la clase con cdigo. La programacin queda como se muestra o o en el listado 4.19 en la siguiente pgina. a

134

Manejo de cadenas y expresiones

Cdigo 4.19 Encabezado de la clase Menu y el mtodo daMenu o e

(MenuCurso) 1/2

1: c l a s s MenuCurso { 2: / 3: Maneja e l men p a r a e l a c c e s o y m a n i p u l a c i n de l a b a s e de u o 4: datos . 5: @param c o n s D i s p o s i t i v o en e l que s e va a i n t e r a c c i o n a r 6: @param miCurso Base de d a t o s con l a que s e va a t r a b a j a r 7: @ r e t u r n l a o p c i n s e l e c c i o n a d a , d e s p u s de h a b e r l a e j e c u t a d o o e 8: / 9: p u b l i c i n t daMenu ( C o n s o l a cons , C u r s o miCurso ) { 10: S t r i n g nombre , c u e n t a , c a r r e r a , c l a v e ; 11: S t r i n g subcad ; 12: String registros ; 13: i n t donde ; 14: S t r i n g menu = "(0)\ tTermina \n"+ 15: "(1)\ tAgrega \n"+ 16: "(2)\ tQuita \n"+ 17: "(3)\ tBusca \n"+ 18: "(4)\ tLista todos \n"+ 19: "(5)\ tLista subconjunto que contenga a ...." ; 20: c o n s . i m p r i m e l n ( menu ) ; 21: int opcion ; 22: S t r i n g s o p c i o n = c o n s . l e e S t r i n g ( " Elige una opci n --> " ) ; o 23: o p c i o n = " 012345 " . i n d e x O f ( s o p c i o n ) ; 24: switch ( opcion ) { 25: case 0 : // S a l i r 26: c o n s . i m p r i m e l n ( " Espero haberte servido .\n"+ 27: " Hasta pronto ..." ) ; 28: r e t u r n 1; 29: case 1 : // Agrega E s t u d i a n t e 30: nombre = pideNombre ( c o n s ) ; 31: cuenta = pideCuenta ( cons ) ; 32: c a r r e r a = p i d e C a r r e r a ( cons ) ; 33: c l a v e = pideClave ( cons ) ; 34: miCurso . a g r e g a E s t ( nombre , c u e n t a , c l a v e , c a r r e r a ) ; 35: return 1; 36: case 2 : // Q u i t a e s t u d i a n t e 37: nombre = pideNombre ( c o n s ) ; 38: donde = miCurso . d a P o s i c i o n ( nombre ) ; 39: i f ( donde != 1) { 40: nombre = miCurso . daNombre ( donde ) ; 41: miCurso . q u i t a E s t ( donde ) ; 42: c o n s . i m p r i m e l n ( "El estudiante :\n\t" 43: + nombre 44: + "\n Ha sido eliminado " ) ; 45: }

4.3 Una clase Menu

135 (MenuCurso) 2/2

Cdigo 4.19 Encabezado de la clase bfseries Menu y el mtodo daMenu o e


46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82:

else { r e p o r t a N o ( cons , nombre ) ; } return 2; case 3 : // Busca s u b c a d e n a s u b c a d = c o n s . l e e S t r i n g ( "Dame la subcadena a " + " buscar : " ) ; donde = miCurso . d a P o s i c i o n ( s u b c a d ) ; i f ( donde != 1) { c o n s . i m p r i m e l n ( miCurso . a r m a R e g i s t r o ( donde ) ) ; } else { r e p o r t a N o ( cons , s u b c a d ) ; } return 3; case 4 : // L i s t a t o d o s miCurso . l i s t a C u r s o ( c o n s ) ; return 4; case 5 : // L i s t a con c r i t e r i o s u b c a d = c o n s . l e e S t r i n g ( "Da la subcadena que " + " quieres contengan los " + " registros :" ) ; r e g i s t r o s = miCurso . losQueCazanCon ( s u b c a d ) ; i f ( r e g i s t r o s . e q u a l s ( "" ) ) { c o n s . i m p r i m e l n ( "No hubo ning n registro con " + u "este criterio " ) ; } else { cons . imprimeln ( r e g i s t r o s ) ; } return 5; d e f a u l t : // E r r o r , v u e l v e a p e d i r c o n s . i m p r i m e l n ( "No diste una opci n v lida .\n" + o a "Por favor vuelve a elegir ." ) ; return 0; } }

Para el men construimos una cadena que se muestra separada por renglones, u como se observa en las l neas68 a 73, y le pedimos al usuario que elija un nmeu ro de opcin, en la l o nea 76. En esta l nea aparece algo nuevo, ya que estamos interaccionando con el usuario. La manera de hacerlo es a travs de una consola e (por eso aparece un objeto de esta clase como parmetro). Por lo pronto vamos a a leer cadenas, y tenemos la opcin de pedirle o no que escriba una cadena como o

136

Manejo de cadenas y expresiones

t tulo de la pequea pantalla en la que solicita el dato. Las dos posible rmas del n mtodo de lectura de cadenas de la clase consola son e
String leeString () String leeString ( String )

En el primer caso simplemente aparecer una pantallita en la que, una vez a seleccionada, se deber teclear la cadena que se desea proporcionar (recurdese a e que podemos tener una cadena de un solo carcter) terminndola con Enter . La a a invocacin de este mtodo, desde un objeto del tipo Consola puede aparecer en o e cualquier lugar donde pudiera aparecer una expresin de cadenas. o Estamos asumiendo que el usuario nos tecle un d o gito. Procedemos a ver exactamente cul de ellos fue, buscndolo en una cadena que contiene todas las a a opciones (si tuviramos ms de 10 opciones tendr e a amos que asignar letras para las siguientes, y as poder seguir utilizando este mtodo). Esto lo hacemos con el e mtodo ya conocido por nosotros, indexOf l e nea 77 del listado 4.19 en la pgina a 134. Una vez determinada la opcin que solicit el usuario, deberemos escoger entre o o un conjunto de opciones, numeradas del 0 al 5. Para ello vamos a utilizar una condicional especial, el switch, que se muestra en la gura 4.18 en la pgina 131. a En el caso de nuestro problema, cada bloque del switch termina con un return, ya que se es el objetivo del proceso, avisar cul es la ultima opcin que se eligi. e a o o Adicionalmente, se ejecuta lo correspondiente a cada opcin. Las revisaremos una o por una.

4.3.1.

Salir
En esta opcin se emite un mensaje para el usuario, avisndole del nal del o a proceso, y se regresa un valor de -1 para que el programa principal ya no siga mostrando el men y termine. u

4.3.2.

Agrega estudiante
Esta opcin tiene que funcionar como una interface entre el mtodo de la o e base de datos y el usuario, para agregar de manera correcta a un estudiante. Por ello, deberemos primero llenar cada uno de los campos del registro (ser absurdo a pedirle al usuario que conociera cmo est implementada nuestra base de datos). o a

4.3 Una clase Menu

137

Por ello se procede a solicitarle al usuario cada uno de los campos involucrados. Elegimos hacer un mtodo distinto para cada campo, para poder indicarle al e usuario que tipo de cadena estamos esperando. La codicacin de cada uno de o estos mtodos se encuentran en el listado 4.20. Algunos de estos mtodos los e e volveremos a usar, ya que nos proporcionan, por parte del usuario, informacin. o Cada uno de estos mtodos simplemente le dice al usuario qu es lo que espera y e e recibe una cadena, que, idealmente, deber ser lo que el usuario espera. a

Cdigo 4.20 Mtodos para agregar un estudiante a la base de datos. o e


83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112:

(MenuCurso)1/2

/ P i d e en e l i n p u t s t r e a m e l nombre d e l alumno . @param c o n s e l i n p u t s t r e a m @ r e t u r n E l nombre l e d o / p r i v a t e S t r i n g pideNombre ( C o n s o l a c o n s ) { S t r i n g nombre = c o n s . l e e S t r i n g ( "Dame el nombre del " + " estudiante , empezando por apellido paterno :" ) ; r e t u r n nombre ; } / P i d e en e l i n p u t s t r e a m l a c a r r e r a d e l alumno . @param c o n s e l i n p u t s t r e a m @ r e t u r n La c a r r e r a l e d a / private S t r i n g p i d e C a r r e r a ( Consola cons ) { S t r i n g c a r r e r a = c o n s . l e e S t r i n g ( "Dame una de las " + " carreras :\ tMate \ tComp \ tFisi \ tActu \ tBiol :" ) ; return c a r r e r a ; } / P i d e en e l i n p u t s t r e a m l a c l a v e de a c c e s o d e l alumno . @param c o n s e l i n p u t s t r e a m @ r e t u r n La c l a v e de a c c e s o l e d a / private S t r i n g pideClave ( Consola cons ) { S t r i n g c l a v e = c o n s . l e e S t r i n g ( "Dame la clave de acceso del " + " estudiante :" ) ; return c l a v e ; }

138

Manejo de cadenas y expresiones

Cdigo 4.20 Mtodos para agregar un estudiante a la base de datos o e


113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123:

(MenuCurso) 2/2

/ P i d e en e l i n p u t s t r e a m e l numero de c u e n t a d e l alumno . @param c o n s e l I n p u t S t r e a m @ r e t u r n E l numero de c u e n t a l e d o / private S t r i n g pideCuenta ( Consola cons ) { S t r i n g cuenta = c o n s . l e e S t r i n g ( "Dame el numero de cuenta del " + " estudiante , de 9 d gitos " ) ; return cuenta ; }

4.3.3.

Quita estudiante

En esta opcin, tambin, la parte interesante la hace el mtodo de la base de o e e datos, que ya revisamos. Debemos notar, sin embargo, que es ac donde vamos a a vericar que las operaciones sobre la base de datos se hayan realizado de manera adecuada, preguntando siempre por el resultado de las mismas. El unico mtodo e que no hemos visto es uno realmente sencillo que reporta que el estudiante solicitado no se encuentra en la base de datos. La programacin de este mtodo se o e encuentra en el listado 4.21.

Cdigo 4.21 Mtodo que reporta estudiante inexistente o e


124: 125: 126: 127: 128: 129: 130: 131: 132: 133:

(MenuCurso)

/ R e p o r t a que e l e s t u d i a n t e b u s c a d o no f u e l o c a l i z a d o . @param c o n s f l u j o de s a l i d a en e l que s e e s c r i b e @param nombre s u b c a d e n a que no f u e l o c a l i z a d a / p r i v a t e v o i d r e p o r t a N o ( C o n s o l a cons , S t r i n g nombre ) { c o n s . i m p r i m e l n ( "El estudiante :\n\t" + nombre + "\n No esta en el grupo " ) ; }

4.3 Una clase Menu

139

4.3.4.

Busca estudiante
Tambin realiza su tarea usando mtodos que ya explicamos. Al igual que las e e otras opciones, verica que las operaciones sobre la base de datos se realicen de manera adecuada.

4.3.5.

Lista todo el curso


Simplemente invoca al mtodo correspondiente de la base de datos. e

4.3.6.

Lista los que cumplan con algn criterio u


Este mtodo verica si algn estudiante cumpli o no con el criterio solicitado, e u o preguntando si la cadena resultante tiene algo o no. Noten que tiene que usar el mtodo equals(String), ya que de otra manera no estar comparando contenidos e a de cadenas y nos dir siempre que no son iguales. a

4.3.7.

Valor por omisin o


En este caso, regresaremos un valor que permita al usuario saber que no dio una opcin correcta y que debe volver a elegir. o

4.3.8.

Mtodo principal de MenuCurso e


En este mtodo es en el que tenemos que declarar nuestros objetos, tanto la e base de datos como el men. Una vez hecho esto, simplemente entraremos en una u iteracin mostrando el men y recibiendo la opcin, mientras el usuario no decida o u o terminar. La programacin de este mtodo se encuentra en el listado 4.22 en la o e siguiente pgina. a Lo unico interesante del mtodo en el listado 4.22 en la siguiente pgina es e a que el enunciado del while es un enunciado vac ya que todo se hace cuando se o, invoca al men para que diga si ya le pidieron que salga o no. u

140

Manejo de cadenas y expresiones

Cdigo 4.22 Mtodo principal de la clase MenuCurso o e


134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152:

(MenuCurso)

p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { int opcion ; C o n s o l a c o n s o l a = new C o n s o l a ( ) ; C u r s o miCurso = new C u r s o ( "7001" , // C u r s o " Aguilar Sol s Aries Olaf " + "Mate" + " 400001528 " + " aaguilar " + "Cruz Cruz Gil No e " + "Comp" + " 098034011 " + " ncruz " + " Garc a Villafuerte Israel " + "Mate" + " 098159820 " + " igarcia " + " Hubard Escalera Alfredo " + "Comp" + " 099586197 " + " ahubard " + " Tapia V zquez Rogelio a " + "Comp" + " 098162075 " + " rtapia " , // l i s t a 5); MenuCurso miMenu = new MenuCurso ( ) ; w h i l e ( ( o p c i o n = miMenu . daMenu ( c o n s o l a , miCurso ) ) != 1); }

Con esto damos por terminada nuestra primera aproximacin a una base de o datos. En lo que sigue haremos ms eciente y exible esta implementacin, busa o cando que se acerque ms a lo que es una verdadera base de datos. a

Datos estructurados

La implementacin que dimos en el cap o tulo anterior a nuestra base de datos es demasiado alejada de como abstraemos la lista del grupo. Realmente, cuando pensamos en una lista es una cierta coleccin de registros, donde cada registro o tiene uno o ms campos. Tratemos de acercarnos un poco ms a esta abstraccin. a a o El tipo de coleccin que usaremos en esta ocasin es una lista. La denicin o o o de una lista es la siguiente:

Denicin 5.6 Una lista es: o


(a) La lista vac aquella que no tiene ningn elemento; o bien a, u (b) el primer elemento de la lista, seguido de una lista. Por ejemplo, si una lista nada ms tiene un elemento, sta consiste del primer a e elemento de una lista, seguido de una lista vac a. Toda lista es una referencia a objetos de cierto tipo que contienen determinada informacin y donde al menos uno de sus campos es una referencia, para acomodar o all a la lista que le sigue. En Java, si una lista es la lista vac tendr el valor de null, a a que corresponde a una referencia nula. Generalmente representamos las listas como se muestra en la gura 5.1 en la siguiente pgina, con la letra r representando a un campo para guardar all una referencia, y el s mbolo representando que no sigue nadie (una referencia nula). Como la denicin de la lista es recursiva, debemos siempre tener anclado o

142

Datos estructurados

al primer elemento de la lista, la cabeza de la misma, como se puede ver en la gura 5.1.

Figura 5.1

Ilustracin de una lista o


r cabeza: al primero de la lista Inf o r Inf o r Inf o r Inf o r

Otros nombres que se usan en la literatura sinnimos de referencia son liga, o 1 apuntador, cadena .

5.1 La clase para cada registro


Como en el cap tulo anterior, queremos que cada estudiante de la lista tenga un campo para nombre, cuenta, carrera y clave, pero en esta ocasin lo haremos o independiente cada uno. Adems requerimos el campo donde se guarde la referena cia al siguiente de la lista. Esta referencia es una referencia a objetos de la misma clase que los registros: es autoreferida. Entonces, la clase que vamos a tener para nuestros registros la llamaremos Estudiante. Veamos por lo pronto los atributos (datos) que tiene esta clase en el listado 5.1.

Cdigo 5.1 Atributos de la clase Estudiante o


1: import i c c 1 . i n t e r f a z . C o n s o l a ; 2: / 3: Base de d a t o s , a b a s e de l i s t a s de r e g i s t r o s , que emula l a 4: l i s t a de un c u r s o de l i c e n c i a t u r a . T i e n e l a s o p c i o n e s 5: n o r m a l e s de una b a s e de d a t o s y f u n c i o n a m e d i a n t e un Men u 6: / 7: c l a s s E s t u d i a n t e { 8: p r i v a t e S t r i n g nombre , c l a v e , c a r r e r a , c u e n t a ; 9: private Estudiante s i g u i e n t e ; 10: s t a t i c p u b l i c f i n a l i n t NOMBRE = 1 ; 11: s t a t i c p u b l i c f i n a l i n t CUENTA = 2 ; 12: s t a t i c p u b l i c f i n a l i n t CARRERA = 3 ; 13: s t a t i c p u b l i c f i n a l i n t CLAVE = 4 ;
1

Del ingls, chain. e

5.1 La clase para cada registro

143

Combinamos en una sola declaracin los 4 campos (atributos) de informacin o o que tiene el registro, ya que todos son del mismo tipo; el atributo siguiente es el que es una referencia al siguiente de la lista (a la lista que le sigue). Deben observar que este campo es de tipo Estudiante, que, como todas las variables del tipo de alguna clase, constituyen referencias. Todas las constantes lo son de la clase (static nal). Eso quiere decir que podrn a ser accesibles desde cualquiera de los objetos, y slo existe una copia, la de la clase. o Debemos tener un constructor que inicialice a un objeto de esta clase con las cadenas adecuadas. Como tenemos al menos un constructor, si queremos uno sin parmetros tambin lo tenemos que programar nosotros. Los constructores se a e encuentran en el listado 5.2.

Cdigo 5.2 Constructores para la clase Estudiante o


14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36:

(Estudiante)

/ Constructor s i n parmetros a / public Estudiante () { nombre = c a r r e r a = c l a v e = c u e n t a = n u l l ; } / C o n s t r u c t o r a p a r t i r de d a t o s de un e s t u d i a n t e . l o s campos v i e n e n s e p a r a d o s e n t r e s p o r comas , m i e n t r a s que l o s r e g i s t r o s v i e n e n s e p a r a d o s e n t r e s p o r punto y coma . @param S t r i n g , S t r i n g , S t r i n g , S t r i n g l o s v a l o r e s p a r a cada uno de l o s campos que s e van a l l e n a r . @ r e t u r n E s t u d i a n t e una r e f e r e n c i a a una l i s t a / p u b l i c E s t u d i a n t e ( S t r i n g nmbre , S t r i n g c nt a , String clve , String crrera ) { nombre = nmbre . t r i m ( ) ; cuenta = cnta . trim ( ) ; clave = clve . trim ( ) ; carrera = c r r e r a . trim ( ) ; siguiente = null ; }

Una vez que tenemos los constructores, tenemos que proveer, para cada campo al que queramos que se tenga acceso, un mtodo de consulta y uno de actualie zacin. La programacin de estos mtodos se encuentra en el listado 5.3 en la o o e siguiente pgina. a

144

Datos estructurados 1/2

Cdigo 5.3 Mtodos de acceso y actualizacin de la clase Estudiante o e o


37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: / R e g r e s a e l c o n t e n i d o d e l campo nombre . @return String / p u b l i c S t r i n g getNombre ( ) { r e t u r n nombre ; } / A c t u a l i z a e l campo nombre . @param S t r i n g e l v a l o r que s e va a a s i g n a r . / p u b l i c v o i d setNombre ( S t r i n g nombre ) { t h i s . nombre = nombre ; } / R e g r e s a e l c o n t e n i d o d e l campo c a r r e r a . @return String / public String getCarrera () { return c a r r e r a ; } / A c t u a l i z a e l campo c a r r e r a . @param S t r i n g e l v a l o r que s e va a a s i g n a r . / public void s e t C a r r e r a ( S t r i n g c a r r e ) { carrera = carre ; } / R e g r e s a e l c o n t e n i d o d e l campo c u e n t a . @return String / public S t r i n g getCuenta () { return cuenta ; } / A c t u a l i z a e l campo c u e n t a . @param S t r i n g e l v a l o r que s e va a a s i g n a r . / public void setCuenta ( S t r i n g cnta ) { cuenta = cnta ; }

5.1 La clase para cada registro

145
2/2

Cdigo 5.3 Mtodos de acceso y actualizacin de la clase Estudiante o e o


79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: / R e g r e s a e l c o n t e n i d o d e l campo c a l v e . @return String / public String getClave () { return c l a v e ; } / A c t u a l i z a e l campo c l a v e . @param S t r i n g e l v a l o r que s e va a a s i g n a r . / public void s e t C l a v e ( S t r i n g c l v e ) { clave = clve ; } / R e g r e s a e l c o n t e n i d o d e l campo s i g u i e n t e . C o r r e s p o n d e a l a l i s t a que s i g u e a e s t e e l e m e n t o . @ r e t u r n E s t u d i a n t e Una r e f e r e n c i a a l s i g u i e n t e o b j e t o de l a lista / public Estudiante getSiguiente () { return s i g u i e n t e ; } / A c t u a l i z a e l campo s i g u i e n t e . @param E s t u d i a n t e l a r e f e r e n c i a que s e va a a s i g n a r . / public void s e t S i g u i e n t e ( E s t u d i a n t e s i g ) { siguiente = sig ; }

Podemos tambin poner mtodos ms generales, dado que todos los atribue e a tos son del mismo tipo, que seleccionen un campo a modicar o a regresar. Los podemos ver en el cdigo 5.4. o

Cdigo 5.4 Mtodos que arman y actualizan registro completo o e


106: c l a s s E s t u d i a n t e { 107: / 108: R e g r e s a e l campo s o l i c i t a d o . 109: @param c u a l i d e n t i f i c a d o r d e l campo s o l i c i t a d o 110: @ r e t u r n La c a d e n a con e l campo s o l i c i t a d o 111: / 112: p u b l i c S t r i n g getCampo ( i n t c u a l ) { 113: switch ( c u a l ) { 114: case NOMBRE: r e t u r n getNombre ( ) ; 115: case CUENTA : return getCuenta ( ) ;

(Estudiante)1/2

146

Datos estructurados
(Estudiante) 2/2

Cdigo 5.4 Mtodos que arman y actualizan registro completo o e


116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: case CARRERA : return getCarrera ( ) ; case CLAVE : return getClave ( ) ; default : r e t u r n " Clave invalida " ; } // end o f s w i t c h ( c u a l ) } / M o d i f i c a e l campo s o l i c i t a d o . @param c u l e l campo s o l i c i t a d o . a @param c a d e n a e l nuevo v a l o r / p u b l i c v o i d setCampo ( i n t c u a l , S t r i n g c a d e n a ) { switch ( c u a l ) { case NOMBRE: setNombre ( c a d e n a ) ; break ; case CUENTA : s e t C u e n t a ( c a d e n a ) ; Break ; case CARRERA : s e t C a r r e r a ( c a d e n a ) ; break ; case CLAVE : s e t C l a v e ( cadena ) ; break ; default : break ; } // end o f s w i t c h ( c u a l ) }

Finalmente, de esta clase unicamente nos faltan dos mtodos, uno que regrese e todo el registro armado, listo para impresin, y uno que actualice todos los campos o del objeto. Podemos ver su implementacin en el listado 5.5. o

Cdigo 5.5 Mtodos que arman y actualizan registro completo (2) o e


140: 141: 142: 143: 144: 145: 146: 147: 148:

(Estudiante)1/2

/ Arma una c a d e n a con e l c o n t e n i d o de t o d o e l r e g i s t r o . @ r e t u r n S t r i n g E l r e g i s t r o armado . / public String getRegistro () { r e t u r n nombre . t r i m ()+"\t"+ c u e n t a . t r i m ()+"\t"+ c a r r e r a . t r i m ()+"\t"+ clave . trim ( ) ; }

5.2 La lista de registros

147
(Estudiante)2/2

Cdigo 5.5 Mtodos que arman y actualizan registro completo (2) o e


149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: / A c t u a l i z a t o d o e l r e g i s t r o de un j a l n . o @param S t r i n g e l nombre , S t r i n g cuenta String carrera String clave . / p u b l i c v o i d s e t R e g i s t r o ( S t r i n g nmbre , S t r i n g c n ta , String clve , String crrera ) nombre = nmbre . t r i m ( ) ; cuenta = cnta . trim ( ) ; clave = clve . trim ( ) ; carrera = c r r e r a . trim ( ) ; }

Como se puede observar, no hay necesidad de disear los mtodos previo a n e su programacin, ya que son, en general, muy pequeos y concisos. Es importante o n notar que en esta clase no interviene ningn mtodo que maneje ningn tipo de u e u coleccin de objetos, sino unicamente lo que concierne a cada registro en s mismo. o

5.2 La lista de registros


Como acabamos de mencionar, la interrelacin entre los distintos registros de o nuestra base de datos la vamos a manejar desde una clase separada a la de los registros mismos, la lista de registros. Para toda lista requerimos, como mencionamos en relacin con la gura 5.1 o en la pgina 142, un ancla para la lista, algo que sea la referencia al primer a elemento de la lista. De no tener esta referencia, como la lista se va a encontrar en el heap, no habr manera de saber dnde empieza o dnde estn sus elementos. a o o a Hagan de cuenta que el heap es el mar, y las declaraciones en una clase son un barco. Cada vez que se crea un objeto, ste se acomoda en el heap (mar), por lo que e tenemos que tener una cuerda (cadena, referencia, apuntador) desde el barco hacia el primer objeto, y de ste al segundo, etc. De esa manera, si jalamos la cuerda e podemos tener acceso a todos los elementos que conforman la lista. Jalar la cuerda quiere decir, en este contexto, ir tomando referencia por referencia, como si se tratara de un collar de cuentas. Al igual que en nuestra implementacin o anterior, usaremos un identicador lista que nos indique el primer elemento de la

148

Datos estructurados

lista, una referencia al primer elemento. Por supuesto que esta lista estar vac a a en tanto no le agreguemos ningn objeto del tipo Estudiante. Por lo tanto, la unica u diferencia entre las declaraciones de nuestra implementacin anterior y sta es el o e tipo de la lista, quedando las primeras l neas de la clase como se puede apreciar en el listado 5.6.

Cdigo 5.6 Atributos de la clase ListaCurso o


1: import i c c 1 . i n t e r f a z . C o n s o l a ; 2: / 3: R e s t r u c t u r a a l a c l a s e Curso p a r a que s e a 4: manejada a t r a v s de una l i s t a e 5: / 6: c l a s s L i s t a C u r s o { 7: / La c a b e z a de l a l i s t a : / 8: private Estudiante l i s t a ; 9: / E l n mero de g r u p o u / 10: private S t r i n g grupo ; 11: / Cuenta e l n mero de r e g i s t r o s / u 12: p r i v a t e i n t numRegs ;

Al igual que con la clase Curso, tenemos dos constructores, uno que unicamente coloca el nmero del grupo y otro inicializa la lista y coloca el nmero de grupo. u u Debemos recordar que lo primero que hace todo constructor es poner los valores de los atributos que corresponden a referencias en null y los que corresponden a valores numricos en 0. Los constructores se encuentran en el listado 5.7. e

Cdigo 5.7 Constructores para la clase ListaCurso o


13: 14: 15: 16: 17: 18: 19: 20:

1/2

/ C o n s t r u c t o r que u n i c a m e n t e a s i g n a n mero de g r u p o a l a l i s t a . u @param S t r i n g g r E l n mero d e l g r u p o u / public ListaCurso ( St ri n g gr ) { l i s t a = null ; grupo = gr ; numRegs = 0 ; }

5.2 La lista de registros

149
2/2

Cdigo 5.7 Constructores para la clase ListaCurso o


21: 22: 23: 24: 25: 26: 27: 28: 29: 30: / C o n s t r u c t o r que a s i g n a e l n mero de g r u p o y una s u b l i s t a } u inicial . @param E s t u d i a n t e s u b l i s t a . @param S t r i n g g r N mero d e l g r u p o . u / public ListaCurso ( Estudiante i n i c i a l e s , St r i ng gr ) { grupo = gr ; lista = iniciales ; numRegs = c u e n t a R e g s ( ) ; }

El segundo constructor tiene dos parmetros; el primero de ellos es una rea ferencia a un objeto de la clase Estudiante y el segundo es una referencia a una cadena. El constructor inicializa la lista con esta referencia (copia la referencia a la variable lista) y asigna el nmero de grupo. u Dado que tenemos 3 atributos, tenemos que denir tres mtodos de acceso e a los atributos. Dos de ellos son muy sencillos: el que me regresa el nmero del u grupo y el que me regresa la cabeza de la lista, pues stos unicamente regresan el e valor del atributo. Los podemos ver en el listado 5.8.

Cdigo 5.8 Mtodos que dan valores de los atributos o e


31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: / R e g r e s a l a l i s t a de e s t u d i a n t e s . @ r e t u r n E s t u d i a n t e La c a b e z a de l a l i s t a . / public Estudiante getLista () { return l i s t a ; } / P r o p o r c i o n a e l n mero de g r u p o . u @return S t r i n g El grupo / public S t r i n g getGrupo ( ) { return grupo ; }

(ListaCurso)

Para regresar el nmero de registros en la lista, preferimos contarlos a pie. u Todo lo que tenemos que hacer es colocarnos al principio de la lista e incrementar u8n contador por cada registro al que podamos llegar. Podemos ver un diagrama de Warnier-Orr muy sencillo en la gura 5.2 en la siguiente pgina y su prograa macin correspondiente en el listado 5.9 en la siguiente pgina. o a

150

Datos estructurados

Figura 5.2

Contando los registros de una lista


6 9 9 9 9 9 9 9 9 9 9Inicio 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 9 Cuenta el 9 9 9 9 9 registro actual 9 9 9 9(mientras haya) 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 7 6 9inicia contador 9 9 9 9 9 8 3

cuantos
6 9 8 9 7 6 9 8 9 7

o 9Colcate al 9 9 inicio de 9 9 9 7 la lista


6 9 9 9incrementa 9 9 9 9 9 contador 9 9 9 8 9 9 9 9 9 pasa al 9 9 9 9 9siguiente 9 7 3

actual

lista

Cuenta registros 9 en una lista 9

cuantos

6 9 8 9 7

actual

toma el siguiente

F inal

Entrega el contador

Cdigo 5.9 Recorrido de una lista para contar sus registros o


43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: / C a l c u l a e l n mero de r e g i s t r o s en l a l i s t a . u @ r e t u r n i n t E l n mero de r e g i s t r o s u / p u b l i c i n t getNumRegs ( ) { int cuantos = 0; Estudiante actual = l i s t a ; w h i l e ( a c t u a l != n u l l ) { c u a n t o s ++; actual = actual . getSiguiente (); } return cuantos ; }

(ListaCurso)

5.2 La lista de registros

151

Este mtodo nos da el patrn que vamos a usar casi siempre para recorrer una e o lista, usando para ello la referencia que tiene cada registro al registro que le sigue. El patrn general es como se muestra en la gura 5.3. o

Figura 5.3

Procesando los registros de una lista


6 5 9 9 9Inicializacin para el proceso Variables para acumular o 9 9 9 Colocarse al principio de la lista 9 9 5 9 8 9 9 9 9 9F inal 9 7 5

Procesa el registro actual Procesa registros 9 en una lista (mientras haya) 9 9

Proceso actual toma el siguiente Entregar resultados

Junto con los mtodos de acceso, debemos tener mtodos que alteren o asige e nen valores a los atributos. Sin embargo, tanto el atributo numRegs como lista debern ser modicados por las operaciones de la base de datos, y no directaa mente. En cambio, podemos querer cambiarle el nmero de grupo a una lista. Lo u hacemos simplemente indicando cul es el nuevo nmero de grupo. Podemos ver a u este mtodo en el listado 5.10. e

Cdigo 5.10 Modica el nmero de grupo o u


55: 56: 57: 58: 59: 60: / M o d i f i c a e l n mero de g r u p o . u @param S t r i n g g r E l nuevo n mero de g r u p o . u / public void setGrupo ( S t r i n g gr ) { grupo = gr ; }

(ListaCurso)

Podemos empezar ya con las funciones propias de la base de datos, como por ejemplo, agregar un estudiante a la lista. Realmente podr amos agregarlo de tal manera que se mantenga un cierto orden alfabtico, o simplemente agregarlo e en cualquier posicin de la misma. Haremos esto ultimo, y el mantener la lista o ordenada se quedar como ejercicio. a Hay dos posiciones lgicas para agregar un registro. La primera de ellas y la o ms sencilla es al inicio de la lista. Bsicamente lo que tenemos que hacer es poner a a al registro que se desea agregar como el primer registro de la lista. Esto quiere

152

Datos estructurados

decir que la nueva lista consiste de este primer registro, seguido de la vieja lista. Veamos en la gura 5.4 qu es lo que queremos decir. El diagrama de Warnier-Orr e para hacer esto est en la gura 5.5 y la programacin del mtodo se encuentra a o e en el listado 5.11.

Figura 5.4

Agregando al principio de la lista


6 9Pon a nuevo a apuntar a lista 8

Agrega registro 9 al principio 7

Pon a lista a apuntar a nuevo

Figura 5.5

Esquema del agregado un registro al principio de la lista

Antes:
r lista Inf o r Inf o r Inf o r Inf o r

r nuevo

Inf o

Despus: e
r lista Inf o r Inf o r Inf o r Inf o r

r nuevo

Inf o

5.2 La lista de registros

153 (ListaCurso)

Cdigo 5.11 Agregar un registro al principio de la lista o


61: 62: 63: 64: 65: 66: 67: / Agrega un r e g i s t r o a l p r i n c i p i o de l a l i s t a @param E s t u d i a n t e nuevo A q u i e n s e va a a g r e g a r / p u b l i c v o i d a g r e g a E s t ( E s t u d i a n t e nuevo ) { nuevo . s e t S i g u i e n t e ( l i s t a ) ; l i s t a = nuevo ; }

Si lo quisiramos acomodar al nal de la lista las cosas se complican un poco e ms, aunque no demasiado. Queremos hacer lo que aparece en la gura 5.6. a

Figura 5.6

Agregando al nal de la lista


r lista Inf o r Inf o r Inf o r Inf o r

r nuevo

Info

Lo que tenemos que hacer es localizar en la lista al registro al que ya no le sigue ninguno, y poner a ese registro a apuntar al nuevo. El algoritmo queda como se muestra en la gura 5.7 en la siguiente pgina. a En este algoritmo la condicin para que pare la iteracin es que al registro o o actual no le siga nada. Esto, traducido a nuestro entorno, equivale a la expresin o booleana
a c t u a l . g e t S i g u i e n t e ( ) != n u l l

La programacin del algoritmo queda como se ve en el listado 5.12 en la siguiente o pgina. a

154

Datos estructurados

Figura 5.7

Agregando al nal de la lista


6 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 Buscar el 9 9 9 9 9 ultimo 9 8 3 6 9lista vac a? lista apunta a nuevo 9 9 9 9 9 9 9 6 9 9 9 9 Colocarse al principio 9 9 9 9 9 9 9 9 9 9 de la lista 9 9 9 6 9 8 9 9 9 9 9 9 9 9 9 8 9 Recorrer registros 9 9 9 9lista vac 9 9 a? 9 9 8 Toma el 9 9 (Mientras siga 9 9 9 9 9 9 9 9 9 9 siguiente 9 alguno 9 9 9 9 9 9 9 9 registro 9 9 9 9 9 9 9 al actual) 9 9 9 9 9 9 7 7 7

Agregar registro al nal de la lista

9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 Pon al ultimo registro a 9 9 9 7

registro

apuntar al nuevo

Cdigo 5.12 Agregar un registro al nal de la lista o


68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: / Agrega e l r e g i s t r o a l f i n a l de l a l i s t a . @param E s t u d i a n t e nuevo E l r e g i s t r o p o r a g r e g a r / p u b l i c v o i d a g r e g a E s t F i n a l ( E s t u d i a n t e nuevo ) { Estudiante actual ; i f ( l i s t a == n u l l ) { // No hay n a d i e en l a l i s t a l i s t a = nuevo ; } else { actual = l i s t a ; w h i l e ( a c t u a l . g e t S i g u i e n t e ( ) != n u l l ) { actual = actual . getSiguiente (); } / Estamos f r e n t e a l u l t i m o r e g i s t r o / a c t u a l . s e t S i g u i e n t e ( nuevo ) ; } // d e l e l s e }

(ListaCurso)

Como se puede ver, seguimos el mismo patrn que recorre la lista, pero nuestra o

5.2 La lista de registros

155

condicin de parada es un poco distinta. Vamos a parar cuando ya no siga nadie o al actual, o sea, cuando la referencia al siguiente sea nula. Sin embargo, si la lista est vac nos da un error al tratar de ver el siguiente a a, de alguien que no existe. Adicionalmente, si la lista est vac agregar al nuevo a a, registro al principio o al nal es equivalente. Sigamos implementando aquellas funciones que responden al patrn de proceo so que dimos. Por ejemplo, el mtodo que lista todos los registros es exactamente e este patrn. El algoritmo lo podemos ver en la gura 5.8 y la programacin en el o o listado 5.13.

Figura 5.8

Imprimiendo todos los registros


6 9Toma el primero de la lista 9 9 6 8 Imprime lista Procesa el registro actual 8Imprime el registro actual 9 9 9 7 (mientras haya) 7 Toma el registro siguiente

Para programar este mtodo debemos tomar en consideracin que debemos e o escribir en algn dispositivo, por lo que requerimos como parmetro un objeto u a de la clase Consola. Imprimir el registro es realmente sencillo, porque tenemos un mtodo que nos regresa el registro en forma de cadena. e

Cdigo 5.13 Imprimiendo toda la lista o


86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102:

(ListaCurso)

/ L i s t a todos l o s r e g i s t r o s d e l Curso . @param C o n s o l a c o n s d nde e s c r i b i r . o / public void l i s t a T o d o s ( Consola cons ) { Estudiante actual = l i s t a ; int i = 0; w h i l e ( a c t u a l != n u l l ) { i ++; cons . imprimeln ( a c t u a l . g e t R e g i s t r o ( ) ) ; actual = actual . getSiguiente (); } i f ( i == 0 ) { c o n s . i m p r i m e l n ( "No hay registros en la base de datos " ) ; } }

156

Datos estructurados

Para el mtodo que escribe todos los registros que cazan con una cierta subcae dena tambin vamos a usar este patrn, pues queremos revisar a todos los registros e o y, conforme los vamos revisando, decidir para cada uno de ellos si se elige (imprime) o no. El algoritmo se encuentra en la gura 5.9 y la programacin en el o listado 5.14.

Figura 5.9

Imprimiendo registros seleccionados


6 9Principio 9 9 9 9 9 9 9 9 9 9 9 9 9 9 Imprime lista 9 8 3 6 9 El campo en el registro 9 9 9 9 actual caza con la 9 9 9 9 9 subcadena? 9 9 9 8

Toma el primero de la lista

6 9 Acepta 8 9 7

Procesa el de registros 9 9 seleccionados 9 registro actual 9 9 9 (mientras haya) 9 9


9 9 9 9 9 9 9 9 7

el registro

6 9 El campo en el registro 8 9 9 9 9 actual NO caza con la 9 9 9 7 9 9 9 subcadena? 9 9 7

Toma el registro siguiente

Cdigo 5.14 Imprimiendo registros seleccionados o


103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125:

(ListaCurso)

/ Imprim e l o s r e g i s t r o s que c a z a n con un c i e r t o p a t r n . o @param C o n s o l a c o n s D i s p o s i t i v o en e l que s e va a e s c r i b i r . @param i n t c u a l Con c u l campo s e d e s e a c o m pa r ar . a @param S t r i n g s u b c a d Con e l que queremos que c a c e . / p u b l i c v o i d losQueCazanCon ( C o n s o l a cons , i n t c u a l , S t r i n g subcad ) { int i = 0; subcad = subcad . toLowerCase ( ) ; Estudiante actual = l i s t a ; w h i l e ( a c t u a l != n u l l ) { i f ( a c t u a l . getCampo ( c u a l ) . i n d e x O f ( s u b c a d ) != 1) { i ++; cons . imprimeln ( a c t u a l . g e t R e g i s t r o ( ) ) ; } actual = actual . getSiguiente (); } i f ( i == 0 ) { c o n s . i m p r i m e l n ( "No se encontr ning n registro " o u + "que cazara " ) ; } }

5.2 La lista de registros

157

En el listado 5.14 en la pgina opuesta hacemos referencia al mtodo geta e Campo que implementamos ya en el listado 5.4. En su momento explicamos las caracter sticas del mismo, por lo que ya no hay necesidad de repetirlas. Para los mtodos buscaEst y quitaEst el patrn que debemos seguir al recorrer e o la lista es un poco distinto, porque tenemos que parar en el momento en que encontremos lo que estamos buscando, y ya no seguir recorriendo la lista. Pero deberemos prevenir el que lo que buscamos no se encuentre en la lista. El patrn o general para este tipo de bsquedas se muestra en la gura 5.10. u Para el caso en que se busca un registro que tenga como subcadena en un campo determinado a cierta subcadena, el proceso del registro que no caza no es ninguno, mientras que el proceso del que caza es simplemente regresar la referencia del registro que caz. La programacin de este mtodo queda como se muestra en o o e el listado 5.15 en la siguiente pgina. a

Figura 5.10

Patrn de bsqueda de un registro que cumpla con . . . o u


6 9 9 9Inicio 9 9 9 9 9 9 9 9 9 9 9 9 Revisa registros 9 9 9 9 9 (mientras haya y 9 9 8 5

Colcate el principio de la lista o Inicializacin de acumuladores o


6 9 8 9 7

Toma el siguiente

Busca algo no lo encuentres) en una lista 9 9

9 9 3 9 9 9SI se encontr el registro Procesa el registro 9 o 9 9 9 9 9 9 9 9 9 9 9 9 3 9 9 7

NO se encontr el registro Emite mensaje o

Como se puede observar, la condicin de parada es doble: por un lado se debe o vericar que no se acabe la lista, y por el otro si el registro actual cumple la condicin que buscamos, que en este caso es que contenga a la subcadena en el o campo indicado.

158

Datos estructurados

Cdigo 5.15 Mtodo que busca un registro o e


126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142:

(ListaCurso)

/ Busca a l r e g i s t r o que c o n t e n g a a l a s u b c a d e n a . @param i n t c u a l C u a l e s e l campo que s e va a comparan . @param S t r i n g s u b c a d La c a d e n a que s e e s t b u s c a n d o . a @return Estudiante El r e g i s t r o deseado o n u l l . / public E s t u d i a n t e buscaSubcad ( i n t cual , S t r i n g subcad ) { Estudiante actual ; subcad = subcad . trim ( ) . toLowerCase ( ) ; actual = l i s t a ; w h i l e ( a c t u a l != n u l l && ( a c t u a l . getCampo ( c u a l ) . i n d e x O f ( s u b c a d . t o L o w e r C a s e ( ) ) ) == 1) actual = actual . getSiguiente (); return a c t u a l ; }

5.2.1.

Paso por valor y por referencia


Es el momento adecuado para saber qu pasa cuando dentro de un mtodo e e o funcin se altera el valor de un parmetro, como sucede en la l o a nea 135: en el listado 5.15. En Java se dice que los parmetros pasan por valor. Esto quiere decir a que al llamar a un mtodo o funcin, se hace una copia del valor de los parmetros, e o a y esto es lo que se entrega al mtodo. Por lo tanto, todo lo que el mtodo haga con e e ese parmetro no se reejar fuera de l, ya que trabaja con una copia. Cuando a a e un parmetro se pasa por valor, se dice que es un parmetro de entrada. a a A lo mejor esto resulta un poco confuso tratndose de objetos que pasan como a parmetros. Cuando se pasa un objeto como parmetro, se pasa la copia de una a a variable que contiene una referencia. Por lo tanto, las modicaciones que se hagan al objeto referido por la copia de la variable s se van a reejar fuera del mtodo, e ya que copia o no, contienen una direccin vlida del heap. Los cambios a los que o a nos referimos son aqullos que se reeren al valor de la referencia, o sea, ponerlos e a apuntar a otro lado. Estos ultimos cambios, en el punto de llamada del mtodo, e no se reejan. Por ejemplo, en la l nea 135: del listado 5.15, los cambios a subcad se van a ver slo mientras permanezcamos en buscaSubcad. Una vez que salgamos, o podremos observar que la cadena que se utiliz como parmetro no sufri ningn o a o u cambio.

5.2 La lista de registros

159

Algunos lenguajes de programacin pueden pasar parmetros por referencia. o a Con este mtodo lo que se pasa al mtodo es la direccin (referencia) donde e e o se encuentra la variable en cuestin. Si este mtodo funcionara en Java. lo que o e estar amos pasando ser la direccin en memoria donde se encuentra la variable a o que contiene a la referencia. Java no tiene este mtodo de pasar parmetros, pero e a se lo pueden encontrar en lenguajes como C++ y Pascal. Cuando un parmetro a se pasa por referencia, generalmente se hace para dejar ah algn valor, por lo que u se dice que es un parmetro de salida, o de entrada y salida. a En los lenguajes mencionados se usan parmetros por referencia para que los a mtodos puedan devolver ms de un valor. En Java esto se har construyendo e a a un objeto que tuviera a los campos que queremos regresar, y se regresa al objeto como resultado del mtodo. e

5.2.2.

Eliminacin de registros o
Tal vez el proceso ms complicado es el de eliminar un estudiante, pues se a tienen que vericar varios aspectos de la lista. No es lo mismo eliminar al primer estudiante que eliminar a alguno que se encuentre en posiciones posteriores a la primera. Veamos en la gura 5.11 los dos casos.

Figura 5.11

Eliminacin de un estudiante o
lista r Inf o r Inf o r Eliminacin del primero o Inf o r Inf o r

lista r Inf o r Inf o r

Eliminacin entre dos registros o Inf o r Inf o r

En el primer caso tenemos que redirigir la referencia lista a que ahora apunte hacia el que era el segundo registro. En el segundo caso tenemos que conservar la

160

Datos estructurados

informacin del registro anterior al que queremos eliminar, para poder modicar o su referencia al siguiente a que sea a la que apuntaba el registro a eliminar. El algoritmo para eliminar un registro se encuentra en la gura 5.12.

Figura 5.12

Eliminacin de un registro en una lista o


6 9 9 9 3 9 9 9lista null? NO se puede 9 9 9 9 9 9 9 9 9 9 9 9 3 9 9 9lista 9 null? 9 9 9 9 9 9 9 5 9 9 9 9 9Es el primero? Modica a lista a que apunte al segundo 9 9 9 regresa que s se pudo 9 9 9 9 9 9 9 9 9 9 9 9 3 9 9 9Es el primero? 9 9 9 9 9 9 9 8





Eliminar un estudiante

9 9Anotar como anterior al primero 9 9 9 9 9 9 9 6 9 9 9 Revisar la lista 9 9 8 9 9 9 9(mientras haya Y moverse al siguiente de la lista 9 9 9 7 9 no lo encuentre) 9 9 9 9 9 9 9 9 5 9 9 9 9 Pon al anterior a apuntar 9 9Lo encontr? e 9 9 9 al siguiente del actual 9 9 9 9 9 9 9 9 9 9 9 6 9 9 9 9 9 8 9 9 9Lo encontr? 9 e Avisa que no se pudo 9 9 9 9 7 7

Colocarse en el segundo de la lista

5.2 La lista de registros

161 (ListaCurso)

Cdigo 5.16 Eliminacin de un registro en una lista o o


143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173:

/ Q u i t a e l r e g i s t r o s o l i c i t a d o ( p o r nombre ) de l a l i s t a @param S t r i n g nombre E l nombre d e l r e g i s t r o / p u b l i c boolean q u i t a E s t ( S t r i n g nombre ) { Estudiante anterior , actual ; nombre=nombre . t r i m ( ) . t o L o w e r C a s e ( ) ; i f ( l i s t a == n u l l ) { // E s t v a ca l a l i s t a : no s e pudo a return f a l s e ; } i f ( l i s t a . getNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) . e q u a l s ( nombre ) ) { l i s t a = l i s t a . getSiguiente (); return true ; } // No e s i g u a l a l p r i m e r o . anterior = lista ; // E l p r i m e r o a c t u a l = l i s t a . g e t S i g u i e n t e ( ) ; // E l s e g u n d o w h i l e ( a c t u a l != n u l l && ! ( a c t u a l . getNombre ( ) . t o L o w e r C a s e ( ) . e q u a l s ( nombre . t o L o w e r C a s e ( ) ) ) ) { anterior = actual ; actual = actual . getSiguiente (); } i f ( a c t u a l == n u l l ) { // No s e e n c o n t r o return f a l s e ; } anterior . setSiguiente ( actual . getSiguiente ()); // P r o c e s a return true ; }

Estamos aprovechando que podemos salir de un mtodo a la mitad del mismo e para que quede un esquema ms simple de la programacin, la que se muestra en a o el listado 5.16. No hay mucho que aclarar en este mtodo, ya que corresponde directamente e al algoritmo. En general, se verica que el estudiante a eliminar no sea el primero; si lo es, se le elimina; si no lo es, se procede a buscar su ubicacin en la lista, o manteniendo siempre la referencia al anterior, para poder modicar su referencia al estudiante que se desea eliminar.

162

Datos estructurados

5.2.3.

La clase MenuLista
La programacin del men para tener acceso a la base de datos del curso es o u sumamente similar al caso en que los registros eran cadenas, excepto que ahora, para agregar a cualquier estudiante hay que construir un objeto de la clase Estudiante. El algoritmo es el mismo, por lo que ya no lo mostramos. La programacin o de encuentra en el listado 5.17. Es en esta clase donde realmente se van a crear objetos nuevos para poderlos ir enlazando en la lista. Por ejemplo, para agregar un estudiante, una vez que tenemos los datos (l neas 95: en la pgina 164- 98: en la pgina 164 en el listado 5.17), a a procedemos a invocar al mtodo agrega de la clase ListaCurso. Este mtodo tiene e e como argumento un objeto, que es creado en el momento de invocar a agrega ver l nea 99: en la pgina 164 del listado 5.17. Este es el unico mtodo en el que a e se crean objetos, y esto tiene sentido, ya que se requiere crear objetos slo cuando o se desea agregar a un estudiante. Para el caso de los mtodos de la clase ListaCurso que regresan una referencia a e un objeto de tipo estudiante, es para lo que se declar la variable donde l o nea 76: en la pgina 164 del listado 5.17. En estos casos el objeto ya existe, y lo unico que a hay que pasar o recibir son las variables que contienen la referencia.

Cdigo 5.17 Men para el manejo de la lista o u

(MenuLista)1/5

1: import i c c 1 . i n t e r f a z . C o n s o l a ; 2: 3: c l a s s M e n u L i s t a { 4: / 5: R e p o r t a que e l e s t u d i a n t e b u s c a d o no f u e l o c a l i z a d o . 6: 7: @param c o n s OutputStream en e l que s e e s c r i b e 8: @param nombre s u b c a d e n a que no f u e l o c a l i z a d a 9: / 10: static final String blancos = 11: " "; 12: p r i v a t e s t a t i c f i n a l i n t SALIR = 0 , 13: AGREGAR = 1 , 14: QUITAR = 2 , 15: BUSCAR = 3 , 16: LISTARTODO = 4 , 17: LISTARALGUNOS = 5 ;

5.2 La lista de registros

163 (MenuLista) 2/5

Cdigo 5.17 Men para el manejo de la lista o u


18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63:

p r i v a t e v o i d r e p o r t a N o ( C o n s o l a cons , S t r i n g nombre ) { c o n s . i m p r i m e l n ( "El estudiante :\n\t" + nombre + "\n No est en el grupo " ) ; a } / P i d e en e l I n p u t S t r e a m e l nombre d e l alumno . @param c o n s e l I n p u t S t r e a m @ r e t u r n E l nombre l e d o / p r i v a t e S t r i n g pideNombre ( C o n s o l a c o n s ) { S t r i n g nombre = c o n s . l e e S t r i n g ( "Dame el nombre del " + " estudiante , empezando por apellido paterno :" ) ; r e t u r n nombre ; } / P i d e en e l I n p u t S t r e a m l a c a r r e r a d e l alumno . @param c o n s e l I n p u t S t r e a m @ r e t u r n La c a r r e r a l e d a / private S t r i n g p i d e C a r r e r a ( Consola cons ) { S t r i n g c a r r e r a = c o n s . l e e S t r i n g ( "Dame una de las " + " carreras :\t" + "Mate\ tComp \ tFisi \ tActu \ tBiol :" ) ; return c a r r e r a ; } / P i d e en e l I n p u t S t r e a m l a c l a v e de a c c e s o d e l alumno . @param c o n s e l I n p u t S t r e a m @ r e t u r n La c l a v e de a c c e s o l e d a / private S t r i n g pideClave ( Consola cons ) { String clave = c o n s . l e e S t r i n g ( "Dame la clave de acceso del " + " estudiante :" ) ; return c l a v e ; } / P i d e en e l I n p u t S t r e a m e l numero de c u e n t a d e l alumno . @param c o n s e l I n p u t S t r e a m @ r e t u r n E l n mero de c u e n t a l e d o u / private S t r i n g pideCuenta ( Consola cons ) { S t r i n g cuenta = c o n s . l e e S t r i n g ( "Dame el n mero de cuenta del" + u " estudiante , de 9 d gitos " ) ; return cuenta ; }

164

Datos estructurados

Cdigo 5.17 Men para el manejo de la lista o u


64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110:

(MenuLista)3/5

/ Maneja e l men p a r a e l a c c e s o y m a n i p u l a c i n de l a b a s e de u o datos . @param c o n s D i s p o s i t i v o en e l que s e va a i n t e r a c c i o n a r @param miCurso Base de d a t o s con l a que s e va a t r a b a j a r @ r e t u r n l a o p c i n s e l e c c i o n a d a , d e s p u s de h a b e r l a e j e c u t a d o o e / p u b l i c i n t daMenu ( C o n s o l a cons , L i s t a C u r s o miCurso ) { S t r i n g nombre , c u e n t a , c a r r e r a , c l a v e ; S t r i n g subcad ; String registros ; E s t u d i a n t e donde ; int cual ; String scual ; S t r i n g menu = "(0)\t Termina \n " + "(1)\t Agrega \n " + "(2)\t Quita \n " + "(3)\t Busca \n " + "(4)\t Lista todos \n " "(5)\t Lista subconjunto que contenga a ...." ; c o n s . i m p r i m e l n ( menu ) ; int opcion ; S t r i n g s o p c i o n = c o n s . l e e S t r i n g ( " Elige una opci n --> " ) ; o o p c i o n = " 012345 " . i n d e x O f ( s o p c i o n ) ; switch ( opcion ) { case SALIR : // S a l i r c o n s . i m p r i m e l n ( " Espero haberte servido .\n" + " Hasta pronto ..." ) ; r e t u r n 1; case AGREGAR : // Agrega E s t u d i a n t e nombre = pideNombre ( c o n s ) ; cuenta = pideCuenta ( cons ) ; c a r r e r a = p i d e C a r r e r a ( cons ) ; c l a v e = pideClave ( cons ) ; miCurso . a g r e g a E s t ( new E s t u d i a n t e ( nombre , c u e n t a , clave , carrera ) ) ; return 1; case QUITAR : // Q u i t a e s t u d i a n t e nombre = pideNombre ( c o n s ) ; donde = miCurso . b u s c a S u b c a d ( E s t u d i a n t e .NOMBRE, nombre ) ; i f ( donde != n u l l ) { nombre = donde . getNombre ( ) ; miCurso . q u i t a E s t ( nombre ) ; c o n s . i m p r i m e l n ( "El estudiante :\n \t" + nombre + "\n Ha sido eliminado " ) ; }

5.2 La lista de registros

165 (MenuLista) 4/5

Cdigo 5.17 Men para el manejo de la lista o u


111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156:

e l s e r e p o r t a N o ( cons , nombre ) ; return 2; case BUSCAR : // Busca s u b c a d e n a s u b c a d = c o n s . l e e S t r i n g ( "Dame la subcadena a "+ " buscar : " ) ; do { s c u a l = cons . l e e S t r i n g ( " Ahora dime de cu l campo : 1: Nombre " a + "2: Cuenta , 3: Carrera , 4: Clave " ) ; c u a l = " 01234 " . i n d e x O f ( s c u a l ) ; i f ( c u a l < 1) { c o n s . i m p r i m e l n ( " Opci n no v lida " ) ; o a } } while ( c ual < 1 ) ; donde = miCurso . b u s c a S u b c a d ( c u a l , s u b c a d ) ; i f ( donde != n u l l ) { c o n s . i m p r i m e l n ( donde . g e t R e g i s t r o ( ) ) ; } else { r e p o r t a N o ( cons , s u b c a d ) ; } return 3; case LISTARTODOS : // L i s t a t o d o s miCurso . l i s t a T o d o s ( c o n s ) ; return 4; case LISTARALGUNOS : // L i s t a con c r i t e r i o s u b c a d = c o n s . l e e S t r i n g ( "Da la subcadena que " + " quieres contengan los " + " registros :" ) ; do { s c u a l = cons . l e e S t r i n g ( " Ahora dime de cu l campo : 1: Nombre , " a + "2: Cuenta , 3: Carrera , 4: Clave " ) ; c u a l = " 01234 " . i n d e x O f ( s c u a l ) ; i f ( c u a l < 1) { c o n s . i m p r i m e l n ( " Opci n no v lida " ) ; o a } } while ( c ual < 1 ) ; miCurso . losQueCazanCon ( cons , c u a l , s u b c a d ) ; return 5; default : // E r r o r , v u e l v e a p e d i r c o n s . i m p r i m e l n ( "No diste una opci n v lida .\n " + o a "Por favor vuelve a elegir ." ) ; return 0; } // s w i t c h } // daMenu

166

Datos estructurados

Cdigo 5.17 Men para el manejo de la lista o u


157: 158: 159: 160: 161: 162: 163: 164: }

(MenuLista) 5/5

p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { int opcion ; C o n s o l a c o n s o l a = new C o n s o l a ( ) ; L i s t a C u r s o miCurso = new L i s t a C u r s o ( "4087" ) ; M e n u L i s t a miMenu = new M e n u L i s t a ( ) ; w h i l e ( ( o p c i o n = miMenu . daMenu ( c o n s o l a , miCurso ) ) != }

1);

Con esto damos por terminado este cap tulo. Se dejan como ejercicios las siguientes mejoras: Conseguir que al agregar un registro, ste se agregue en el lugar que le toca e alfabticamente. e Si la lista est ordenada, al buscar a un registro, se sabe que no se encontr en a o cuanto se localiza el primer registro con llave mayor a la llave del que se busca, por lo que se puede hacer la bsqueda ms eciente. u a

Herencia

En los lenguajes orientados a objetos, la herencia, que conlleva la capacidad de reutilizar cdigo de manera inteligente, es una de las caracter o sticas ms ima portantes de este enfoque.

6.1 Extensin de clases o


Una de las caracter sticas ms valiosas en el cdigo que hacemos es la posibilia o dad de reutilizarlo, esto es, tomar un cdigo que ya trabaja para cierta aplicacin o o y usarlo en otra aplicacin parecida pero no idntica. En el contexto de lenguao e jes tradicionales esto se logrando copiando aquellas partes del programa que nos sirven y modicndolas para que se ajusten a nuevas situaciones. Estos mecanisa mos son, en el mejor de los casos muy laboriosos, y en el peor muy susceptibles a errores. Uno de los mayores benecios de la Orientacin a Objetos es la posibilidad de o extender clases ya construidas construir subclases de tal manera que se pueda seguir utilizando la clase original en el contexto original, pero se pueda, asimismo, agregarle atributos a la subclase, redenir algunos de los mtodos para que tomen e en cuenta a los nuevos atributos o agregar mtodos nuevos. e La clase original es la superclase con respecto a la nueva; decimos que la

168

Herencia

subclase hereda los atributos y mtodos de la superclase. Que herede quiere decir e que, por el hecho de extender a la superclase, tiene al menos todos los atributos y mtodos de la superclase. e Si regresamos por unos momentos a nuestro ejemplo de la base de datos para listas de cursos, veremos que hay muchas otras listas que se nos ocurre hacer y que tendr la misma informacin bsica. Quitemos la clave de usuario, porque an o a esa informacin no la necesitan ms que en el centro de cmputo, y entonces o a o nuestra clase EstudianteBasico quedar denida como se muestra en el listado 6.1. a Omitimos los comentarios para tener un cdigo ms uido. o a

Cdigo 6.1 Superclase EstudianteBasico o


1: import i c c 1 . i n t e r f a z . C o n s o l a ; 2: / 3: R e g i s t r o b s i c o p a r a una Base de Datos de e s t u d i a n t e s a 4: de l a F a c u l t a d de C i e n c i a s . 5: / 6: c l a s s E s t u d i a n t e B a s i c o { 7: p r i v a t e S t r i n g nombre , c a r r e r a , c u e n t a ; 8: private EstudianteBasico s i g u i e n t e ; 9: 10: s t a t i c p u b l i c f i n a l i n t NOMBRE = 1 ; 11: s t a t i c p u b l i c f i n a l i n t CUENTA = 2 ; 12: s t a t i c p u b l i c f i n a l i n t CARRERA = 3 ; 13: 14: public EstudianteBasico () { 15: nombre = c a r r e r a = c u e n t a = n u l l ; 16: } 17: p u b l i c E s t u d i a n t e B a s i c o ( S t r i n g nmbre , S t r i n g c nta , 18: String crrera ) { 19: nombre = nmbre . t r i m ( ) ; 20: cuenta = cnta . trim ( ) ; 21: carrera = c r r e r a . trim ( ) ; 22: siguiente = null ; 23: } 24: p u b l i c S t r i n g getNombre ( ) { 25: r e t u r n nombre ; 26: } 27: p u b l i c v o i d setNombre ( S t r i n g nombre ) { 28: t h i s . nombre = nombre ; 29: } 30: public String getCarrera () { 31: return c a r r e r a ; 32: }

1/2

6.1 Extensin de clases o

169
2/2

Cdigo 6.1 Superclase EstudianteBasico o


33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: } public void s e t C a r r e r a ( S t r i n g c a r r e ) { carrera = carre ; } public S t r i n g getCuenta () { return cuenta ; } public void setCuenta ( S t r i n g cnta ) { cuenta = cnta ; } public EstudianteBasico getSiguiente () { return s i g u i e n t e ; } public void s e t S i g u i e n t e ( E s t u d i a n t e B a s i c o s i g ) { siguiente = sig ; } public String getRegistro () { r e t u r n nombre . t r i m ()+"\t" + c u e n t a . t r i m ()+"\t" + carrera . trim ( ) ; } p u b l i c S t r i n g getCampo ( i n t c u a l ) { S t r i n g cadena ; switch ( c u a l ) { case E s t u d i a n t e B a s i c o .NOMBRE: c a d e n a = getNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) ; break ; case E s t u d i a n t e B a s i c o . CUENTA : cadena = getCuenta ( ) . tr i m ( ) . toLowerCase ( ) ; break ; case E s t u d i a n t e B a s i c o . CARRERA : cadena = g e t C a r r e r a ( ) . tr i m ( ) . toLowerCase ( ) ; break ; default : c a d e n a = " Campo no existente " ; } return cadena ; } p u b l i c v o i d s e t R e g i s t r o ( S t r i n g nmbre , S t r i n g c n ta , String crrera ) { nombre = nmbre . t r i m ( ) ; cuenta = cnta . trim ( ) ; carrera = c r r e r a . trim ( ) ; }

170

Herencia

Se nos ocurren distintos tipos de listas que tomen como base a EstudianteBasico. Por ejemplo, un listado que le pudiera servir a una biblioteca tendr adems de la a, a informacin de EstudianteBasico, los libros en prstamo. Un listado para la Divisin o e o de Estudios Profesionales deber tener una historia acadmica y el primer ao a e n de inscripcin a la carrera. Un listado para calicar a un grupo deber tener un o a cierto nmero de campos para las calicaciones. Por ultimo, un listado para las u salas de cmputo deber contar con la clave de acceso. o a En todos los casos que listamos, la informacin original debe seguir estando o presente, as como los mtodos que tiene la superclase. Debemos indicar, entonces, e que la clase que se est deniendo extiende a la superclase, heredando por lo tanto a todos los mtodos y atributos de la superclase. La sintaxis en Java se muestra a e continuacin. o Sintaxis:

xsubclase que hereday ::= class xsubclasey extends xsuperclasey { xdeclaraciones de campos y mtodosy e
}

Semantica:

La xsubclasey es una nueva declaracin, que incluye (hereda) a todos los o campos de la superclase, junto con todos sus mtodos. Las xdeclaraciones e de campos y mtodosy se reere a lo que se desea agregar a la denicin de e o la superclase en esta subclase.

Si alguna de las declaraciones hechas en la xsubclasey coincide en tipo o rma con algo declarado en la xsuperclasey, entonces los campos o mtodos de la e superclase quedarn tapados o escondidos (redenidos) para la xsubclasey. a Pongamos por ejemplo la programacin de una subclase para las listas con calio caciones. El registro deber contener lo que contiene EstudianteBasico, agregndoa a le nada ms lugar para las calicaciones de los exmenes.El encabezado de la clase a a quedar como se ve en el listado 6.2. a

Cdigo 6.2 Encabezado para la subclase EstudianteCurso o


c l a s s E s t u d i a n t e C u r s o extends E s t u d i a n t e B a s i c o {

A partir de ese momento podemos asumir que EstudianteCurso contiene todos los campos y mtodos que tiene EstudianteBasico. Los unicos mtodos que no heree e

6.2 Arreglos

171 da impl citamente son los constructores con parmetros. Si existe un constructor a sin parmetros, ya sea el que est por omisin o alguno que haya declarado el a a o programador, ste va a ser invocado siempre que se invoque a algn constructor e u de la subclase EstudianteCurso. Regresaremos a esto en cuanto programemos los constructores para la subclase. Cuando tenemos extensin de clases tiene sentido hablar del tipo de acceso que o hemos mencionado muy poco, el acceso protegido (protected). Este tipo de acceso se usa para cuando queremos que las clases que extienden a la actual puedan tener acceso a ciertas variables o mtodos, ya que funciona como si fuera acceso pblico e u para las clases que extienden, pero como si fuera privado para las clases ajenas a la clase actual. Los datos con acceso privado no son accesibles ms que para a la clase actual. El acceso protegido funciona como el privado para clases que no heredan, y como pblico para las clases que heredan. u Cuando un mtodo de una superclase tiene un cierto acceso, si se redene e ese mtodo en una subclase el acceso no puede ser restringido. Esto es, si en la e superclase el mtodo ten acceso pblico, en la subclase deber mantener ese e a u a mismo acceso. Si en la superclase ten acceso de paquete, en la subclase puede a tener acceso de paquete o pblico, pero el acceso no puede ser ni protegido ni u privado.

6.2 Arreglos
En esta seccin estudiaremos estructuras repetitivas de datos, organizados o como vectores, matrices, etc.

6.2.1.

Arreglos de una dimensin o


Podemos pensar en las calicaciones de cada estudiante como un vector, calif0 , calif1 , . . . , calif14 donde estamos asumiendo que cada estudiante tiene lugar para, a lo ms, 15 a calicaciones (del 0 al 14). Se ve, asimismo, muy util el poder manejar a cada una de las calicaciones rerindonos a su sub e ndice, en lugar de declarar calif0, calif1, etc., imitando el manejo que damos a los vectores en matemticas. a

172

Herencia

Es importante notar que lo que tenemos es una coleccin de datos del mismo o tipo que se distinguen uno de otro por el lugar que ocupan. A estas colecciones les llamamos en programacin arreglos. La declaracin de arreglos tiene la siguiente o o sintaxis: Sintaxis: xdeclaracin de arregloy ::= xtipoy[ ] xidenticadory; o Semantica: Se declara una variable que va a ser una referencia a un arreglo de objetos o datos primitivos del tipo dado. Veamos algunos ejemplos: int [ ] arregloDeEnteros ; EstudianteBasico [ ] estudiantes ; oat [ ] vector ; String [ ] cadenas; // // // // // Arreglo de enteros Arreglo de objetos del tipo EstudianteBasico. Arreglo de reales. Arreglo de cadenas

Noten como en la declaracin no estamos diciendo el tamao del arreglo. Esto o n es porque en este momento Java no tiene por qu saber el nmero de elementos, e u ya que lo unico que est reservando es una variable donde guardar la referencia a de donde quedar, en el heap, el arreglo. a Podemos en la declaracin de los arreglos determinar el tamao que van a o n tener. Lo hacemos como en las cadenas, inicializando el arreglo en el momento de declararlo, proporcionando una lista de los elementos que queremos en el arreglo. En ese caso, el tamao del arreglo quedar jado al nmero de elementos que se n a u d en la lista. La sintaxis para este tipo de declaracin es: e o Sintaxis: xdeclaracin de arreglos con inicializaciny ::= o o xtipoy[ ] xidenticadory = { xlistay }; Semantica: Para inicializar el arreglo se dan, entre llaves, valores del tipo del arreglo, separados entre s por coma. El arreglo tendr tantos elementos como apa a rezcan en la lista, y cada elemento estar creado de acuerdo a la lista. En a el caso de un arreglo de objetos, la lista deber contener objetos que ya a existen, o la creacin de nuevos mediante el operador new. o Extendamos los ejemplos que acabamos de dar a que se inicialicen en el momento de la declaracin: o

6.2 Arreglos

173 Arreglo de enteros con 5 elementos, todos ellos enteros. Corresponden a los primeros 5 nmeros primos. u

int [ ] primos = {2,3,5,7,11};


Arreglo de reales con tres elementos.

oat [ ] vector = { 3.14, 8.7, 19.0 };


Arreglo de dos cadenas, la primera de ellas con el valor S y la segunda con el valor No.

String [ ] cadenas = { "S", "No"};


Arreglo de objetos del tipo EstudianteBasico con tres elementos, el primero y tercero con el constructor por omisin y el segundo con los datos que se o indican.

E s t u d i a n t e B a s i c o paco = new E s t u d i a n t e ( "Paco" , " 095376383 " , "Compu" ) ; EstudianteBasico [ ] estudiantes = {new E s t u d i a n t e ( ) , paco , new E s t u d i a n t e ( ) };
Veamos en las guras 6.1 a 6.3 a continuacin cmo se crea el espacio para los o o arreglos que se mostraron arriba. En los esquemas marcamos las localidades de memoria que contienen una referencia con una @en la esquina superior izquierda. Esto quiere decir que su contenido es una direccin en el heap. Las localidades o estn identicadas con rectngulos. El nmero que se encuentra ya sea inmediaa a u tamente a la izquierda o encima del rectngulo corresponde a la direccin en el a o heap.

174

Herencia

Figura 6.1

int[ ] primos = {2,3,5,7,11}; MEMORIA


primos
r 1000
1000 1004 1008 1012 1016

2 [0]

3 [1]

5 [2]

7 [3]

11 [4]

HEAP

Figura 6.2

oat [ ] vector = { 3.14, 8.7, 19.0};

vector @ 2124

2124

2130

2136

3,14
[0]

8,7
[1]

19,0
[2]

MEMORIA

HEAP

Figura 6.3

String [ ] cadenas = { S No}; ,


2784 @ 2784 @ 3826 @ 4218

r0 s r1 s

3826 Si 4218 No

MEM

HEAP

6.2 Arreglos

175

Figura 6.4

Declaracin del contenido de un arreglo de objetos o

1500

1506

1512

@ null @ null @ null @ = referencia


1820 1826 1832

@ null @ null @ null


1020

1500

[0] [1] [2]


3200 Compu 2054 2060 2066

@ 1026 2054 @ 1032


1820

@4020 @5100 @3200

estudiantes @1020 5100

4020 Paco

095376383 looooooooooooooooooooooooooooooooooooooomooooooooooooooooooooooooooooooooooooooon

MEM

HEAP

EstudianteBasico [ ] estudiantes = {% new E s t u d i a n t e ( ) , new E s t u d i a n t e ( "Paco" , " 095376383 " , "Compu" ) , new E s t u d i a n t e ( ) } ;

La manera normal de darle tamao a un arreglo es crendolo, con el enunn a ciado new y especicando el nmero de elementos que va a tener el arreglo. En u este caso las referencias a objetos se inicializan en null mientras que los datos numricos se inicializan en 0 (cero). La sintaxis precisa se da a continuacin. e o

176

Herencia

Sintaxis: xdeclaracin de un arreglo con tamao dadoy ::= o n xtipoy xidenticadory = new xtipoy[xexpresin enteray]; o Semantica: Se inicializa la referencia a un arreglo de referencias en el caso de objetos, o de datos en el caso de tipos primitivos. Si los ejemplos que dimos antes los hubiramos hecho sin la inicializacin ser e o an:

1: 2: 3: 4:

i n t [ ] p r i m o s = new i n t [ 5 ] ; E s t u d i a n t e B a s i c o [ ] e s t u d i a n t e s = new E s t u d i a n t e B a s i c o [ 3 ] ; f l o a t [ ] v e c t o r = new f l o a t [ 3 ] ; S t r i n g [ ] c a d e n a s = new S t r i n g [ 2 ] ;

y los esquemas de su organizacin en memoria quedar como se muestra en las o a guras 6.5 a 6.8. La creacin de los arreglos en la modalidad que estamos presentando no o tiene por qu darse en la declaracin, sino que, como con cualquier otro dato, e o ya sea ste primitivo u objeto, se puede hacer como un enunciado de asignacin e o comn y corriente. Lo unico que hay que tener en mente es que a una misma u referencia se le pueden asignar distintos espacios en el heap.

Figura 6.5

int [ ] primos = new int[5];


1000 1004 1008 1012 1016

primos @ 1000

r0 s

r1 s

r2 s

r3 s

r4 s

MEMORIA

HEAP

6.2 Arreglos

177

Figura 6.6

EstudianteBasico [ ] estudiantes = new EstudianteBasico[3];


5124 5130 5136

estudiantes @ 5124

@null @null @null

r0 s

r1 s

r2 s

MEMORIA
Figura 6.7 oat [ ] vector = oat [3];
2134 2140

HEAP

2146

vector @ 2134

0,0

0,0

0,0

r0 s

r1 s

r2 s

MEMORIA

HEAP

Figura 6.8

String [ ] cadenas = new String[2];


27844 27850

cadenas @ 27844

@null @null

r0 s

r1 s

MEMORIA

HEAP

Uso de los elementos de un arreglo


En el caso de los arreglos de una dimensin, para usar a un elemento del arreglo o debemos seleccionarlo, de manera similar a como seleccionamos a un atributo o mtodo de un objeto. Mientras que en el caso de los objetos se utiliza el operador e punto (.), en el caso de los arreglos de una dimensin se usa el operador r s, cuya o sintaxis es:

178

Herencia

Sintaxis: xseleccin de un elemento de un arregloy ::= o xid. de arregloy [ xexpresin enteray ] o Semantica: El operador r s es el de mayor precedencia de entre los operadores de Java. Eso indica que evaluar la expresin dentro de ella antes que cualquier otra a o operacin (en la ausencia de parntesis). Una vez obtenido el entero correso e pondiente a la xexpresin enteray que pudiera ser una constante entera o proceder a elegir al elemento con ese a ndice en el arreglo. El resultado de esta operacin es del tipo de los elementos del arreglo. De no existir el o elemento al que corresponde el ndice calculado, el programa abortar con a el mensaje ArrayIndexOutOfBoundsException. Al primer elemento del arreglo le corresponde siempre el ndice 0 (cero) y al ultimo elemento el ndice n 1, donde el arreglo se cre con n elementos. o Es importante apreciar que el tamao de un arreglo no forma parte del tipo. n Lo que forma parte del tipo es el nmero de dimensiones (vector, matriz, cubo, u . . . ) y el tipo de sus elementos.

Los arreglos son estructuras de datos estticas a


Cuando decimos que un arreglo no puede cambiar de tamao una vez creado, n nos estamos reriendo al espacio asignado en el heap. Sin embargo, es perfectamente vlido que una misma referencia apunte a un arreglo de un cierto tamao, a n para pasar despus a apuntar a uno de otro tamao. Supongamos, por ejemplo, e n que tenemos la siguiente secuencia de enunciados:
1: 2: 3: 4: 5: 6: 7: 8: 9:

int [ ] enteros ; ... e n t e r o s = new i n t [ 5 ] ; int i = 0; while ( i < 5) { e n t e r o s [ i ] = ( i + 1) 2 ; i ++; } e n t e r o s = new i n t [ 9 ] ; La secuencia de pasos que se dan durante la ejecucin se puede observar en la o gura 6.9 en la pgina opuesta. El nmero entre parntesis a la izquierda de cada a u e subesquema corresponde al nmero de instruccin que se termin de ejecutar. u o o

6.2 Arreglos

179

Figura 6.9

Reasignacin de arreglos o

p1q
enteros @ null

p5q
enteros @ 1260
1260 1264 1268 1272 1276

2
[0]

4
[1]

6
[2]

8
[3]

10
[4]

p6q
enteros @ 2580

2580 2584 2588 2592 2596 2600 2604 2608 2612 2616

r0 s
[0]

0
[1]

0
[2]

0
[3]

0
[4]

0
[5]

0
[6]

0
[7]

0
[8]

Como se puede observar en esta gura, el arreglo que se crea en la l nea 3 del cdigo no es el mismo que el que se crea en la l o nea 7: no se encuentran en la misma direccin del heap, y no contienen lo mismo. Insistimos: no es que haya o cambiado el tamao del arreglo, sino que se cre un arreglo nuevo. n o

6.2.2.

Iteracin enumerativa o

En el cdigo que acabamos de presentar utilizamos una iteracin que no o o hab amos visto (pero que se entiende qu hace) y que es una de las formas de e iteracin ms comn en los lenguajes de programacin. Como el t o a u o tulo de esta seccin lo indica, se utiliza para hacer un recorrido asociado a un tipo discreto y o que se pueda numerar (podr amos utilizar tambin carcteres). La sintaxis general e a de este enunciado se da en el cuadro que sigue.

180

Herencia

Sintaxis: xenunciado de iteracin enumerativay ::= o for ( xenunciado de inicializaciny ; o xexpresin booleanay ; o xlista de enunciadosy ) xenunciado simple o compuestoy Semantica: En la ejecucin va a suceder lo siguiente: o 1. Se ejecuta el xenunciado de inicializaciny. o 2. Se evala la xexpresin booleanay. u o a) Si es verdadera, se contina en el paso 3. u b) Si es falsa, se sale de la iteracin. o 3. Se ejecuta el xenunciado simple o compuestoy. 4. Se ejecuta la xlista de enunciadosy. 5. Se regresa al paso 2. Cualquiera de las tres partes puede estar vac aunque el ; s tiene que a, aparecer. En el caso de la primera y tercera parte, el que est vac indica e o que no se hace nada ni al inicio del enunciado ni al nal de cada iteracin. o En el caso de una xexpresin booleanay, se interpreta como la constante o true. Vimos ya un ejemplo sencillo del uso de un while para recorrer un arreglo unidimensional. Sin embargo, para este tipo de tareas el for es el enunciado indicado. La misma iteracin quedar de la siguiente forma: o a
1: 2: 3: 4: 5: 6:

int [ ] enteros ; ... e n t e r o s = new i n t [ 5 ] ; f o r ( i n t i = 0 ; i < 5 ; i++ ) { e n t e r o s [ i ] = ( i + 1) 2 ; } Hay una pequea diferencia entre las dos versiones. En el caso del for, la varian ble i es local a l, mientras que en while tuvimos que declararla fuera. En ambos e casos, sin embargo, esto se hace una unica vez, que es el paso de inicializacin. o Otra manera de hacer esto con un for, usando a dos variables enumerativas, una para i y otra para i + 1, pudiera ser como sigue:

6.2 Arreglos
1: 2: 3: 4: 5: 6:

181 int [ ] enteros ; ... e n t e r o s = new i n t [ 5 ] ; f o r ( i n t i = 0 , j = 1 ; i < 5 ; i ++, j ++) enteros [ i ] = j 2; }

Del ejemplo anterior hay que notar que tanto i como j son variables locales al for; para que esto suceda se requiere que la primera variable en una lista de este estilo aparezca declarada con su tipo, aunque la segunda (tercera, etc.) variable no debe aparecer como declaracin. Si alguna de las variables est declarada antes y o a fuera del for aparecer el mensaje de que se est repitiendo la declaracin, aunque a a o esto no es correcto. Lo que s es vlido es tener varios fors, cada uno con una a variable local i. Por supuesto que el xenunciado simple o compuestoy puede contener o consistir de, a su vez, algn otro enunciado for o while o lo que queramos. La ejecucin va u o a seguir el patrn dado arriba, terminando la ejecucin de los ciclos de adentro o o hacia afuera.

EJEMPLO 6.2.7
Supongamos que queremos calcular el factorial de un entero n que nos pasan como parmetro. El mtodo podr estar codicado como se muestra en el a e a cdigo 6.3. o

Cdigo 6.3 Clculo de n! o a


p u b l i c long f a c t o r i a l ( i n t n ) { long f a c t = 1 ; f o r ( i n t i = 2 ; i <= n ; i ++) { fact = fact i ; } return f a c t ; }

EJEMPLO 6.2.8
Supongamos ahora que queremos tener dos variables para controlar la iteracin, donde la primera nos va a decir en cul iteracin va (se incrementa de 1 en o a o

182

Herencia

1) y la segunda se calcula sumndose uno al doble de lo que lo que llevaba. El a encabezado del for ser a:
f o r ( i n t i =1, j =1; i <= 8 && j < 2 0 0 ; i ++, j = 2 j + 1 ) c o n s . i m p r i m e l n ( "i=\t" + i + "\tj =\t" + j ) ; } {

En este ejemplo tenemos dos inicializaciones simultneas y dos enunciados de a iteracin que se ejecutan al nal del bloque, antes de regresar a volver a iterar. o Lo que escribir este segmento de programa es lo siguiente: a i= i= i= i= i= i= i= 1 2 3 4 5 6 7 j= j= j= j= j= j= j= 1 3 7 15 31 63 127

Cuando i vale 8, se deja de cumplir la expresin booleana i 8&&j 200. o Al terminar la iteracin donde escribe . . . j  127, inmediatamente despus, o e antes de regresar al encabezado, calcula j  j 2 1  255, que ya no cumple la expresin booleana, por lo que ya no vuelve a entrar para i  8. o Es conveniente mencionar que en el encabezado que acabamos de ver se declaran dos variables, i y j que slo van a ser reconocidas dentro del cuerpo del for. o Regresaremos ms adelante a utilizar este enunciado. Sin embargo, recomendamos a que cuando la expresin booleana no sea numrica o no tenga que ver con relacioo e nes entre enteros, mejor se use alguna de las otras iteraciones que presentamos. Si estuviramos en un lenguaje de programacin que unicamente tuviera la e o iteracin while, el cdigo ser como se muestra en el listado 6.4. o o a

Cdigo 6.4 Codicacin de iteracin for con while o o o


int i = 1 , j = 1; / I n i c i a l i z a c i n / o w h i l e ( i <= 8 && j < 2 0 0 ) { / e x p r e s i n b o o l e a n a / o c o n s . i m p r i m e l n ( "i=\t" + i "\tj = " + j ) ; / c u e r p o i ++; / e n u n c i a d o s de i t e r a c i n / o j j 2 + 1 ; / e n u n c i a d o s de i t e r a c i n / o } // w h i l e

6.2 Arreglos

183

EJEMPLO 6.2.9
Podemos tener iteraciones anidadas, como lo dice la descripcin de la sintaxis. o Por ejemplo, queremos producir un tringulo con la siguiente forma: a 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2

3 3 3 3 3 3 3

4 4 4 4 4 4

5 5 5 5 5

6 6 7 6 7 6 7

8 8

En este caso debemos recorrer rengln por rengln, y en cada rengln recoo o o rrer tantas columnas como renglones llevamos en ese momento. El cdigo para o conseguir escribir esto se encuentra en el listado 6.5.

Cdigo 6.5 Construccin de un tringulo de nmeros o o a u


f o r ( i n t i = 1 ; i <= 9 ; i ++) f o r ( j = 1 ; j <= i ; j ++) c o n s . i m p r i m e ( j + "\t" ) ; { / r e c o r r e l o s r e n g l o n e s / { / r e c o r r e l a s c o l u m n a s / / e s c r i b e e l v a l o r de l a / / columna , s i n t e r m i n a r / / e l r e n g l n . o /

} cons . imprimeln ( ) ; }

/ cambia de r e n g l n / o

Conforme resolvamos ms problemas utilizaremos todo tipo de iteraciones y a aprenderemos a elegir la ms apropiada para cada caso. a

6.2.3.

Arreglos de ms de una dimensin a o


Es obvio que podemos necesitar arreglos de ms de una dimensin, como por a o ejemplo matrices. Para Java los arreglos de dos dimensiones son arreglos de arre-

184

Herencia

glos, y los de tres dimensiones son arreglos de arreglos de arreglos, y as su cesivamente. La declaracin se hace poniendo tantas parejas de corchetes como o dimensiones queramos en un arreglo, como se puede ver a continuacin. o int [ ] [ ] matriz ; EstudianteBasico [ ] [ ] [ ] // 2 d i m e n s i o n e s : m a t r i z f a c u l t a d ; // 3 d i m e n s i o n e s : cubo

Por la manera en que maneja Java los arreglos, si quiero inicializar un arreglo, por ejemplo, de dos dimensiones, tendr que darle entre llaves las inicializaciones e para cada uno de los renglones: int [ ] [ ] matriz = {{2 ,2 ,3 ,3} ,{3 ,4 ,5} ,{8}}; Reconocemos inmediatamente tres sublistas encerradas entre llaves, lo que indica que el arreglo matriz tiene tres renglones. Cada uno de los renglones tiene tamao distinto, y esto se vale, pues siendo un arreglo de arreglos, cada uno de los n arreglos en la ultima dimensin puede tener el nmero de elementos que se desee. o u Un esquema de cmo quedar en memoria se puede ver en la gura 6.10. o an

Figura 6.10

Acomodo en memoria de un arreglo de dos dimensiones


2128 2134 2140 2146 2152

2 [0] @ 3600 @ 3200 @ 2128

2 [1]
3200

3 [2]
3206

3 [3]
3212

1730

[0] [1] [2]

matriz @ 1730

1736 1742

3 [0]
3600

4 [1]

5 [2]

8 [0]
loooooooooooooooooooooooooooooooooooooooooomoooooooooooooooooooooooooooooooooooooooooon

MEM

H E A P

Podemos, para un arreglo de 3 dimensiones, tener la siguiente sucesin de o enunciados:

6.2 Arreglos
i n t [ ] [ ] [ ] cubos ; c u b o s = new i n t [ 3 ] [ ] [ ] ; c u b o s [ 0 ] = new i n t [ 6 ] [ ] ; c u b o s [ 1 ] = new i n t [ 3 ] [ 3 ] ; c u b o s [ 2 ] = new i n t [ 2 ] [ ] ;

185

y tendr amos un arreglo de 3 dimensiones; en la primera tenemos tres elementos: el primer elemento es un arreglo de 6 arreglos, aunque no sabemos todav de a qu tamao es cada uno de los 6 arreglos; el segundo elemento es un arreglo de e n tres arreglos de tres elementos; el tercer elemento es un arreglo de dos arreglos y no sabemos cuantos elementos en cada arreglo. Como podemos tener tambin variables o expresiones enteras que nos den el e tamao de un arreglo, es importante que en cualquier momento podamos saber el n tamao de alguna de las dimensiones del mismo. Para eso, la clase que corresponde n a los arreglos tiene una variable length que nos regresa el tamao del arreglo. n Por ejemplo, para los enunciados que acabamos de hacer, cubos[0].length es 6, cubos[1][1].length es 3 y cubos[2].length es 2. Resumiendo, los arreglos en Java tienen las siguientes caracter sticas: (a) Un arreglo es un objeto. Esto quiere decir que cuando declaramos un arreglo simplemente estamos reservando espacio en memoria para la referencia, la direccin en el heap donde se encontrarn los elementos del arreglo. o a (b) Es una estructura de datos esttica: una vez dado su tamao, el arreglo no a n puede achicarse o agrandarse. (c) Podemos tener arreglos del nmero de dimensiones y del tipo que queramos. u (d) Los arreglos son estructuras de datos homogneas, porque todos sus elementos e son del mismo tipo. (e) Los arreglos de dos dimensiones no tienen por qu tener el mismo nmero de e u elementos en cada rengln del arreglo.Esto se extiende en todas las dimensioo nes que tenga el arreglo.

Uso de arreglos de dos o ms dimensiones a


De manera similar a como lo hacemos en arreglos de una dimensin, podemos o seleccionar a un elemento de un arreglo de ms de una dimensin dando una a o expresin entera para cada una de las dimensiones. Los elementos se van eligiendo o de izquierda a derecha, en el mismo orden en que fueron creados. No hay que perder de vista que en el caso de Java una matriz es un arreglo de arreglos, y por la manera en que los representa internamente se puede dar un error de ndice en cualquiera de las dimensiones, por lo que es importante mantener presente la construccin del arreglo cuando se seleccionan elementos dentro de l. Las o e primeras k 1 dimensiones, si se selecciona a un elemento de ellos, obtendremos

186

Herencia

una referencia a arreglos; es unicamente en la ultima dimensin (la del extremo o derecho) donde obtendremos ya un elemento del tipo dado para el arreglo.

6.2.4.

Los arreglos como parmetros o valor de regreso de un mtodo a e


En algunos otros lenguajes el paso de arreglos como parmetros es realmena te complicado. Afortunadamente, por el manejo de arreglos que tiene Java (de guardar las referencias nada ms) este aspecto es muy sencillo en este lenguaje. a Para pasar un arreglo como parmetro, unicamente hay que indicarle a Java el a nmero de dimensiones que deber tener el argumento, y el tipo de los elementos. u a Esto es necesario porque tiene que saber cuntos brincos tiene que dar, a travs de a e las listas de referencias, para encontrar a los elementos solicitados. Por ejemplo, en el Listado 6.6 en la pgina opuesta, en la l a nea 7: se indica que se van a pasar dos arreglos de enteros como argumentos, cada uno de ellos de dos dimensiones; mientras que en la l nea 22: (del mismo listado) se indica que los argumentos sern a dos arreglos de enteros, cada uno de una dimensin. No se puede especicar el o tamao de una dimensin en un parmetro, pues el tipo del parmetro tiene que n o a a ver nada ms con el tipo de los elementos y el nmero de dimensiones; lo que a u hay que especicar para cada parmetro de un mtodo es, unicamente, el tipo a e del parmetro. Lo mismo sucede con el valor que regresa un mtodo: unicamente a e hay que especicar el tipo del valor que regresa el mtodo, que en el caso de los e arreglos, repetimos una vez ms, consiste del tipo de los elementos y el nmero de a u dimensiones. El mtodo suma en la l e nea 7: regresa un arreglo de enteros de dos dimensiones, mientras que el mtodo con el mismo nombre suma de la l e nea 22: regresa un arreglo de enteros de una dimensin. o Cuando se pasa un arreglo como argumento (en el momento de invocar al mtodo) tenemos que pasar el nombre de lo que espera el mtodo. Por ejemplo, e e en el Listado 6.6 en la pgina opuesta, podemos ver las invocaciones a los mtodos a e que se llaman suma. La primer invocacin es en la l o nea 41: y se le pasan como argumentos los identicadores uno y dos, que estn declarados como arreglos de a enteros de dos dimensiones, que coincide con el tipo de los parmetros. a La otra invocacin est en la l o a nea 12:, dentro del mtodo cuya rma es sue ma(int[][],int[][]). En este caso le estamos pasando al mtodo dos arreglos de ene teros, cada uno de una dimensin. Como Java maneja los arreglos a travs de o e referencias y vectores de referencias (como ya vimos), se le pasa simplemente una de las referencias (de la primera dimensin) a uno de los arreglos de una dimeno sin. Cuando en Java declaramos una matriz, en realidad estamos construyendo o un arreglo en el que cada elemento es un arreglo; cuando declaramos un cubo, es-

6.2 Arreglos

187 tamos declarando un arreglo en el que cada elemento es un arreglo en el que cada elemento es un arreglo en el que cada elemento es un entero; y as sucesivamente.

Cdigo 6.6 Arreglos como parmetros y valor de regreso de una funcin o a o

1/2

1: p u b l i c c l a s s A r r e g l o s { 2: / 3: Suma d os a r r e g l o s de do s d i m e n s i o n e s . 4: @param Los a r r e g l o s 5: @ r e t u r n e l a r r e g l o que c o n t i e n e a l a suma 6: / 7: p u b l i c i n t [ ] [ ] suma ( i n t [ ] [ ] A , i n t [ ] [ ] B) { 8: i n t min1 = Math . min (A . l e n g t h , B . l e n g t h ) ; 9: i n t [ ] [ ] laSuma = new i n t [ min1 ] [ ] ; 10: / I n v o c a m o s a q u i e n s a b e sumar a r r e g l o s de una d i m e n s i n / o 11: f o r ( i n t i =0; i <min1 ; i ++) { 12: laSuma [ i ] = suma (A [ i ] , B [ i ] ) ; 13: } // end o f f o r ( ( i n t i =0; i <min1 ; i ++) 14: 15: r e t u r n laSuma ; 16: } // f i n suma ( i n t [ ] [ ] , i n t [ ] [ ] ) 17: / 18: Suma d o s a r r e g l o s de una d i m e n s i n o 19: @param Dos a r r e g l o s de una d i m e n s i n o 20: @ r e t u r n Un a r r e g l o con l a suma 21: / 22: p u b l i c i n t [ ] suma ( i n t [ ] A , i n t [ ] B) { 23: i n t tam = Math . min (A . l e n g t h , B . l e n g t h ) ; 24: 25: i n t [ ] r e s u l t = new i n t [ tam ] ; 26: f o r ( i n t i =0; i <tam ; i ++) { 27: r e s u l t [ i ] = A[ i ] + B[ i ] ; 28: } 29: return r e s u l t ; 30: } // f i n suma ( i n t [ ] , i n t [ ] ) 31: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 32: C o n s o l a c o n s = new C o n s o l a ( ) ; 33: / Los d o s a r r e g l o s a sumar / 34: i n t [ ] [ ] uno = { { 3 , 4 , 5 } , { 2 , 8 , 7 , 5 } , { 3 , 4 , 7 } } ; 35: i n t [ ] [ ] dos = { { 8 , 4 , 2 , 1 } , { 8 , 7 , 2 , 3 } , { 4 , 3 , 5 , 1 } } ; 36: / A r r e g l o p a r a g u a r d a r l a suma / 37: i n t [ ] [ ] miSuma ;

188

Herencia 2/2

Cdigo 6.6 Arreglos como parmetros y valor de regreso de una funcin o a o


38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: } / O b j e t o p a r a p o d e r u s a r l o s m todos / e A r r e g l o s p r u e b i t a = new A r r e g l o s ( ) ; / I n v o c a c i n a l a suma de d os m a t r i c e s / o miSuma = p r u e b i t a . suma ( uno , do s ) ; / I m p r e s i n de l o s r e s u l t a d o s / o f o r ( i n t i =0; i <miSuma . l e n g t h ; i ++) { f o r ( i n t j =0; j <miSuma [ i ] . l e n g t h ; j ++) { c o n s . i m p r i m e ( miSuma [ i ] [ j ]+"\t" ) ; } // end o f f o r ( i n t j =0; j <miSuma [ i ] . l e n g t h ; j ++) cons . imprimeln ( ) ; } // end o f f o r ( i n t i =0; i <miSuma . l e n g t h ; i ++) } // f i n main ( )

El resultado de la invocacin de esta clase se puede ver en la Figura 6.11. o

Figura 6.11

Ejecucin de la clase Arreglos o


11 10 7 8 15 7 7 9 12 8

En el listado anterior se pregunta por el tamao de cada arreglo que se va n a manejar para no abortar el programa por ndices invlidos (aborta dando el a mensaje ArrayindexOutOfBoundsException). Queda pendiente el uso de los arreglos. Cundo hay que construirlos y cundo no? Por ejemplo, en la l a a nea 9: se construye la primera dimensin, pero la segunda no, y en la l o nea 25: tambin se construye e el arreglo. Esto se debe a que en el primer caso el i-simo rengln aparece del e o lado izquierdo de una asignacin (l o nea 12:) y lo mismo sucede con cada uno de los elementos en la l nea 27: para los elementos del arreglo result. Sin embargo, en la l nea 12: no se dice cuntos elementos va a tener la segunda dimensin, porque a o cuando suma regrese la referencia a un arreglo de una dimensin, esta referencia o se copiar en el lugar correspondiente (el arreglo de referencias de la primera a dimensin). Lo mismo sucede en la l o nea 41:, donde al regresar el mtodo a un e arreglo de dos dimensiones, como lo que est regresando es una referencia, sta a e simplemente se copia. Con esto damos por terminado el tema de arreglos, que tienen un manejo muy uniforme y exible en Java.

6.3 Aspectos principales de la herencia

189

6.3 Aspectos principales de la herencia


Regresamos a hablar ms sobre la herencia en lenguajes orientados a objetos, a que es una de las caracter sticas ms importantes de esta clase de lenguajes de a programacin. o

6.3.1.

Super y subclases
Tenemos ya las herramientas necesarias para hacer las declaraciones de nuestras subclase, as que proseguiremos donde nos quedamos en la seccin 6.1 en la o pgina 167, que era en la declaracin de un arreglo para guardar las calicacioa o nes del estudiante. Tambin desarrollaremos el constructor de la subclase ver e listado 6.7. Cuando se crea un objeto cuyo tipo es una subclase, lo primero que hace el objeto es invocar al constructor sin parmetros de la superclase. Si no a hay tal porque hayamos declarado constructores con parmetros nada ms a a el constructor de la subclase deber llamar expl a citamente a algn constructor u de la superclase. Esto se hace con el identicador super seguido por la lista de argumentos entre parntesis. e Supongamos, por ejemplo, que el nmero de calicaciones que deseamos guaru dar para un estudiante dado depende del curso que est tomando. Entonces, la e creacin del arreglo de calicaciones se har en el constructor, como se puede ver o a en el listado 6.7.

Cdigo 6.7 Campos y constructor de EstudianteCurso o


1: c l a s s E s t u d i a n t e C u r s o extends E s t u d i a n t e B a s i c o { 2: float [ ] c a l i f s ; 3: / C o n s t r u c t o r . 4: @params S t r i n g nombre E l nombre d e l e s t u d i a n t e . 5: @params S t r i n g c u e n t a N mero de c u e n t a . u 6: @params S t r i n g c a r r e r a La c a r r e r a en l a que e s t i n s c r i t o . a 7: @params i n t n u m C a l i f s N mero de c a l i f i c a c i o n e s que vamos a u 8: guardar para e l e s t u d i a n t e . 9: / 10: p u b l i c E s t u d i a n t e C u r s o ( S t r i n g nombre , S t r i n g c u e n t a , 11: String carrera , int numCalifs ) { 12: super ( nombre , c u e n t a , c a r r e r a ) ; 13: c a l i f s = new f l o a t [ n u m C a l i f s ] ; 14: f o r ( i n t i =0; i < n u m C a l i f s ; i ++) c a l i f s [ i ] = 0 ; 15: }

190

Herencia

Una vez hecho y entendido el constructor, se agregan toda clase de mtodos e que manejan a la parte que se extendi en la subclase. La mayor de los mtoo a e dos nuevos unicamente manipulan al nuevo campo, por lo que no tienen nada merecedor de atencin ver listado 6.8. o

Cdigo 6.8 Mtodos nuevos para la subclase EstudianteCurso o e


16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: / R e g r e s a l a s c a l i f i c a c i o n e s que t i e n e e l e s t u d i a n t e . @ r e t u r n s i n t [ ] un a r r e g l o con l a s c a l i f i c a c i o n e s . / public float [ ] g e t C a l i f s () { return c a l i f s ; } / R e g r e s a una d e t e r m i n a d a c a l i f i c a c i n . o @params i n t i R e g r e s a r l a i s i m a c a l i f i c a c i n . e o @ r e t u r n s f l o a t c a l i f s [ i ] . / public float g e t C a l i f s ( int i ) { i f ( ( i >= 0 ) && ( i < c a l i f s . l e n g t h ) ) return c a l i f s [ i ] ; e l s e r e t u r n 1; } / Pone una d e t e r m i n a d a c a l i f i c a c i n . o @params i n t i E l l u g a r de l a c a l i f i c a c i n . o @params f l o a t x La c a l i f i c a c i n . o / public void s e t C a l i f ( i n t i , f l o a t x ) { i f ( i >= 0 && i < c a l i f s . l e n g t h ) { califs [ i ] = x; } } / Da e l p r o m e d i o d e l e s t u d i a n t e . @re t urns f l o a t El promedio / public f l o a t getPromedio () { f l o a t suma = 0 ; f o r ( i n t i = 0 ; i < c a l i f s . l e n g t h ; i ++) { suma += c a l i f s [ i ] ; } r e t u r n suma / c a l i f s . l e n g t h ; }

El unico mtodo interesante es getRegistro, ya que existe un mtodo con la e e misma rma en la superclase. El efecto de declarar un mtodo en la subclase con e la misma rma que el de la superclase es que, de hecho, se esconde el mtodo de e la superclase. Cualquier uso que se quiera dar a ese mtodo dentro de la subclase e tendr que ser identicado por el prejo super seguido de un punto obsrvese la a e

6.4 Polimorsmo

191

l nea 62 del listado 6.9.

Cdigo 6.9 Redenicin del mtodo getRegistro() o o e


48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: / V a ca e l r e g i s t r o . E s t e m todo u t i l i z a a l de l a s u p e r c l a s e e p a r a d a r l e p a r t e de l a cadena , y d e s p u s c o m p l e t a con l a s e calificaciones . @ r e t u r n s S t r i n g l a c a d e n a que c o n t i e n e a l r e g i s t r o / public String getRegistro () { S t r i n g c a d e n a = super . g e t R e g i s t r o ( ) ; f o r ( i n t i = 0 ; i < c a l i f s . l e n g t h ; i ++) { i f ( i % 5 == 0 ) c a d e n a += "\n" ; // Cambia de r e n g l n cada 5 o c a d e n a += c a l i f s [ i ] + "\t" ; } return cadena ; }

6.4 Polimorsmo
Una de las grandes ventajas que nos ofrece la herencia es el poder manipular subclases a travs de las superclases, sin que sepamos concretamente de cul e a subclase se trata. Supongamos, para poner un ejemplo, que declaramos otra subclase de EstudianteBasico que se llama EstudianteBiblio y que de manera similar a como lo hicimos con EstudianteCurso extendemos adecuadamente y redenimos nuevamente el mtodo getRegistro(). Podr e amos tener el cdigo que sigue: o
1: 2: 3: 4: EstudianteBasico [ ] estudiantes = { new E s t u d i a n t e C u r s o ( " Pedro ,. . . ) new EstudianteBiblio (. . . ) };

En estas l neas declaramos un arreglo con dos elementos del tipo EstudianteBasico, donde el primero contiene a un EstudianteCurso mientras que el segundo contiene a un EstudianteBiblio. Como las subclases contienen todo lo que contiene la superclase, y como lo que guardamos son referencias, podemos guardar en un arreglo de la superclase elementos de las subclases. Es ms; si hacemos la siguiente a solicitud
S t r i n g cadena = e s t u d i a n t e s [ 0 ] . g e t R e g i s t r o ( ) ;

192

Herencia

se da cuenta de que lo que tiene ah es una referencia a algo del tipo EstudianteCur so, por lo que utilizar la implementacin dada en esa subclase para dar respuesta a o a esta solicitud. Decimos que gobierna el tipo de la referencia, no el tipo de la declaracin. La decisin de a cul de las implementaciones de getRegistro() debe o o a ser invocada se toma en ejecucin, ya que depende de la secuencia de ejecucin o o el tipo de la referencia guardada en la localidad del arreglo. A esta capacidad de los lenguajes orientados a objetos de resolver dinmicamente el signicado de un a nombre de mtodo es a lo que se llama polimorsmo, ya que el mismo nombre (de e hecho, la misma rma) puede tomar signicados distintos dependiendo del estado del programa.

El operador instanceof
Supongamos que estamos en la seccin escolar de la Facultad, donde tienen o registros de alumnos de distintos tipos, y que le piden al coordinador que por favor le entregue una lista de todos los registros que tiene para la biblioteca. El coordinador deber tener un programa que identique de qu clase es cada uno de a e los objetos que se encuentran en la lista (arreglo, lista ligada, etc.). El operador instanceof hace exactamente esto. Es un operador binario, donde las expresiones que lo usan toman la siguiente forma: xexpresin de objetoy instanceof xidenticador de clasey o y regresa el valor booleano verdadero si, en efecto, el objeto dado en la expresin o de la izquierda es un ejemplar de la clase dada a la derecha; y falso si no. Para un proceso como el que acabamos de describir tendr amos un cdigo como el que o sigue, suponiendo que tenemos los registros en una lista, como se puede observar en el listado 6.10.

Cdigo 6.10 Registros en un arreglo o


EstudianteBasico [ ] estudiantes ; / Ac s e i n i c i a l i z a e l a r r e g l o y s e van a g r e g a n d o l o s a estudiantes . / int i ; String reporte ; f o r ( i = 0 , r e p o r t e = "" ; i < e s t u d i a n t e s . l e n g t h ; i ++) { i f ( e s t u di ant e s [ i ] instanceof E s t u d i a n t e B i b l i o ) { r e p o r t e += e s t u d i a n t e s [ i ] . g e t R e g i s t r o ( ) ; } } ...

6.5 Clases abstractas

193

Supongamos ahora que queremos una lista de estudiantes con su calicacin o nal del curso, pero sin las calicaciones parciales. El mtodo getRegistro no sae tisface esto. Tendr amos un cdigo similar al anterior en el listado 6.11. o

Cdigo 6.11 Otra versin del mtodo getRegistro o o e


EstudianteBasico [ ] estudiantes ; / Ac s e i n i c i a l i z a e l a r r e g l o y s e van a g r e g a n d o l o s a estudiantes . / int i ; String reporte ; f o r ( i = 0 , r e p o r t e = "" ; i < e s t u d i a n t e s . l e n g t h ; i ++) { i f ( e s t u di ant e s [ i ] instanceof EstudianteCurso ) { / Ac hay p r o b l e m a s , p u e s queremos u n i c a m e n t e e l a nombre d e l e s t u d i a n t e y s u p r o m e d i o ! E l nombre y s u s d a t o s , p e r o no hay manera de a c c e d e r a l m todo e que c o r r e s p o n d e a l a s u p e r c l a s e . Por l o t a n t o hay que a r m a r l o a p i e : / r e p o r t e += e s t u d i a n t e s [ i ] . getNombre ( ) + "\t" + (( EstudianteCurso ). estudiantes [ i ] ) . getPromedio ( ) ; } } ...

El casting que se hizo en la ultima l nea es necesario, porque en el momento de compilacin no se sabe la subclase que corresponder al elemento en cuestin, por o a o lo que en ejecucin se deber pedir que se interprete el registro de esa manera. o a Si no lo puede hacer, porque se no sea el caso, habr un error de ejecucin de e a o InvalidClassException. En general, cuando tenemos una declaracin asociada con una superclase y o queremos hacer uso de mtodos o atributos declarados para la subclase (desde un e programa principal u otra clase que no est relacionada jerrquicamente con la e a super y subclase) tendremos que hacer un casting con el nombre de la subclase para que en tiempo de compilacin identique que estamos hablando de la subclase. o

194

Herencia

6.5 Clases abstractas


Supongamos que tenemos una jerarqu de clases para las guras geomtricas, a e que se podr ver como en la gura 6.12. a

Figura 6.12

Jerarqu de clases a
Figura geomtrica e

L nea

Supercie

Volumen

recta

elipse

parbola a

cubo

icosaedro cilindro

cuadrado

c rculo

rectngulo a

La superclase es Figura Geomtrica y reconocemos que toda gura geomtrica e e debe tener los siguientes servicios: dibujarse, moverse, borrarse, crearse. En el caso de las l neas podemos obtener su longitud. En el caso de las supercies, podemos obtener su per metro y su rea. En el caso de los volmenes, a u podemos querer obtener su volumen. Por ejemplo, en el caso de las supercies podemos tener una lista de puntos que las denen. Y as sucesivamente. Cuando estamos en la ra del rbol realmente todo lo que podemos decir es z a que se trata de una gura geomtrica. Se trata de una clase abstracta de la que e no podemos decir mucho todav y de la que no puede haber objetos que sean, a en abstracto, una gura geomtrica. Conforme vamos bajando por el rbol hacia e a las hojas se van concretando algunas de las caracter sticas, pero en el caso de la jerarqu que nos ocupa, en el segundo nivel todav no podemos tener objetos que a a sean una supercie si no decimos qu clase de supercie es. En la gura 6.12 los e nodos ovalados representan clases abstractas, mientras que los nodos rectangulares representan clases concretas.

6.5 Clases abstractas

195

En una jerarqu cualquiera de clases tenemos las mismas caracter a sticas que hemos visto hasta ahora, donde cada subclase hereda todo lo heredable de la superclase, excepto que para uno o ms mtodos de la clase no damos su implea e mentacin. Si una clase tiene al menos un mtodo sin implementacin la clase es o e o abstracta y se tiene que indicar eso como se indica a continuacin: o Sintaxis: xencabezado de clase abstractay ::= abstract class xidenticadory Semantica: Le estamos indicando al compilador dos cosas: No pueden crearse objetos de esta clase. Contiene al menos un mtodo cuya implementacin no est denida. e o a Aquellos mtodos que no se puede denir su implementacin en el nivel de la e o jerarqu en la que est la clase, se les precede tambin de la palabra abstract y se a a e escribe unicamente su encabezado seguido de un ;. En el listado 6.12 podemos ver un ejemplo con la jerarqu de clases que dimos antes. No pondremos las clases a completas porque no es el objetivo en este momento.

Cdigo 6.12 Clases abstractas y concretas o


/ R az de l a j e r a r q u a . C l a s e a b s t r a c t a / abstract class FiguraGeometrica { / E s q u i n a i n f e r i o r i z q u i e r d a / i n t x1 , y1 ; abstract public void p i n t a ( ) ; a b s t r a c t p u b l i c v o i d mueve ( i n t x2 , i n t y2 ) ; a b s t r a c t p u b l i c v o i d c o p i a ( i n t x2 , i n t y2 ) ; ...... } / S i g u i e n t e n i v e l de l a j e r a r q u a . Tambi n a b s t r a c t a / e a b s t r a c t c l a s s L i n e a extends F i g u r a G e o m e t r i c a { abstract public void l o n g i t u d ( ) ; ...... } ...... / T e r c e r n i v e l de l a j e r a r q u a . C l a s e c o n c r e t a / c l a s s R e c t a extends L i n e a { public void p i n t a ( ) { ... } ... } ...

196

Herencia

No hay la obligacin de que todos los mtodos en una clase abstracta sean o e abstractos. Depender del diseo y de las posibilidades que tenga la superclase a n para denir algunos mtodos para aquellas clases que hereden. An cuando la e u clase no tenga mtodos abstractos, si queremos que no se creen objetos de esa e clase la declaramos como abstracta.

6.6 Interfaces
La herencia en Java es simple, esto es, cada clase puede heredar de a lo ms a una superclase. Pero muchas veces necesitamos que hereden de ms de una clase. a Las interfaces corresponden a un tipo, como las clases, que denen exclusivamente comportamiento. Lo unico que pueden tener las interfaces son constantes estticas a y mtodos abstractos, por lo que denen el contrato que se establece con aquellas e clases que implementen esa interfaz. Se dice que una clase implementa una interfaz, porque cuando una clase hereda de una interfaz debe dar la implementacin de los o mtodos declarados en la interfaz. La sintaxis para la declaracin de una interfaz e o es: Sintaxis: xencabezado de interfazy ::= interface xidenticadory ... Semantica: Se declara un tipo que corresponde a constantes y encabezados de mtodos. e Como todo lo que se declare dentro de una interfaz es pblico, pues corresponde u siempre a lo que puede hacer un objeto, no se usa el calicativo public en los enunciados dentro de la declaracin de la interfaz. Como forzosamente todos los o mtodos son abstractos, ya que no tienen implementacin, tampoco se pone este e o calicativo frente a cada mtodo. Y como slo se permiten constantes estticas, los e o a calicativos de static y nal se omiten en las declaraciones de constantes dentro de una interfaz. Por lo tanto, las constantes y mtodos en una interfaz sern e a declarados nada ms con el tipo de la constante o el tipo de valor que regrese el a mtodo, los identicadores y, en el caso de las constantes el valor; en el caso de e los mtodos, los parmetros. e a Pensemos en el comportamiento de una lista, como las que hemos estado vien-

6.6 Interfaces

197 do. Sabemos que no importa de qu sea la lista, tiene que tener un mtodo que e e agregue, uno que busque, etc. Dependiendo de los objetos en la lista y de la implementacin particular, la implementacin de cada uno de los mtodos puede variar. o o e Tenemos ac un caso perfecto para una interfaz. En el listado 6.13 podemos ver a la declaracin de una interfaz de este tipo. o

Cdigo 6.13 Interfaz para manejar una lista o


1: 2: 3: 4: 5: 6: 7: 8: interfaz Lista { void agrega ( Object obj ) ; boolean q u i t a ( S t r i n g cad ) ; O b j e c t b u s c a ( i n t c u a l , S t r i n g cad ) ; String armaRegistro ( ) ; void l i s t a T o d o s ( Consola cons ) ; v o i d l o s Q u e C a z a n ( i n t c u a l , S t r i n g cad ) ; }

Como se ve en el listado 6.13 ninguno de los mtodos tiene su implementacin. e o Cualquier clase que implemente a esta interfaz tiene que dar la implementacin o de todos y cada uno de los mtodos dados en esta declaracin. Por ejemplo, si e o deseamos una Lista de EstudianteCurso podr amos tener declaraciones como las que se ven en el listado 6.14.

Cdigo 6.14 Herencia con una interfaz o


1: 2: 3: 4: 5: 6: 7: 8: c l a s s L i s t a C u r s o implements L i s t a { EstudianteCurso cabeza ; ... O b j e c t b u s c a ( i n t c u a l , S t r i n g cad ) ... } ... }

Dado que las interfaces corresponden a tipos, igual que las clases, podemos declarar variables de estos tipos. Por ejemplo,
Lista miLista ;

y usarse en cualquier lugar en que se pueda usar un objeto de una clase que implementa a la interfaz Lista, de la misma manera que se puede usar una variable de la clase Object en cualquier lugar de cualquier objeto. Sin embargo, si deseamos

198

Herencia

que el objeto sea visto como la subclase, tendremos que aplicar lo que se conoce como casting, que obliga al objeto a comportarse como de la subclase. Se logra poniendo el tipo al que deseamos conformar al objeto de tipo Object entre parntesis, precediendo a la variable: e
E s t u d i a n t e C u r s o nuevo = ( E s t u d i a n t e C u r s o ) b u s c a ( 1 , " Pedro " ) ;

El mtodo busca nos regresa un objeto de tipo Object (la referencia), pero e sabemos, por la implementacin de busca que en realidad nos va a regresar un o una referencia a un objeto de tipo EstudianteCurso, por lo que podemos aplicar el casting. Una clase dada puede extender a una sola superclase, pero puede implementar a tantas interfaces como queramos. Como no tenemos la implementacin de los o mtodos en las interfaces, an cuando una misma rma aparezca en ms de una e u a interfaz (o inclusive en la superclase) la implementacin que se va a elegir es la o que aparezca en la clase, por lo que no se presentan conictos. Las interfaces tambin pueden extender a una o ms interfaces de la misma e a manera que subclases extienden a superclases. La sintaxis es la misma que con la extensin de clases: o Sintaxis: interface xidentif1 y extends xidentif2 y, . . . , xidentifn y Semantica: De la misma manera que con las clases, la subinterfaz hereda todos los mtodos de las superinterfaces. e En adelante se har un uso ms intenso de herencia y seguiremos, como hasta a a ahora, insistiendo en el uso de interfaces.

Administracin de o la memoria durante ejecucin o

7.1 El stack y el heap


Revisitaremos el tema asignacin de espacio durante la ejecucin del programa, o o pues hay varios grandes detalles a los que no les hemos dado la importancia adecuada. Empecemos por lo que sucede en la memoria de la mquina durante a la ejecucin del programa y para ello veamos el concepto de la estructura de o bloques de Java y qu sucede con ella durante ejecucin. La estructura de bloques e o sintctica (estticamente) tiene la forma que se ve en la gura 7.1 en la siguiente a a pgina. Un archivo de Java puede tener una o ms clases declaradas. Hasta ahora a a hemos hablado extensamente de los campos que se declaran en la clase y cmo son o accesibles desde cualquier mtodo de la misma. Tambin hemos visto el papel que e e juegan los parmetros en los mtodos de las clases, que corresponde a variables a e locales de las mismas. Adicionalmente a los parmetros tenemos las variables a declaradas dentro de un mtodo, a las que unicamente dentro de ese mtodo, e e al igual que los parmetros, se tiene acceso de ah viene el nombre de locales. a

200

Administracin de la memoria durante ejecucin o o

Adicionalmente a esto, cuando se abren bloques de enunciados en las condicionales o en las iteraciones se pueden declarar variables que unicamente son locales dentro de ese bloque de enunciados. Slo son conocidas dentro del bloque1 . o Esto nos dene dos niveles lexicogrcos distintos: el global, que se reere a a lo que se puede utilizar desde cualquier punto de cualquiera de nuestras clases, dados los permisos adecuados, y el local, que es aquello que se encuentra dentro de un mtodo. Adicionalmente, dentro de los mtodos podemos tener bloques e e de enunciados que incluyan declaraciones. Estas declaraciones son unicamente 2 visibles dentro del bloque de instrucciones .

Figura 7.1

Estructura de bloques de un programa. Clase atributos


mtodo e

mtodo e bloque bloque

mtodo e bloque mtodo e bloque bloque

Sin embargo, si existe alguna declaracin de una variable con el mismo nombre fuera del o bloque y que la precede, el compilador dar error de sintaxis por identicador ya declarado. a 2 No pueden tener el mismo identicador que una declaracin previa y fuera del bloque. o

7.1 El stack y el heap

201

Para los mtodos estticos, como es el caso del mtodo main de las clases esto e a e funciona un poco distinto, ya que este tipo de mtodos no tiene acceso ms que a e a los atributos o mtodos estticos de la misma clase, y a los mtodos o atributos e a e pblicos o de paquete de las clases a las que se tenga acceso. Olvidndonos un poco u a de los mtodos estticos (de clase) podemos decir que la estructura de bloques e a nos da lo que conocemos como el rango de una variable, que se reere a aquellos puntos de la clase donde la variable puede ser utilizada. Si regresamos a nuestro esquema de los espejos/cristales, tenemos que desde dentro de un bloque podemos ver hacia afuera: desde el nivel local tenemos acceso a las variables de la clase - el rango de las variables de la clase es toda la clase. Desde dentro de un mtodo, sin e embargo, no podemos ver lo que est declarado dentro de otro mtodo o bloques. a e Pero, qu pasa cuando el nombre de una variable de clase se repite dentro de e un bloque como parmetro o variable local? En este caso, decimos que la variable a local bloquea a la variable global: si existe una variable local con el mismo nombre, todas las referencias a esa variable en ese bloque se reeren a la variable local. El compilador utiliza a la variable que le queda ms cerca, siempre y cuando tenga a acceso a ella, la pueda ver. El rango de una variable es esttico, pues est denido por la estructura del a a programa: de ver el listado podemos decir cul es el rango de una variable dada, y a denirlo como pblico (global, pero atado a un objeto) o local (declarado dentro u de un mtodo o como privado para una clase). Es el compilador el que se encarga e de resolver todo lo relacionado con el rango de las variables, siguiendo la estructura de bloques. Durante la ejecucin del programa, esta estructura presenta un cierto anidao miento, distinto del anidamiento sintctico o lexicogrco, donde desde dentro a a de un mtodo se puede llamar a cualquiera de los que est en rango, o sea, cuale a quiera de los mtodos declarados en la clase o que sean pblicos de otras clases. e u Se lleva a cabo una anidamiento dinmico durante ejecucin, donde se establece a o una cadena de llamadas tal que el ultimo mtodo que se llam es el primero que e o se va a abandonar. Supongamos que tenemos la clase del listado 7.1. En el esquema de la gura 7.2 en la pgina 203 tenemos los bloques en el orden a en que son invocados, mostrando el anidamiento en las llamadas. El nombre del mtodo junto con el valor de sus parmetros se encuentra sobre la l e a nea que lo demarca. En la esquina superior derecha de cada bloque se encuentra el nivel de anidamiento dinmico que tiene cada mtodo. El valor de las variables, tanto de los a e atributos como de las variables locales, depender del anidamiento lexicogrco. a a Por ejemplo, dentro del mtodo B en el que se le asigna valor a una variable e entera a, sta es una variable local, por lo que el valor del atributo a no se va a e ver modicado. Por ello, en la llamada de la l nea 29: al mtodo B, los valores con e

202

Administracin de la memoria durante ejecucin o o

los que es llamado son los originales de a y b, o sea 3 y 2.

Cdigo 7.1 Clase que ilustra el anidamiento dinmico o a


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: class Cualquiera {

private int a = 3 , b = 2; ... p u b l i c v o i d A( i n t i ) { ... B( i , a ) ; ... } ... p u b l i c v o i d B( i n t i , i n t j ) int a = i + j ; ... C(); ... } public void C( ) { int k = 2 a ; ... } p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { i n t m = 10 , n = 5 ; C u a l q u i e r a o b j e t o = new C u a l q u i e r a ( ) ; o b j e t o . A(m) ; o b j e t o . B( a , b ) ; objeto .C( ) ; } }

Al esquema de bloques le sigue un diagrama de Warnier en la gura 7.3 en la pgina opuesta que nos muestra la relacin entre las llamadas. a o

7.1 El stack y el heap

203

Figura 7.2

Diagrama de anidamiento dinmico. a


main Constructor de objeto A(10) B(10,3) [3] [1] [2] [2]

C( )

[4]

B(3,2)

C( )

[3]

[2]

C( )

[2]

Figura 7.3

Secuencia de llamadas en el listado 7.1.


6 9Construccin de objeto o 9 9 6 9 9 9 9. . . 9 9 9 9 6 9 9 9 9 9 9 9. . . 9 8 8 9 9 9Llamada a A(10) Llamada a B(10,3) Llamada a C() 9 9 9 9 9 9 9 7 8 9 ... 9 Secuencia de 9 9 7 ... Ejecucin 9 o 9 6 9 9 9 9. . . 9 8 9 9 9Llamada a B(3,2) Llamada a C() 9 9 9 9 9 7 9 9 ... 9 3 9 9 9 7Llamada a C() . . .

Lo que me indican los dos diagramas anteriores, es que la ejecucin del proo grama debe proseguir de la siguiente manera:

204

Administracin de la memoria durante ejecucin o o

1. . . Entrar a ejecutar main. 2. . . Entrar a ejecutar el constructor de Cualquiera. 2. . . Salir de ejecutar el constructor de Cualquiera. 2. . . Entrar a ejecutar el mtodo A(10). e 3. . . Entrar a ejecutar el mtodo B(10,3). e 4. . . Entrar a ejecutar el mtodo C(). e 4. . . Salir de ejecutar el mtodo C(). e 3. . . Salir ejecutar el mtodo B(10,3). e 2. . . Salir de ejecutar el mtodo A(10). e 2. . . Entrar a ejecutar el mtodo B(3,2). e 3. . . Entrar a ejecutar el mtodo C(). e 3. . . Salir de ejecutar el mtodo C(). e 2. . . Salir ejecutar el mtodo B(3,2). e 2. . . Entrar a ejecutar el mtodo C(). e 2. . . Salir de ejecutar el mtodo C(). e 1. . . Salir de ejecutar main. Tanto en el esquema como en la secuencia de ejecucin (donde omitimos para o cada funcin la ejecucin de lo que no fuera llamadas a mtodos), asociamos o o e un entero a cada llamada. Esto es con el objeto de identicar los anidamientos dinmicos - los que estn denidos por la secuencia de ejecucin del programa. Este a a o esquema muestra varios aspectos importantes que tienen que ver con la ejecucin o de un programa. Revisemos algunos de ellos: 1. El anidamiento dinmico (en ejecucin) no forzosamente coincide con el a o esttico (sintctico). Mientras que lexicogrcamente hablando unicamena a a te tenemos el nivel global y el local, dinmicamente podemos tener tantos a niveles como queramos, uno por cada vez que desde dentro de una funcin o llamamos a otra. 2. La ultima rutina a la que entramos es la primera de la que salimos. 3. Cuando aparece una funcin f como argumento de una funcin g, la llamada o o a f se hace inicia y termina antes que la llamada a g. Para poder llamar a g debemos tener el valor de sus argumentos, por lo que es necesario que antes de entrar a g obtengamos el valor de f. 4. El nivel dinmico que le corresponde a una funcin f que aparece como a o argumento de una funcin g es el mismo que el de la funcin g. o o

7.1 El stack y el heap

205

Para poder hacer esto, la ejecucin del programa se lleva a cabo en la memoria o de la mquina, organizada sta como un stack, que es una estructura de datos con a e las siguientes caracter sticas: a) Respecto a su estructura: La estructura es lineal, esto es, podemos pensarla con sus elementos formados uno detrs del otro. a Es una estructura homognea, donde todos sus elementos son del mismo e tipo. Es una estructura dinmica, esto es, crece y se achica durante ejecucin. a o Tiene asociado un tope, que corresponde al ultimo elemento que se coloc en o el stack. b) Respecto a su uso: Un stack empieza siempre vac sin elementos. o, Conforme progresa la ejecucin, se van colocando elementos en el stack o y se van quitando elementos del stack, siguiendo siempre esta regla: los elementos se colocan siempre en el tope del stack y cuando se remueven, se hace tambin del tope del stack. e Veamos un esquema de un stack en la gura 7.4.

Figura 7.4

Esquema de una stack o pila.


tope ... crece hacia arriba ... ... ... ... ... ... ... ... ...

stack o pila

El tope del stack corresponde a un apuntador que me indica cul es el siguiente a lugar en el que se van a colocar datos en el stack. Suponiendo que la primera posicin a ocupar en un stack es la 0, si el tope vale 0 quiere decir que el stack o est vac a o. Para poder ejecutar un programa, el sistema cuenta con un contador de programa (Program Counter:PC ) que apunta a (contiene la direccin de) la siguiente o

206

Administracin de la memoria durante ejecucin o o

instruccin a ejecutarse. El cdigo del programa se encuentra en una seccin de o o o memoria, y las variables y la ejecucin se hace sobre el stack. Los objetos se eno cuentran en el heap. El algoritmo para ejecutar un programa se encuentra en la gura 7.5.

Figura 7.5

Algoritmo para ejecutar un programa.


6 6 9 9Toma la siguiente instruccin o 9 9 9 9 9 9 9 9Obtn los operandos e 9 9 9 9 9 9 9 9 9 9Incrementa el contador del programa 9 9 9 9 6 9 9 9 9 9 9 9Suma 9 9 9 9 Ejecuta instrucciones 9 9 9 9 9 8 8 9 9 Ejecuta 9 9 (Hasta que encuentres 9 9Resta programa 9 9 9 9 9 8 9 9 la de parar) 9 9 9 9Ejecuta la instruccin 9 9 o 9 9 9 9 9 9 9 9 9 9 9 . 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 . 9 9 9 9 9 9 9 9 9 7 7 7

Antes de empezar a ejecutar un programa, el sistema operativo debe cargar en el stack de ejecucin todo lo que corresponde a lo que est accesible para la o a clase que se va a ejecutar, que incluye los nombres de las clases accesibles y las variables y mtodos de la clase que se va a ejecutar. A esto le llamamos el paso 0 e en la ejecucin de un programa. o Cuando se est ejecutando un programa un mtodo se puede invocar desde a e distintos puntos del programa. En el punto de llamada de un mtodo la ejecucin e o debe transferirse a ejecutar ese mtodo, y una vez terminada le ejecucin del e o mismo, regresar al punto desde donde se hizo la invocacin, para continuar con o la ejecucin del programa. A la posicin en la que se encuentra la llamada se le o o conoce como punto de llamada e indica el punto al que debe regresar la ejecucin o del programa una vez que termine la funcin. Esta ultima caracter o stica hace que se le utilice como direccin de regreso. La ejecucin del programa, como ya o o mencionamos, se lleva a cabo en el stack. Cada vez que se invoca a un mtodo e el programa principal main es una funcin invocada por el sistema operativo - se o tiene que montar al mtodo en el stack, anotando muy claramente a donde debe e regresar la ejecucin al terminar la rutina. Al terminar la ejecucin del mtodo, o o e

7.1 El stack y el heap

207

se desmonta del stack al mismo. Para montar un mtodo al stack hay que e construir lo que se conoce como su registro de activacin, que es una tabla en o la que hay lugar para los parmetros y las variables locales del mtodo, de tal a e manera que durante la ejecucin se encuentren siempre en la parte superior del o stack. Al invocar un mtodo (para transferirse a ejecutarlo), el sistema debe realizar e los siguientes pasos: 1. Dejar un lugar en el stack para que el mtodo coloque ah el valor que va a e regresar, si es que regresa valor. 2. Hacer la marca en el stack, copiando ah el contenido del contador del pro grama. 3. Buscar en el stack, en el registro de activacin global, la direccin de cdigo o o o donde se encuentra denida ese mtodo. Copiar esa direccin al contador e o del programa (es donde va a continuar la ejecucin cuando se termine de o montar al mtodo en el stack). e 4. Construir el registro de activacin del mtodo, dejando un lugar para cada o e parmetro, en el orden en que estn declarados, y un lugar para cada variable a a local (o estructura de datos pblica o privada, si se trata de una clase). u 5. Evaluar los argumentos, para entregarle al mtodo una lista de valores e e irlos colocando en el registro de activacin. o 6. Copiar al stack, en el orden en que aparece, el registro de activacin (el o ultimo es el que queda en el tope del stack). 7. Copiar los valores de los argumentos a los parmetros. a 8. Conforme se va ejecutando el mtodo, se van colocando en el stack las vae riables y objetos que se van declarando. Un mtodo tiene acceso a su bloque local (lo que se encuentra a partir de la e ultima marca en el stack) y al bloque global (lo que se encuentra en la base o fondo del stack y hasta la primera marca en el stack). En la base del stack se encuentran los identicadores de las clases a las que se tiene acceso desde la clase en ejecucin. o Cuando termina la ejecucin del mtodo, el control debe regresar al punto o e de llamada. La ejecucin del mtodo termina cuando se llega a un enunciado de o e return o bien se llega al nal del bloque que dene al mtodo. Antes de continuar e la ejecucin en el punto de llamada, el sistema tiene que hacer lo siguiente: o

208

Administracin de la memoria durante ejecucin o o

1. Localizar la marca del stack ms cercana al tope, la ultima que se coloc. a o 2. Colocar en el contador del programa la direccin de regreso que se encuentra o en esa marca. 3. Si el enunciado que causa la terminacin de la rutina es un return, colocar o el valor en el lugar inmediatamente abajo de la marca del stack correspondiente. 4. Quitar del stack todo lo que se encuentra a partir de la marca, incluyndola. e Se quita algo del stack simplemente bajando el apuntador al tope del stack a que apunte al ultimo registro que se quit (recordar que lo ultimo o que se coloc es lo primero que se quita). No hay necesidad de borrar la o informacin pues la ejecucin solo va a tomar en cuenta aquella informacin o o o que se encuentre antes del tope del stack. 5. Contina la ejecucin en el lugar del cdigo al que apunta el contador del u o o programa. Para ilustrar estos pasos, vamos a seguir el programa que escribimos, y que tiene los renglones numerados. Por supuesto que la ejecucin del programa no o se lleva a cabo directamente sobre el texto fuente. El compilador y ligador del programa producen un programa en binario (lenguaje de mquina) que se coloca a en un cierto segmento de la memoria. El contador del programa va apuntando a direcciones de este segmento de memoria y en cada momento apunta a una instruccin de mquina. Es suciente para nuestros propsitos manejar el programa o a o a nivel de enunciado. En los esquemas del stack que presentamos a continuacin, o lo que corresponde a direcciones en memoria de datos (el stack) se preceden con un * mientras que lo que corresponde a memoria de programa se precede con un #. El contador del programa apunta a la siguiente instruccin a ejecutarse. o El tope del stack apunta al primer lugar vac en el stack (si el tope del stack o contiene un 0, quiere decir que no hay nadie en el stack). Al ejecutar el paso 0, se cargan al stack todos los atributos y nombres de mtodos de la clase3 , quedando el stack como se observa en la gura 7.6. e El sistema operativo sabe que el primer mtodo a ejecutarse es main, por lo e que inicia la ejecucin con l. Sigamos los pasos, uno a uno, para ver como lo hace: o e 1. Como main no entrega valor, no deja espacio en el stack. 2. Marca el stack para poder montar al mtodo. e
3 No ilustraremos las clases a las que tiene acceso para ahorrar espacio, y porque ser prctia a camente interminable.

7.1 El stack y el heap

209

Figura 7.6

Estado del stack al iniciarse la ejecucin de una clase. o

xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

#25: #19: #12: #5: *2 *3 d.r. Sistema Operativo clase Cualquiera

3. Localiza la direccin de main en el stack, y ve que es la direccin de cdigo o o o 24. El stack y los contadores quedan como se ve en la gura 7.7.

Figura 7.7

El stack al iniciarse la llamada a main.


d.r. Sistema Operativo main #25: #19: #12: #5: *2 *3
d.r. Sistema Operativo clase Cualquiera

25: Contador del Programa

xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

4. Construye el registro de activacin para main. El registro de activacin conso o truido se puede ver en la gura 7.8 en la siguiente pgina. En el registro se a

210

Administracin de la memoria durante ejecucin o o

va dando lugar para cada una de las declaraciones locales. En el caso de declaraciones de objetos, se colocan en el stack las referencias a los objetos que se van a localizar en el heap.

Figura 7.8

Registro de activacin para main. o

xCualquieray objeto xinty n xinty m xStringr sy args

# heap *5 *10 # heap

5 y 6 Se monta el registro de activacin en el stack. El stack queda como se ve en o la gura 7.9.

Figura 7.9

El stack listo para iniciar la ejecucin de main. o

xCualquieray objeto xinty n xinty m xStringr sy args xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

#heap *5 *10 #heap d.r. Sistema Operativo main #25: #19: #12: #5: *2 *3
d.r. Sistema Operativo clase Cualquiera

25: Contador del Programa

7.1 El stack y el heap

211

Una vez armado el stack, se procede a ejecutar la rutina. En este momento es accesible todo lo que corresponde a las variables y mtodos pblicos de las clases e u a las que se tiene acceso, a travs de los objetos construidos, y lo que est desde e a la ultima marca hasta el tope del stack. 7. Empieza la ejecucin en el enunciado #25:, con las declaraciones locales de o main ya montadas en el stack. Las l neas de cdigo 26: y 27: corresponden a las declaraciones que ya hicio mos, as que procedemos a ejecutar la l nea 28:. Para ello debemos invocar el mtodo A del objeto objeto. Volvamos a seguir la ejecucin, en lo que se e o reere al manejo del stack. 1 a 3: Como el mtodo no regresa valor, no dejamos un lugar en el stae ck. Ponemos la marca, colocando en ella la direccin de cdigo que se o o encuentre en el contador del programa. Asimismo, se coloca en el contador del programa la direccin del mtodo. Todo esto se puede ver en o e la gura 7.10 en la siguiente pgina. a

Figura 7.10

El stack durante la ejecucin de main. o

xCualquieray objeto xinty n xinty m xStringr sy args

direccin de o regreso: #29: A(10)

#heap *5 *10 #heap d.r. Sistema Operativo main #5: Contador del Programa

4 a 6 Al evaluar los argumentos, tenemos la lista (10). Montamos en el stack el registro de activacin y colocamos los valores de la lista en el o espacio reservado para los argumentos. El contenido del stack en este momento se puede ver en la gura 7.11. 8. Continuar la ejecucin del programa en la l o nea de cdigo #5:. o

212

Administracin de la memoria durante ejecucin o o

Figura 7.11

El stack durante la ejecucin de A. o

xinty i xCualquieray objeto xinty n xinty m xStringr sy args

*10
direccin de o regreso: #29: A(10)

#heap *5 *10 #heap d.r. Sistema Operativo main #5: Contador del Programa

En la l nea #8: tenemos una llamada al mtodo B(i,a), por lo que nuevamente e marcamos el stack, copiamos la direccin del PC a la marca, armamos el registro o de activacin para B y lo montamos en el stack, colocamos la direccin donde o o empieza B a ejecutarse en el PC y proseguimos la ejecucin en ese punto. En el o momento inmediato anterior a que se ejecute B, el stack se presenta como se puede observar en la gura 7.12. Al llegar a la l nea de cdigo #16: hay una llamada desde B al mtodo C, por o e lo que nuevamente se marca el stack, se actualiza el contador del programa y se monta en el stack el registro de activacin de C(). El resultado de estas acciones o se pueden ver en la gura 7.13 en la pgina 214. a Se ejecuta el mtodo C() y al llegar al nal del mismo el sistema desmonta e el registro de activacin de C() del stack, coloca en el contador del programa o la direccin que se encuentra en la marca y elimina la marca puesta por esta o invocacin. El stack se ve como en la gura 7.14 en la pgina opuesta. o a Se termina la ejecucin de B(10,3) en la l o nea #18:, por lo que se desmonta el registro de activacin de b(10,3) del stack, se copia la direccin de regreso de la o o marca al PC y se quita la marca del stack, quedando el stack como se muestra en la gura 7.15.

7.1 El stack y el heap

213

Figura 7.12

El stack antes de empezar a ejecutar B.

xinty a xinty j xinty i xinty i xCualquieray objeto xinty n xinty m xStringr sy args xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

*13 *3 *10
direccin de o regreso: #9: B(10,3)

*10
direccin de o regreso: #29: A(10)

#heap *5 *10 #heap d.r. Sistema Operativo main #25: #19: #12: #5: *2 *3
d.r. Sistema Operativo clase Cualquiera

#14: Contador del Programa

214

Administracin de la memoria durante ejecucin o o

Figura 7.13

El stack antes de empezar a ejecutar C desde la l nea #16:.

xinty k xinty a xinty j xinty i xinty i xCualquieray objeto xinty n xinty m xStringr sy args xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

*6 direccin de o regreso: #17: C() *13 *3 *10


direccin de o regreso: #9: B(10,3)

*10
direccin de o regreso: #29: A(10)

#heap *5 *10 #heap d.r. Sistema Operativo main #25: #19: #12: #5: *2 *3
d.r. Sistema Operativo clase Cualquiera

#19: Contador del Programa

7.1 El stack y el heap

215

Figura 7.14

El stack al terminar de ejecutarse C().

xinty a xinty j xinty i xinty i xCualquieray objeto xinty n xinty m xString[ ]y args

*13 *3 *10 direccin de o regreso: #9: B(10,3) *10 direccin de o regreso: #29: A(10) # heap *5 *10 # heap direccin de o regreso: #29: main #17: Contador del Programa

Figura 7.15

El stack al terminar la ejecucin de B(10,3). o

xinty i xCualquieray objeto xinty n xinty m xString[ ]y args

*10 direccin de o regreso: #29: A(10) # heap *5 *10 # heap d. r. Sistema Operativo main #9: Contador del Programa

216

Administracin de la memoria durante ejecucin o o

Se llega al nal del mtodo A(10), por lo que se desmonta el registro de ace tivacin de A(10), se copia la direccin de regreso de la marca al contador del o o programa y quita la marca del stack. Podemos observar el estado del stack en este momento en la gura 7.16.

Figura 7.16

El stack al terminar la ejecucin de A(10). o

xCualquieray objeto xinty n xinty m xStringr sy args xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

#heap *5 *10 #heap d.r. Sistema Operativo main #25: #19: #12: #5: *2 *3
d.r. Sistema Operativo clase Cualquiera

#29: Contador del Programa

Al llegar la ejecucin del programa a la l o nea 29: se encuentra con otra invocacin a B(3,2), que son los campos de la clase. Se coloca la marca en el stack con o direccin de regreso 30:, se actualiza el PC para que marque el inicio del mtodo o e B y se monta al stack el registro de activacin de B(3,2). Los resultados de estas o acciones se muestran en la gura 7.17.

7.1 El stack y el heap

217

Figura 7.17

El stack antes de la ejecucin de B(3,2). o

xinty a xinty j xinty i xCualquieray objeto xinty n xinty m xStringr sy args xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

*5 *2 *3 direccin de o regreso: #29: B(3,2) #heap *5 *10 #heap d.r. Sistema Operativo main #25: #19: #12: #5: *2 *3
d.r. Sistema Operativo clase Cualquiera

#12: Contador del Programa

Al ejecutar al mtodo B(3,2), en la l e nea 16: se invoca al mtodo C(), por lo e que el sistema hace lo conducente con el stack, quedando ste como se muestra en e la gura 7.18.

218

Administracin de la memoria durante ejecucin o o

Figura 7.18

El stack antes de la ejecucin de C(). o

xinty k xinty a xinty j xinty i xCualquieray objeto xinty n xinty m xStringr sy args xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

*6 direccin de o regreso: #17: C() *5 *2 *3 direccin de o regreso: #29: B(3,2) #heap *5 *10 #heap d.r. Sistema Operativo main #25: #19: #12: #5: *2 *3
d.r. Sistema Operativo clase Cualquiera

#22: Contador del Programa

Termina de ejecutarse C() en la l nea 18: y el stack regresa a verse como en la gura 7.17 en la pgina anterior, excepto que el PC vale ahora 17:. Esta situacin a o se muestra en la gura 7.19.

7.1 El stack y el heap

219

Figura 7.19

El stack al terminar la ejecucin deC(). o

xinty a xinty j xinty i xCualquieray objeto xinty n xinty m xString[ ]y args

*5 *2 *3 direccin de o regreso: #29: B(3,2) # heap *5 *10 # heap d. r. Sistema Operativo main #14: Contador del Programa

Al continuar la ejecucin el programa, llega al nal del mtodo B(3,2) y sale o e de l, dejando el stack como se ve en la gura 7.20, con el PC apuntando a la e direccin de cdigo 30:. o o

Figura 7.20

El stack al terminar la ejecucin de B(3,2). o

xCualquieray objeto xinty n xinty m xString[ ]y args

# heap *5 *10 # heap d. r. Sistema Operativo main #30: Contador del Programa

220

Administracin de la memoria durante ejecucin o o

En la l nea 30: nuevamente se hace una llamada al mtodo C(), por lo que se e marca el stack y se monta su registro de activacin. El resultado se puede ver en o la gura 7.21 en la pgina opuesta. a

Figura 7.21

El stack antes de empezar la ejecucin de C(). o

xinty k xCualquieray objeto xinty n xinty m xStringr sy args xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

*6 direccin de o regreso: #31: C() #heap *5 *10 #heap d.r. Sistema Operativo main #25: #19: #12: #5: *2 *3
d.r. Sistema Operativo clase Cualquiera

#14: Contador del Programa

Al terminarse de ejecutar C() se desmonta su registro de activacin del stack, o se copia la direccin de regreso de la marca al PC y se quita la marca. El stack o queda como se muestra en la gura 7.22, con el PC apuntando a la l nea 31: del cdigo. o

7.1 El stack y el heap

221

Figura 7.22

El stack listo para iniciar la ejecucin de main. o

xCualquieray objeto xinty n xinty m xStringr sy args xvoidy main xvoidy C xvoidy B xvoidy A xinty b xinty a

#heap *5 *10 #heap d.r. Sistema Operativo main #25: #19: #12: #5: *2 *3
d.r. Sistema Operativo clase Cualquiera

#31: Contador del Programa

Como la l nea 31: es la que termina main, se descarga del stack el registro de activacin de este mtodo, se copia al PC la direccin de regreso de la marca y se o e o quita la marca. En ese momento termina la ejecucin del programa, por lo que se o libera el stack y el PC. En todo momento durante la ejecucin, el sistema puede utilizar lo que se o encuentre en el bloque global, ms aquello que se encuentre por encima de la ultima a marca en el stack y hasta inmediatamente antes de la celda antes de la ultima marca que se puso. De esta manera, Cada mtodo crea su propio ambiente de e ejecucin. o Resumiendo, el stack se utiliza para la administracin de la memoria en ejeo cucin. Cada vez que se invoca una rutina o mtodo, se construye el registro de o e activacin de la misma y se coloca en el stack. Cada vez que se sale de un mtodo, o e se quita del stack el registro de activacin de la rutina que est en el tope y la o a ejecucin contina en la direccin de regreso desde la que se invoc a esa instancia o u o o

222

Administracin de la memoria durante ejecucin o o

del mtodo. e Durante la ejecucin de un programa, el sistema trabaja con dos variables, el o tope del stack, que indica cul es la siguiente celda en la que se va a colocar ina formacin, y el contador del programa, que indica cul es la siguiente instruccin o a o que se va a ejecutar. En ambos casos, decimos que las variables son apuntadores, pues el tope del stack apunta a una celda en el stack (contiene una direccin del o stack) y el contador del programa apunta a una direccin de memoria del prograo ma donde se encuentra almacenado el cdigo del programa. o En el stack se le da lugar a: Todo lo declarado pblico en el paquete (o conjunto de programas). u Todas las estructuras de datos de las clases. Apuntadores a todos los mtodos miembros de clases. e A los resultados que entregan las funciones. A los parmetros formales de cada mtodo. a e A las variables locales de cada mtodo conforme se van declarando. e Decimos que cada mtodo tiene su ambiente propio de trabajo, en la medida e en que, al cargarse su registro de activacin en el stack, su entorno lo constituye o ese registro de activacin y el registro de activacin global. En nuestros esquemas, o o las celdas intermedias entre la primera y ultima marca no son accesibles en ese momento de la ejecucin. Esto nos da dos conceptos importantes en programacin: o o Rango de un identicador Se reere a los puntos del programa desde donde el identicador puede ser referido. El rango est dado de manera esttica a a por la estructura de bloques del programa. El compilador se encarga de que las referencias a los identicadores sean vlidas. a Existencia de una variable Se reere a los momentos, durante la ejecucin, en o que una variable conserva su valor. Una variable declarada existe mientras se encuentre en el stack. Deja de existir cuando se quita del stack el registro de activacin que la contiene. o Como ya mencionamos antes, de existir identicadores duplicados, el compilador busca a la declaracin ms cercana en el stack, pero busca unicamente en o a los registros de activacin vivos (despiertos), el global y el local, primero en el o local. Por ello, al declarar una variable repitiendo un nombre global, se crea una instancia fresca y nueva, que no tiene relacin alguna con la variable global orio ginal y que de hecho, oculta a la variable global original. Java permite ver datos

7.2 Recursividad

223

miembros de una clase que han sido ocultados por declaraciones locales utilizando el identicador de objeto this seguido del operador . y a continuacin el nombre o del atributo. Debo insistir en que el bloque o registro de activacin en el que se o encuentra la variable debe ser visible desde el punto de ejecucin y unicamente o se aplica a variables que hayan sido ocultadas por una reutilizacin del nombre. o Si la variable se encuentra en un registro de activacin inaccesible, entonces el o compilador emitir un mensaje de error. a

7.2 Recursividad
En matemticas nos encontramos frecuentemente con deniciones o funciones a recursivas. El ejemplo t pico de este tipo de funciones es la denicin del mtodo o e factorial:
6 91 8

si n

1 1

n!

9 7

n pn 1q! si n

Si escribimos un mtodo en Java que reeje esta denicin, tendr e o amos lo siguiente:
long f a c t o r i a l ( i n t n ) { i f ( n <= 0 ) { r e t u r n 1; } i f ( n > 1) { r e t u r n ( f a c t o r i a l ( n 1) n ) ; } else { return 1; } }

La ejecucin del mtodo es como sigue: en la invocacin desde fuera (el o e o mtodo main o algn otro mtodo), se llama con un argumento n, que debe e u e ser un valor entero. Si el entero es mayor que 1, procede el mtodo a llamarse e nuevamente a s mismo, pero disminuyendo en 1 al argumento. Si el argumento vale 1, el mtodo logra salir. Veamos la ejecucin de este mtodo dentro de una e o e clase en el listado 7.2, para poder seguir su ejecucin en el stack. o

224

Administracin de la memoria durante ejecucin o o

Cdigo 7.2 La funcin factorial o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: class Factorial { long f a c t o r i a l ( i n t n ) { i f ( n <= 0 ) { r e t u r n 1; } i f ( n > 1) { r e t u r n ( f a c t o r i a l ( n 1) n); } else { return 1; } } p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) C o n s o l a c o n s = new C o n s o l a ( ) ; F a c t o r i a l f = new F a c t o r i a l ( ) ; c o n s . i m p r i m e l n ( "4! es " + f . factorial (4)); } }

El stack, una vez que se carg el registro de activacin de main, se muestra en o o la gura 7.23.

Figura 7.23

Estado del stack al iniciarse la ejecucin de una clase. o

xFactorialy f xConsolay cons xvoidy main xlongy factorial

# heap # heap d.r. Sistema Operativo main #13: #2: d.r. Sistema Operativo clase Factorial

#16: Contador del Programa

Numeramos las l neas del programa para poder hacer referencia a ellas en

7.2 Recursividad

225

la ejecucin, que empieza en la l o nea 13:. En las l neas 13: y 15: tenemos las declaraciones e inicializaciones de variables locales a main, y en las l neas 16: y 17: est el unico enunciado realmente de ejecucin de main. La primera llamada de a o factorial desde main deja el stack como se ve en la gura 7.24

Figura 7.24

Estado del stack al iniciarse la llamada de factorial desde main.

xinty n xlongy valor de regreso xFactorialy f xConsolay cons xvoidy main xlongy factorial

*4 direccin de o regreso #17: factorial(4) # heap # heap d.r. Sistema Operativo main #13: #2: d.r. Sistema Operativo clase Factorial

#3: Contador del Programa

Se ejecuta la condicional de la l nea 3:, pero como n es mayor que 1 no se hace nada. Despus se evala la condicional de la l e u nea 6:, y como es verdadera se ejecuta el enunciado en las l neas 7: y 8: que es una llamada recursiva a factorial. Vuelve a entrar a ejecutar factorial y el stack se ve como en la gura 7.25 en la siguiente pgina. a

226

Administracin de la memoria durante ejecucin o o

Figura 7.25

Estado del stack al iniciarse la llamada de factorial desde factorial.

xinty n xlongy valor de regreso xinty n xlongy valor de regreso xFactorialy f xConsolay cons xvoidy main xlongy factorial

*3 direccin de o regreso #7: factorial(3) *4 direccin de o regreso #17: factorial(4) # heap # heap d.r. Sistema Operativo main #13: #2: d.r. Sistema Operativo clase Factorial

#3: Contador del Programa

Nuevamente se evala a falso la primera condicin, y como se evala a veru o u dadero la condicin en la l o nea 6: volvemos a llamar a factorial desde la l nea 7:, quedando el stack como se puede apreciar en la gura 7.26 en la pgina opuesta. a Como n sigue siendo mayor que 1, volvemos a llamar a factorial con 1 como argumento. El stack se ve como se muestra en la gura 7.27 en la pgina 228. a En esta llamada las condicionales de las l neas 3: y 6: ambas se evalan a u falso, por lo que se ejecuta el enunciado de la l nea 10:, y se regresa el valor 1. Esto se traduce en colocar en el espacio reservado para ello cerca del tope del stack ese valor, quitar del tope del stack el registro de activacin de la llamada de o factorial(1) y continuar la ejecucin en la l o nea 8: para hacer la multiplicacin. El o stack se ve como se muestra en la gura 7.28 en la pgina 229. a

7.2 Recursividad

227

En este punto se puede terminar de ejecutar la invocacin de factorial(2), por o lo que nuevamente se hace la multiplicacin y se coloca el resultado inmediatao mente abajo de la ultima marca en el stack; se procede a desmontar el registro de activacin de factorial(2).El stack se muestra en la gura 7.29 en la pgina 230. o a

Figura 7.26

Estado del stack al iniciarse la llamada de factorial desde factorial.

xinty n xlongy valor de regreso xinty n xlongy valor de regreso xinty n xlongy valor de regreso xFactorialy f xConsolay cons xvoidy main xlongy factorial

*2 direccin de o regreso #7: factorial(2) *3 direccin de o regreso #7: factorial(3) *4 direccin de o regreso #17: factorial(4) # heap # heap d.r. Sistema Operativo main #13: #2: d.r. Sistema Operativo clase Factorial

#3: Contador del Programa

228

Administracin de la memoria durante ejecucin o o

Figura 7.27

Estado del stack al iniciarse la llamada de factorial desde factorial.

xinty n xlongy valor de regreso xinty n xlongy valor de regreso xinty n xlongy valor de regreso xinty n xlongy valor de regreso xFactorialy f xConsolay cons xvoidy main xlongy factorial

*1 direccin de o regreso #7: factorial(1) *2 direccin de o regreso #7: factorial(2) *3 direccin de o regreso #7: factorial(3) *4 direccin de o regreso #17: factorial(4) # heap # heap d.r. Sistema Operativo main #13: #2: d.r. Sistema Operativo clase Factorial

#3: Contador del Programa

7.2 Recursividad

229

Figura 7.28

Estado del stack al terminarse la llamada de factorial(1).

xlongy valor de regreso xinty n xlongy valor de regreso xinty n xlongy valor de regreso xinty n xlongy valor de regreso xFactorialy f xConsolay cons xvoidy main xlongy factorial

*1 *2 direccin de o regreso #7: factorial(2) *3 direccin de o regreso #7: factorial(3) *4 direccin de o regreso #17: factorial(4) # heap # heap d.r. Sistema Operativo main #13: #2: d.r. Sistema Operativo clase Factorial

#8: Contador del Programa

Se sale de la llamada de factorial(3), acomodando el resultado del producto del resultado entregado por factorial(2) y 3. El stack se puede ver en la gura 7.30 en la pgina 231. a Nuevamente, al regresar con el valor de factorial(3) en la l nea 7:, lo multiplica por 4, usando para ello el valor que coloc la ejecucin de factorial(3) en el stack. o o

230

Administracin de la memoria durante ejecucin o o

Despus de quitar el registro de activacin y la marca de factorial(3) el stack se ve e o como se muestra en la gura 7.31 en la pgina opuesta. a

Figura 7.29

Estado del stack al terminarse la llamada de factorial(2).

xlongy valor de regreso xinty n xlongy valor de regreso xinty n xlongy valor de regreso xFactorialy f xConsolay cons xvoidy main xlongy factorial

*2 *3 direccin de o regreso #7: factorial(3) *4 direccin de o regreso #17: factorial(4) # heap # heap d.r. Sistema Operativo main #13: #2: d.r. Sistema Operativo clase Factorial

#8: Contador del Programa

En este momento se procede a escribir el valor de factorial(4) que fue entregado y colocado en el stack. Se termina de ejecutar el programa, y de manera similar a mtodos no recursivos, se libera el stack y el contador del programa. e

7.2 Recursividad

231

Figura 7.30

Estado del stack al terminarse la llamada de factorial(3).

xlongy valor de regreso xinty n xlongy valor de regreso xFactorialy f xConsolay cons xvoidy main xlongy factorial

*6 *4 direccin de o regreso #17: factorial(4) # heap # heap d.r. Sistema Operativo main #13: #2: d.r. Sistema Operativo clase Factorial

#8: Contador del Programa

Figura 7.31

Estado del stack al terminarse la llamada de factorial desde main.

xlongy valor de regreso xFactorialy f xConsolay cons xvoidy main xlongy factorial

24 # heap # heap d.r. Sistema Operativo main #13: #2: d.r. Sistema Operativo clase Factorial

#18: Contador del Programa

232

Administracin de la memoria durante ejecucin o o

Debemos insistir en que usamos la funcin factorial para mostrar la situacin o o en el stack porque es fcil de mostrar los cambios que va sufriendo el stack, no a porque sea un buen ejemplo para la recursividad. De hecho, dado que ya vimos el trabajo escondido que tenemos con la recursividad, una forma ms econmica de a o calcular factorial es con una iteracin simple, que no involucra manejo del stack. o El mtodo quedar codicado como se ve en el listado lis:s7-3. e a

Cdigo 7.3 Factorial calculado iterativamente o


s t a t i c p u b l i c long f a c t o r i a l ( i n t t o p e ) long f a c t = 1 ; f o r ( i n t i = 2 ; i <= t o p e ; i ++) { fact = fact i ; } return f a c t ; } {

7.2.1.

Las torres de Hanoi


Un ejemplo donde se entiende muy bien la utilidad de la recursividad es en el de las torres de Hanoi. El juego consiste de los siguiente:

El juego consiste de una tabla con tres postes pegados perpendiculares a la tabla y n discos de radios distintos entre s y con un oricio en el centro para poder ser colocados en los postes. Al empezar el juego se encuentran los n discos en un mismo poste, acomodados por tamao n decreciente, el ms grande hasta abajo. a El juego consiste de pasar los n discos a un segundo palo, moviendo disco por disco; est prohibido que quede un disco encima de otro que a es menor que l. e

En la gura 7.32 se muestra un ejemplo con 8 chas. Este juego tiene su origen en un monasterio tibetano, y consist de 64 chas. a La leyenda dec que cuando se lograran mover las 64 chas siguiendo las reglas el a mundo se iba a terminar. El algoritmo para lograr mover las n chas se muestra en la gura 7.33.

7.2 Recursividad

233

Figura 7.32

Juego de las torres de Hanoi

Figura 7.33

Estrategia recursiva para las torres de Hanoi


6 9 9 9 9 9n 9 9 9 9 9 9 8 9 9 9 9n 9 9 9 9 7 6 9Mover 1 cha del poste 1 al 3 8 7

 2? 9Mover 1 cha del poste 1 al 2


Mover 1 cha del poste 3 al 2

Mover n chas del poste 1 al poste 2 9 9 9

2? 9Mover 1 cha del poste 1 al 2 7 Mover n 1 chas del poste 3 al 2

6 9Mover n 8

1 chas del poste 1 al 3

Lo que me dice esta estrategia es que si slo tenemos dos chas las sabemos o mover a pie. Para el caso de que tenga ms de dos chas (n 2), suponemos a que pudimos mover las n 1 chas que estn en el tope del poste al poste a auxiliar, siguiendo las reglas del juego, despus movimos una sola cha al poste e denitivo, y para terminar movimos las n 1 chas del poste auxiliar al denitivo. Como en el caso del clculo de factorial con recursividad, se entra al mtodo a e

234

Administracin de la memoria durante ejecucin o o

decrementando la n en 1 hasta que tengamos que mover una sola cha; en cuanto la movemos, pasamos a trabajar con el resto de las chas. Hay que aclarar que esto funciona porque se van intercambiando los postes 1, 2 y 3. El cdigo (de o manera esquemtica) se puede ver en el listado 7.4. a

Cdigo 7.4 Mtodos para las torres de Hanoi o e


/ R e a l i z a e l m o v i m i e n t o de una f i c h a de un p o s t e a o t r o . @param p o s t e 1 E l p o s t e d e s d e e l que s e mueven l a s f i c h a s . @param p o s t e 2 E l p o s t e a l que s e mueve l a f i c h a . / p u b l i c v o i d mueveUno ( i n t p o s t e 1 , i n t p o s t e 2 ) { / E s c r i b e e l n mero de p o s t e c o r r e s p o n d i e n t e u o d i b u j a e l movimiento / System . o u t . p r i n t l n ( "Del " + p o s t e 1 + " al " + p o s t e 2 ) ; } / Mueve n f i c h a s d e l poste1 a l poste2 , usando e l poste3 como p o s t e de t r a b a j o . @param n e l n mero de f i c h a s a mover . u @param p o s t e 1 e l p o s t e d e s d e e l c u a l s e mueven . @param p o s t e 2 e l p o s t e d e s t i n o . @param p o s t e 3 e l p o s t e de t r a b a j o . / p u b l i c v o i d mueveN ( i n t n , i n t p o s t e 1 , i n t p o s t e 2 , i n t p o s t e 3 ) { i f ( n == 2 ) { mueveUno ( p o s t e 1 , p o s t e 3 ) ; mueveUno ( p o s t e 1 , p o s t e 2 ) ; mueveUno ( p o s t e 3 , p o s t e 2 ) ; } else { mueveN ( n 1, p o s t e 1 , p o s t e 3 , p o s t e 2 ) ; mueveUno ( p o s t e 1 , p o s t e 2 ) ; mueveN ( n 1, p o s t e 3 , p o s t e 2 , p o s t e 1 ) ; } }

Podemos hacer el ejercicio con 4 chas, llamando al procedimiento con mueveN(4,1,2,3). Veamos en la 7.34 los anidamientos que se hacen. Debe ser claro que en el unico mtodo que realmente hace trabajo es mueveUno, ya que para e n 2 todo lo que se hace es una llamada recursiva. Ilustraremos en las guras 7.35 a 7.42 cul es el trabajo realizado en las llamadas a mueveUno. a

7.2 Recursividad

235

Figura 7.34

Secuencia de llamadas en la torres de Hanoi


mueveN(4,1,2,3) 4  2? mueveN(3,1,3,2) 3  2? mueveN(2,1,2,3) 2  2? mueveUno(1,3) mueveUno(1,2) mueveUno(3,2) mueveUno(1,3) mueveN(2,2,3,1) 2  2? mueveUno(2,1) mueveUno(2,3) mueveUno(1,3) mueveUno(1,2) mueveN(3,3,2,1) 3  2? mueveN(2,3,1,2) 2  2? mueveUno(3,2) mueveUno(3,1) mueveUno(2,1) mueveUno(3,2) mueveN(2,1,2,3) 2  2? mueveUno(1,3) mueveUno(1,2) mueveUno(3,2)

/* /* /* /*

1 2 3 4

*/ */ */ */

/* /* /* /*

5 6 7 8

*/ */ */ */

/* /* /* /*

9 */ 10 */ 11 */ 12 */

/* 13 */ /* 14 */ /* 15 */

236

Administracin de la memoria durante ejecucin o o

Comprobemos que este algoritmo trabaja viendo una visualizacin con cuatro o chas. En cada gura mostraremos los movimientos que se hicieron mediante echas desde el poste en el que estaba la cha al poste en el que se coloc. Las o reglas exigen que cada vez que se mueve una cha, sta sea la que se encuentra e hasta arriba.

Figura 7.35

Situacin de las chas antes de la llamada o

Figura 7.36

Movimientos /* 1 */ al /* 3 */
/* 1 */ /* 2 */ /* 3 */

Figura 7.37

Movimiento /* 4 */
/* 4 */

7.2 Recursividad

237

Figura 7.38

Movimientos /* 5 */ al /* 7 */
/* 7 */ /* 6 */ /* 5 */

Figura 7.39

Movimiento /* 8 */
/* 8 */

Figura 7.40

Movimientos /* 9 */ al /* 11 */
/* 10 */

/* 11 */

/* 9 */

238

Administracin de la memoria durante ejecucin o o

Figura 7.41

Movimiento /* 12 */
/* 12 */

Figura 7.42

Movimientos /* 13 */ al /* 15 */
/* 13 */

/* 14 */

/* 15 */

Como se puede ver del ejercicio con las torres de Hanoi, 4 chas provocan 15 movimientos. Podr amos comprobar que 5 chas generan 31 movimientos. Esto se debe a la recursividad, que se encuentra escondida en la simplicidad del algoritmo. Aunque denitivamente es ms fcil expresarlo as que ocupa aproxia a , madamente 10 l neas, que dar las reglas con las que se mueven las chas de dos en dos. Entre otros ejemplos que ya no veremos por el momento, donde la solucin o recursiva es elegante y mucho ms clara que la iterativa se encuentra el recorrido a de rboles, las bsquedas binarias y algunos ordenamientos como el de mezcla y a u el de Quick. Con esto damos por terminado una descripcin somera sobre cmo se como o porta la memoria durante la ejecucin de un programa, en particular el stack de o ejecucin. Esta descripcin no pretende ser exhaustiva, sino unicamente proporo o cionar una idea de cmo identica la ejecucin los puntos de entrada, de regreso o o y parmetros a una funcin. a o

Ordenamientos usando estructuras de datos

8.1 Base de datos en un arreglo


Habiendo ya visto arreglos, se nos ocurre que puede resultar ms fcil guardar a a nuestras listas de cursos en un arreglo, en lugar de tenerlo en una lista ligada. Todo lo que tenemos que hacer es pensar en cul es el tamao mximo de un grupo y a n a reservar ese nmero de localidades en un arreglo. La superclase para el registro u con la informacin del estudiante queda casi exactamente igual al que utilizamos o como EstudianteBasico, excepto que como ahora la relacin de quin sigue a quin o e e va a estar dada por la posicin en el arreglo, no necesitamos ya la referencia al o siguiente estudiante. Todos los mtodos quedan exactamente igual, excepto que e todo lo relacionado con el campo siguiente ya no aparece ver listado 8.1 en la siguiente pgina. a

240

Ordenamientos usando estructuras de datos

Cdigo 8.1 Superclase con la informacin bsica de los estudiantes o o a

(InfoEstudiante)1/3

1: import i c c 1 . i n t e r f a z . C o n s o l a ; 2: / 3: Base de d a t o s , a b a s e de l i s t a s de r e g i s t r o s , que emula l a l i s t a 4: de un c u r s o de l i c e n c i a t u r a . T i e n e l a s o p c i o n e s n o r m a l e s de una 5: b a s e de d a t o s y f u n c i o n a m e d i a n t e un Men . u 6: / 7: p u b l i c c l a s s I n f o E s t u d i a n t e implements E s t u d i a n t e { 8: p r o t e c t e d S t r i n g nombre , 9: cuenta , 10: carrera ; 11: / C o n s t a n t e s s i m b l i c a s p a r a i d e n t i f i c a r campo / o 12: s t a t i c p u b l i c f i n a l i n t NOMBRE = 1 ; 13: s t a t i c p u b l i c f i n a l i n t CUENTA = 2 ; 14: s t a t i c p u b l i c f i n a l i n t CARRERA = 3 ; \ 15: / C o n s t a n t e s p a r a e d i t a r m e j o r l o s campos . 16: R e g i s t r a n e l v a l o r c o r r e s p o n d i e n t e de mayor 17: tama o e n c o n t r a d o p a r a l a c l a s e . n 18: / 19: s t a t i c p r o t e c t e d i n t maxTamN=0; 20: s t a t i c p r o t e c t e d i n t maxTamCta = 0 ; 21: s t a t i c p r o t e c t e d i n t maxTamCarr = 0 ; 22: / Para r e g r e s a r l a p o s i c i n r e l a t i v a d e n t r o de l a l i s t a / o 23: protected i n t pos ; 24: / Se u s a p a r a r e l l e n a r b l a n c o s . / 25: s t a t i c protected f i n a l S t r i n g b l a n c o s = r e p l i c a t e ( " " , 1 0 0 ) ; 26: 27: / C o n s t r u c t o r s i n p a r a m e t r o s . / 28: public InfoEstudiante () { 29: nombre = c a r r e r a = c u e n t a = n u l l ; 30: } 31: 32: / C o n s t r u c t o r que c o n s t r u y e e l o b j e t o con l o s d a t o s . / 33: p u b l i c I n f o E s t u d i a n t e ( S t r i n g nmbre , S t r i n g c nt a , 34: String crrera ) { 35: nombre = nmbre . t r i m ( ) ; 36: cuenta = cnta . trim ( ) ; 37: carrera = c r r e r a . trim ( ) ; 38: maxTamN = Math . max ( nmbre . l e n g t h ( ) , maxTamN ) ; 39: maxTamCta = Math . max ( c u e n t a . l e n g t h ( ) , maxTamCta ) ; 40: maxTamCarr = Math . max ( c a r r e r a . l e n g t h ( ) , maxTamCarr ) ; 41: } 42: 43: / R e g r e s a e l v a l o r d e l a t r i b u t o nombre . / 44: p u b l i c S t r i n g getNombre ( ) { 45: r e t u r n t h i s . nombre ; 46: }

8.1 Base de datos en un arreglo

241 (InfoEstudiante)2/3

Cdigo 8.1 Superclase con la informacin bsica de los estudiantes o o a


47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: / E s t a b l e c e e l v a l o r d e l a t r i b u t o nombre . @param argNombre , c a d e n a . / p u b l i c v o i d setNombre ( S t r i n g argNombre ) { t h i s . nombre = argNombre ; } / R e g r e s a e l v a l o r d e l a t r i b u t o cuenta . / public S t r i n g getCuenta () { return this . cuenta ; } / E s t a b l e c e e l v a l o r d e l a t r i b u t o cuenta . / public void setCuenta ( S t r i n g argCuenta ) { t h i s . cuenta = argCuenta ; } / R e g r e s a e l v a l o r d e l a t r i b u t o carrera . / public String getCarrera () { return this . c a r r e r a ; } / E s t a b l e c e e l v a l o r d e l a t r i b u t o carrera . / public void s e t C a r r e r a ( S t r i n g a r g C a r r e r a ) { this . carrera = argCarrera ; }

/ R e g r e s a l a p o s i c i n r e l a t i v a de e s e r e g i s t r o en o la l i s t a . @ r e t u r n La p o s i c i n r e l a t i v a . o / public i n t getPos () { r e t u r n po s ; } / Registra la posicin re la ti va del registro . o @param p os La p o s i c i n r e l a t i v a . o / p u b l i c s e t P o s ( i n t p os ) { t h i s . p os = po s ; }

242

Ordenamientos usando estructuras de datos

Cdigo 8.1 Superclase con la informacin bsica de los estudiantes o o a


93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: }

(InfoEstudiante)3/3

/ R e g r e s a e l campo i n d i c a d o d e l r e g i s t r o dado . @param c u a l S e l e c c i o n a campo en e l r e g i s t r o . @ r e t u r n e l v a l o r de e s e campo en e l r e g i s t r o . / p u b l i c S t r i n g getCampo ( i n t c u a l ) { S t r i n g cadena ; switch ( c u a l ) { case NOMBRE: c a d e n a = getNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) ; break ; case CUENTA : cadena = getCuenta ( ) . tr i m ( ) . toLowerCase ( ) ; break ; case CARRERA : cadena = g e t C a r r e r a ( ) . tr i m ( ) . toLowerCase ( ) ; break ; default : c a d e n a = " Campo no existente " ; break ; } // end o f s w i t c h ( c u a l ) return cadena ; } / Arma una c a d e n a con e l c o n t e n i d o d e l r e g i s t r o . @return e l contenido del r e g i s t r o . / public String armaRegistro () { r e t u r n ( nombre . t r i m ( ) + b l a n c o s ) . s u b s t r i n g ( 0 , maxTamN + 1 ) + "\t" + ( c u e n t a . t r i m ( ) + b l a n c o s ) . s u b s t r i n g ( 0 , maxTamCta + 1 ) + "\t" + ( c a r r e r a . t r i m ( ) + b l a n c o s ) . s u b s t r i n g ( 0 , maxTamCarr + 1 ) + "\t" ; } / L l e n a un r e g i s t r o con l o s v a l o r e s de l o s a r g u m e n t o s . @param nmbre Cadena con e l nombre . @param c n t a Cadena con e l n umero de c u e n t a . @param c r r e r a Cadena con l a c a r r e r a . / p u b l i c v o i d s e t R e g i s t r o ( S t r i n g nmbre , S t r i n g c n ta , String crrera ) { nombre = nmbre . t r i m ( ) ; cuenta = cnta . trim ( ) ; carrera = c r r e r a . trim ( ) ; }

8.1 Base de datos en un arreglo

243

Como se puede observar en el listado 8.1 en la pgina 240, todo qued prctia o a camente igual, excepto que quitamos todo lo relacionado con la referencia al siguiente. En su lugar agregamos un campo para que el registro se identique a s mismo en cuanto a la posicin que ocupa en el arreglo, pos y los mtodos o e correspondientes. Este campo se ver actualizado cuando se entregue la referencia a del registro sin especicar su posicin. o La clase as denida puede ser usada, por ejemplo, para cuando queramos una lista de estudiantes que tengan esta informacin bsica incluida. Un posible o a ejemplo se muestra en el listado 8.2. Esta jerarqu se puede extender tanto como a queramos. Si pensamos en estudiantes para listas usamos InfoEstudiante para heredar, agregando simplemente campos necesarios para mantener la lista, como en el caso EstudianteLista del Listado 8.2.

Cdigo 8.2 Extendiendo la clase InfoEstudiante o

(EstudianteLista)

1: p u b l i c c l a s s E s t u d i a n t e L i s t a extends I n f o E s t u d i a n t e { 2: / R e f e r e n c i a a l s i g u i e n t e de l a l i s t a / 3: protected E s t u d i a n t e L i s t a s i g u i e n t e ; \ 4: / C o n s t r u c t o r s i n p a r a m t e r o s . / 5: public EstudianteLista () { 6: } 7: / C o n s t r u c t o r a p a r t i r de una c l a s e que i m p l e m e n t e a * Estudiante . 8: / 9: public EstudianteLista ( Estudiante est ) { 10: nombre = e s t . getNombre ( ) ; 11: cuenta = e s t . getCuenta ( ) ; 12: carrera = est . getCarrera (); 13: } 14: / C o n s t r u c t o r con l o s d a t o s p a r a l o s a t r i b u t o s . 15: / 16: p u b l i c E s t u d i a n t e L i s t a ( S t r i n g nmbre , S t r i n g c t a , S t r i n g c a r r ) { 17: super ( nmbre , c t a , c a r r ) ; 18: } 19: / R e g r e s a e l v a l o r d e l a t r i b u t o siguiente . / 20: public EstudianteLista getSiguiente () { 21: return this . s i g u i e n t e ; 22: } 23: / E s t a b l e c e e l v a l o r d e l a t r i b u t o siguiente . / 24: public void s e t S i g u i e n t e ( E s t u d i a n t e L i s t a a r g S i g u i e n t e ) { 25: this . siguiente = argSiguiente ; 26: } 27: }

Si en cambio queremos estudiantes para arreglos, usamos directamente In-

244

Ordenamientos usando estructuras de datos

foEstudiante. Por ejemplo, si pensamos en registros para estudiantes de un curso podemos usar la denicin de EstudianteCalifs para armar un curso ver Listao do 8.3.

Cdigo 8.3 Extendiendo la clase InfoEstudiante con calicaciones o

(EstudianteCalifs) 1/3

1: p u b l i c c l a s s E s t u d i a n t e C a l i f s extends I n f o E s t u d i a n t e { 2: / Para a l m a c e n a r l a s c a l i f i c a c i o n e s d e l e s t u d i a n t e / 3: protected f l o a t [ ] c a l i f s ; 4: / V a l o r p o r o m i s i n p a r a e l numero de c a l i f i c a c i o n e s . / o 5: p r o t e c t e d f i n a l i n t MAX CALIFS = 1 0 ; 6: / C o n s t r u c t o r que e s t a b l e c e e l numero de c a l i f i c a c i o n e s 7: como e l v a l o r p o r o m i s i o n . 8: / 9: public E s t u d i a n t e C a l i f s () { 10: c a l i f s = new f l o a t [ MAX CALIFS ] ; 11: } 12: / C o n s t r u c t o r con v a l o r e s p a r a l o s a t r i b u t o s y numero de 13: calificaciones . 14: / 15: p u b l i c E s t u d i a n t e C a l i f s ( i n t c u a n t a s , S t r i n g nmbre , S t r i n g c t a , 16: String crrera ) { 17: super ( nmbre , c t a , c r r e r a ) ; 18: c a l i f s = new f l o a t [ c u a n t a s ] ; 19: } 20: / C o n s t r u y e un o b j e t o nuevo a p a r t i r de uno en l a i n t e r f a z 21: en l a r a i z de l a e s t r u c t u r a j e r a r q u i c a de c l a s e s . 22: / 23: public E s t u d i a n t e C a l i f s ( Estudiante est , int numCalifs ) { 24: nombre = e s t . getNombre ( ) ; 25: carrera = est . getCarrera (); 26: cuenta = e s t . getCuenta ( ) ; 27: c a l i f s = new f l o a t [ n u m C a l i f s ] ; 28: } 29: / R e g r e s a e l v a l o r d e l a t r i b u t o califs . / 30: public float [ ] g e t C a l i f s () { 31: return this . c a l i f s ; 32: } 33: / E s t a b l e c e e l v a l o r d e l a t r i b u t o califs . / 34: public void s e t C a l i f s ( f l o a t [ ] a r g C a l i f s ) { 35: this . c a l i f s = argCalifs ; 36: } 37: / R e g r e s a e l v a l o r d e l a t r i b u t o MAX CALIFS . / 38: p u b l i c i n t getMAX CALIFS ( ) { 39: r e t u r n t h i s . MAX CALIFS ; 40: }

8.1 Base de datos en un arreglo

245 (EstudianteCalifs)2/3

Cdigo 8.3 Extensin de la clase InfoEstudiante con calicaciones o o


41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85:

/ C o l o c a l a c a l f i c a c i o n en l a p o s i c i o n i n d i c a d a . @param c a l i f La c a l i f i c a c i on d e s e a d a . @param donde La p o s i c i on d e s e a d a . / p u b l i c boolean s e t C a l i f ( f l o a t c a l i f , i n t donde ) { / A s e g u r a r s e que l a p o s i c i o n e s t e en r a n g o s / i f ( donde < 0 | | donde >= c a l i f s . l e n g t h ) { return f a l s e ; } // end o f i f ( donde < 0 ! ! donde >= c a l i f s . l e n g t h ) / A s e g u r a r s e que l a c a l i f i c a c i o n e s t e en r a n g o s / c a l i f = Math . min ( 1 0 . 0 0 f , c a l i f ) ; c a l i f = Math . max ( 0 f , c a l i f ) ; / R e g i s t r a r l a c a l i f i c a c i o n / c a l i f s [ donde ] = c a l i f ; return true ; } / R e g r e s a l a c a l f i c a c i o n en l a p o s i c i o n i n d i c a d a . @param donde La p o s i c i o n d e s e a d a . / p u b l i c f l o a t g e t C a l i f ( i n t donde ) { / A s e g u r a r s e que l a p o s i c i o n e s t e en r a n g o s / i f ( donde < 0 | | donde >= c a l i f s . l e n g t h ) { return 0; } // end o f i f ( donde < 0 ! ! donde >= c a l i f s . l e n g t h ) r e t u r n c a l i f s [ donde ] ; } / R e g r e s a una c a d e n a con e l r e g i s t r o armado . / public String armaRegistro () { S t r i n g l i n e a = super . a r m a R e g i s t r o ( ) ; l i n e a += m u e s t r a C a l i f s ( c a l i f s . l e n g t h ) ; return l i n e a ; } / O b t i e n e e l p r o m e d i o de c a l i f i c a c i o n e s . @param e l numero de c a l i f i c a c i o n e s a c o n s i d e r a r . @ r e t u r n e l p r o m e d i o como un f l o a t . / public float calcPromedio ( int cuantos ) { f l o a t suma = 0 . 0 f ; i n t maxI = Math . min ( c a l i f s . l e n g t h , c u a n t o s ) ; f o r ( i n t i = 0 ; i < maxI ; i ++) { suma += c a l i f s [ i ] ; } // end o f f o r ( i n t i = 0 ; . . . r e t u r n suma / maxI ; }

246

Ordenamientos usando estructuras de datos

Cdigo 8.3 Extensin de la clase InfoEstudiante con calicaciones o o


86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: }

(EstudianteCalifs)3/3

/ R e g r e s a una c a d e n a con l a s c a l i f i c a c i o n e s b i e n o r g a n i z a d a s . @param e l numero de c a l i f i a c i o n e s a c o n s i d e r a r . @ r e t u r n Una c a d e n a con l a s c a l i f i c i o n e s o r g a n i z a d a s . / public String muestraCalifs ( int cuantas ) { i n t maxI = Math . max ( c a l i f s . l e n g t h , c u a n t a s ) ; S t r i n g armada = "" ; f o r ( i n t i = 0 ; i < maxI ; i ++) { i n t p a r t e E n t e r a = Math . r o u n d ( c a l i f s [ i ] 0 . 5 f ) ; i n t p a r t e F r a c c = Math . r o u n d ( c a l i f s [ i ] 1 0 0 ) % 1 0 0 ; String edita ; i f ( p a r t e E n t e r a > 9) { e d i t a = p a r t e E n t e r a + "." ; } // end o f e l s e else { i f ( p a r t e E n t e r a == 0 ) { e d i t a = "0." ; } // end o f i f ( c a l i f s [ i ] == 0 ) else { e d i t a = " " + p a r t e E n t e r a + "." ; } // end o f e l s e } // end o f e l s e i f ( p a r t e F r a c c > 9) { e d i t a += p a r t e F r a c c ; } // end o f i f ( p a r t e F r a c c > 9 ) else { i f ( p a r t e F r a c c == 0 ) { e d i t a += "00" ; } // end o f i f ( p a r t e F r a c c == 0 ) else { e d i t a += "0" + p a r t e F r a c c ; } // end o f e l s e } // end o f e l s e armada += ( ( i % 5 == 0 && i > 0 ) ? "\n" + r e p l i c a t e ( " " ,maxTamN +1) + "\t" + r e p l i c a t e ( " " , maxTamCta + 1 ) +"\t" + r e p l i c a t e ( " " , maxTamCarr + 1 ) +"\t\t" : "\t" ) + edita ; } // end o f f o r ( i n t i = 0 ; i < maxI ; i ++) r e t u r n armada ; }

8.1 Base de datos en un arreglo

247

Queremos cambiar nuestra estructura de datos de una lista a un arreglo. Estas dos estructuras de datos tienen semejanzas y diferencias. Veamos primero las semejanzas: En ambas estructuras existe una nocin de orden entre los elementos de la o estructura: podemos determinar cul elemento va antes y cul despus. Decia a e mos entonces que ambas estructuras son lineales porque podemos formar a los elementos en una l nea. En el caso de los arreglos el orden est dado a por el ndice, y en el caso de las listas est dado por la posicin relativa entre a o los elementos. Todos los elementos de una lista o de un arreglo son del mismo tipo. Decimos entonces que ambas estructuras son homogneas. e En cuanto a las diferencias, mencionamos las siguientes: Las listas pueden cambiar de tamao durante la ejecucin, mientras que n o los arreglos, una vez denido su tamao, ste ya no puede cambiar. Las n e listas son estructuras dinmicas mientras que los arreglos son estructuras a estticas. a El acceso al elemento de una lista se lleva a cabo recorriendo cada uno de los elementos que estn antes que el que buscamos; esto es, es un acceso a secuencial ; el acceso a un elemento de un arreglo es mediante un ndice, esto es acceso directo. Dependiendo de qu tipo de datos tengamos podremos elegir entre listas o e arreglos para nuestras estructuras de datos. Esta decisin deber estar, de alguna o a manera, justicada. Para construir la clase que maneja el arreglo, lo primero es que en lugar de una cabeza de lista deberemos tener el arreglo, denido de un cierto tamao, que n corresponder al mximo nmero de elementos que esperamos. Nuestros algorita a u mos son exactamente igual, excepto que interpretamos de distinta manera toma el siguiente, o colcate al principio. En el caso de que los registros estn en o e un arreglo colcate al principio se interpreta como inicializa un o ndice en 0; y toma el siguiente se interpreta como incrementa en uno a la variable empleada como ndice. Con esta representacin es fcil obtener el anterior, ya que o a unicamente se decrementa el ndice en 1, si es que existe anterior. Adems, en a todo momento tenemos que tener cuidado de no tratar de tomar elementos ms a all del n del arreglo. Veamos cmo queda con estos cambios.1 Dado un arreglo a o es fcil saber cul es el tamao del arreglo, mediante el atributo length, pero no a a n es igual de fcil saber el nmero de elementos que se encuentran realmente en el a u arreglo. Por lo tanto, es conveniente ir contando los registros que se agregan y los
Se recomienda referirse a los diagramas de Warnier-Orr donde se dieron los algoritmos en su momento.
1

248

Ordenamientos usando estructuras de datos

que se quitan, para que en todo momento se tenga claro el nmero de elementos u que tenemos en un arreglo. En el listado 8.4 podemos ver lo relacionado con el cambio de estructura de datos de una lista a un arreglo.

Cdigo 8.4 Base de datos implementada en un arreglo o

(CursoEnVector)1/2

1: import i c c 1 . i n t e r f a z . C o n s o l a ; 2: p u b l i c c l a s s C u r s o E n V e c t o r implements C u r s o { 3: / En e s t e c u r s o s e van a r e g i s t r a r NUM CALIFS 4: c a l i f i c a c i o n e s . / 5: p r i v a t e i n t NUM CALIFS = 1 0 ; 6: / Numero p o r o m i s i o n de r e g i s t r o s . / 7: p r i v a t e f i n a l i n t MAXREG = 1 0 ; 8: 9: / E l a r r e g l o que a l m a c e n a a l a l i s t a . / 10: private EstudianteVector [ ] l i s t a ; 11: / Numero de g r u p o . / 12: private S t r i n g grupo ; 13: / Numero de e s t u d i a n t e s i n s c r i t o s . / 14: p r i v a t e i n t numRegs = 0 ; 15: 16: / C o n s t r u c t o r que e s t a b l e c e e l numero de g r u p o y c u a n t a s 17: calificaciones . 18: / 19: p u b l i c C u r s o E n V e c t o r ( S t r i n g gr , i n t n u m C a l i f s ) { 20: NUM CALIFS = n u m C a l i f s ; 21: grupo = gr ; 22: numRegs = 0 ; 23: l i s t a = new E s t u d i a n t e V e c t o r [MAXREG ] ; 24: } 25: 26: / C o n s t r u c t o r que e s t a b l e c e e l numero de g r u p o . c u a n t a s 27: c a l i f i c a c i o n e s y c u a l e s e l maximo de e s t u d i a n t e s . 28: / 29: p u b l i c C u r s o E n V e c t o r ( S t r i n g gr , i n t c u a n t o s , i n t n u m C a l i f s ) { 30: NUM CALIFS = n u m C a l i f s ; 31: grupo = gr ; 32: numRegs = 0 ; 33: l i s t a = new E s t u d i a n t e V e c t o r [ c u a n t o s ] ; 34: } 35: 36: / R e g r e s a e l v a l o r d e l a t r i b u t o numRegs . / 37: p u b l i c i n t getNumRegs ( ) { 38: r e t u r n numRegs ; 39: }

8.1 Base de datos en un arreglo

249 (CursoEnVector)2/2

Cdigo 8.4 Base de datos implementada en un arreglo o


40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57:

/ C o n s t r u c t o r que e s t a b l e c e e l numero de g r u p o y una l i s t a i n i c i a l de e s t u d i a n t r e s i n s c r i t o s . / public CursoEnVector ( E s t u d i a n t e [ ] i n i c i a l e s , S t r i n g gr ) { grupo = gr ; i f ( i n i c i a l e s . l e n g t h > MAXREG) { l i s t a = new E s t u d i a n t e V e c t o r [ Math . max ( 2 i n i c i a l e s . l e n g t h , 2 MAXREG ) ] ; } // end o f i f ( i n i c i a l e s . l e n g t h ) else { l i s t a = new E s t u d i a n t e V e c t o r [MAXREG ] ; } // end o f e l s e f o r ( i n t i = 0 ; i < i n i c i a l e s . l e n g t h ; i ++) { l i s t a [ i ] = new E s t u d i a n t e V e c t o r ( i n i c i a l e s [ i ] , NUM CALIFS ) ; numRegs ++; } // end o f f o r ( i n t i = 0 ; i < i n i c i a l e s . l e n g t h ; i ++) }

Hay algunas operaciones bsicas que vamos a necesitar al trabajar con arreglos. a Por ejemplo, para agregar a un elemento en medio de los elementos del arreglo (o al principio) necesitamos recorrer a la derecha a todos los elementos que se encuentren a partir de la posicin que queremos que ocupe el nuevo elemento. o Esto lo tendremos que hacer si queremos agregar a los elementos y mantenerlos en orden conforme los vamos agregando. Similarmente, si queremos eliminar a alguno de los registros del arreglo, tenemos que recorrer a los que estn ms all del espacio que se desocupa para que se e a a ocupe el lugar desocupado. En ambos casos tenemos que recorrer a los elementos uno por uno y deberemos tener mucho cuidado en el orden en que recorramos a los elementos. Al recorrer a la derecha deberemos recorrer desde el nal del arreglo hacia la primera posicin que se desea mover. Si no se hace en este orden se o tendr como resultado el valor del primer registro que se desea mover copiado a a todos los registros a su derecha. El mtodo para recorrer hacia la derecha se ene cuentra en el listado 8.5 en la siguiente pgina. El mtodo nos tiene que regresar a e si pudo o no pudo recorrer a los elementos. En el caso de que no haya suciente lugar a la derecha, nos responder falso, y nos responder verdadero si es que a a pudo recorrer. Si deseamos regresar un lugar a la izquierda, el procedimiento es similar, excepto que tenemos que mover desde el primero hacia el ultimo, para no acabar con una repeticin de lo mismo. o

250

Ordenamientos usando estructuras de datos

Cdigo 8.5 Corrimiento de registros hacia la derecha e izquierda o


58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91:

(CursoEnVector)

/ R e c o r r e r r e g i s t r o s a p a r t i r d e l que e s t en l a a p o s i c i n desde cuantos l u g a r e s a l a derecha . o @param i n t d e s d e P o s i c i n d e l p r i m e r r e g i s t r o a r e c o r r e r . o @param i n t c u a n t o s N mero de e s p a c i o s a r e c o r r e r . u @ r e t u r n s b o o l e a n S i pudo o no h a c e r l o . / / Hace l u g a r p a r t a i n s e r t a r a un e l e m e n t o en e l l u g a r que l e c o r r e s p o n d e . / p r i v a t e boolean r e c o r r e ( i n t de sde , i n t c u a n t o s ) { i f ( numRegs + c u a n t o s >= l i s t a . l e n g t h ) return f a l s e ; f o r ( i n t i = numRegs 1 ; i >= d e s d e ; i ) { l i s t a [ i + cuantos ] = l i s t a [ i ] ; l i s t a [ i + cuantos ] . setPos ( i + cuantos ) ; l i s t a [ i ] = null ; } // end o f f o r ( i n t i = numRegs 1 ; i >= d e s d e ; i ) return true ; } / Se r e c o r r e n r e g i s t r o s a l a i z q u i e r d a p a r a o c u p a r un e s p a c i o @param i n t d e s d e l a p r i m e r a p o s i c i n que s e r e c o r r e h a c i a l a o izquierda / p r i v a t e boolean r e g r e s a ( i n t de sde , i n t c u a n t o s ) { i f ( ( ( d e s d e c u a n t o s ) < 0 ) | | ( d e s d e > numRegs 1)) { return f a l s e ; } // end o f i f ( ( d e s d e c u a n t o s ) < 0 ) f o r ( i n t i = d e s d e + 1 ; i < numRegs 1 ; i ++) { l i s t a [ i 1] = l i s t a [ i ] ; l i s t a [ i 1 ] . s e t P o s ( i 1); } // end o f f o r ( i n t i = 0 ; i < numRegs 1 ; i ++) numRegs = c u a n t o s ; return true ; }

El mtodo que regresaba la referencia de la lista del primer elemento de e la lista ahora debe regresar la referencia al arreglo completo. Queda como se muestra en el listado 8.6 en la pgina opuesta, junto con los mtodos que ponen a e y regresan el nmero de grupo, que no cambian. u Para saber el nmero de registros en el arreglo ya no nos sirve revisarlo y u ver cuntas referencias distintas de null tiene. Por ejemplo, si el arreglo en vez de a objetos tiene nmeros, y un valor vlido para los nmeros es el 0, no habr forma u a u a de distinguir entre un lugar ocupado por un 0 o un lugar que no estuviera ocupado. Por ello es conveniente ir contando los registros que se van agregando e ir descon-

8.1 Base de datos en un arreglo

251

tando los que se quitan, con los registros activos ocupando posiciones consecutivas

Cdigo 8.6 Mtodos de acceso y manipulacin o e o


92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: / R e g r e s a l a l i s t a de e s t u d i a n t e s . / public Estudiante [ ] getLista () { return l i s t a ; } / P r o p o r c i o n a e l n mero de g r u p o . u / public S t r i n g getGrupo ( ) { return grupo ; } / M o d i f i c a e l n mero de g r u p o . u / public void setGrupo ( S t r i n g gr ){ grupo = gr ; }

(CursoEnVector)

en el arreglo. Por ello, ya no se calcula el nmero de elementos en el arreglo, sino u que simplemente se regresa este valor. El mtodo se encuentra en el listado 8.7. e

Cdigo 8.7 Mtodo de acceso al nmero de registros o e u


107: 108: 109: 110: 111: / R e g r e s a e l n mero de r e g i s t r o s en l a l i s t a . u / p u b l i c i n t getNumRegs ( ) { r e t u r n numRegs ; }

(CursoEnVector)

Tenemos tres maneras de agregar registros al arreglo. La primera de ellas, la ms fcil, es agregando al nal de los registros, ya que esto no implica mover a a a nadie, sino simplemente vericar cul es el siguiente lugar a ocupar. De manera a similar a que cuando el nmero de elementos en un arreglo es n los u ndices van del 0 al n 1, si numRegs vale k quiere decir que los k registros ocupan las posiciones 0 a k 1, por lo que la siguiente posicin a ocupar es, precisamente, o k. En general, numRegs contiene la siguiente posicin a ocuparse. Por lo que si o se agrega al nal, lo unico que hay que hacer es ocupar el lugar marcado por numRegs, incrementando a este ultimo. Si vamos a agregar los registros siempre al principio del arreglo, lo que tenemos que hacer es recorrer todos los registros un lugar a la derecha para desocupar

252

Ordenamientos usando estructuras de datos

el primer lugar y colocar ah el registro. Por ultimo, si se desea mantener ordenados los registros con un orden lexi cogrco, primero tenemos que localizar el lugar que le toca. Una vez hecho esto a se recorren todos los registros a partir de ah un lugar a la derecha, y se coloca en el lugar desocupado al nuevo registro. En los tres casos, antes de agregar algn registro deberemos vericar que tou dav hay lugar en el arreglo, ya que el arreglo tiene una capacidad ja dada en a el momento en que se crea. Como no siempre vamos a poder agregar registros (algo que no suced cuando ten a amos una lista), todos estos mtodos tienen que e decirnos de regreso si pudieron o no. Para localizar el lugar que le toca a un registro nuevo lo vamos comparando con los registros en el arreglo, hasta que encontremos el primero mayor que l. e Como el orden est dado por el nombre, que es una cadena, tenemos que comparar a cadenas. El mtodo compareTo de la clase String nos sirve para saber la relacin e o entre dos cadenas, de la siguiente forma:
6 9 1 8

s1.compareTo(String s2)

9 7

0 1

Si s1 s2 Si s1 == s2 Si s1 s2

La programacin de estos tres mtodos se puede apreciar en el listado 8.8. o e

Cdigo 8.8 Agregando registros a la base de datos o


112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123:

(CursoEnVector)1/2

/ Agrega un r e g i s t r o a l f i n a l de l a l i s t a . @param I n f o E s t u d i a n t e nuevo E l r e g i s t r o a a g r e g a r . / p u b l i c boolean a g r e g a E s t F i n a l ( E s t u d i a n t e nuevo ) { int actual ; i f ( numRegs >= l i s t a . l e n g t h ) return f a l s e ; l i s t a [ numRegs++] = nuevo ; return true ; }

8.1 Base de datos en un arreglo

253 (CursoEnVector)2/2

Cdigo 8.8 Agregando registros a la base de datos o


124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164:

/ Agrega un r e g i s t r o a l p r i n c i p i o de l a l i s t a . @param I n f o E s t u d i a n t e nuevo A q u i e n s e va a a g r e g a r / p u b l i c boolean a g r e g a E s t ( I n f o E s t u d i a n t e nuevo ) { // R e c o r r e a t o d o s l o s e s t u d i a n t e s un l u g a r a l a d e r e c h a i f (! recorre (0 ,1)) { return f a l s e ; } l i s t a [ 0 ] = nuevo ; numRegs++; return true ; } / Agrega un r e g i s t r o donde l a t o c a p a r a m an t e n e r l a l i s t a ordenada . @param I n f o E s t u d i a n t e nuevo A q u i e n s e va a a g r e g a r @ r e t u r n s b o o l e a n S i pudo o no a g r e g a r / p u b l i c boolean a g r e g a E s t O r d e n ( I n f o E s t u d i a n t e nuevo ) { i f ( numRegs == l i s t a . l e n g t h ) { return f a l s e ; } int actual = 0; w h i l e ( a c t u a l < numRegs && l i s t a [ a c t u a l ] . daNombre ( ) . compareTo ( nuevo . daNombre ( ) ) <= 0 ) a c t u a l ++; i f ( a c t u a l == numRegs ) { // E n t r a a l f i n a l l i s t a [ a c t u a l ] = nuevo ; numRegs++; return true ; } i f (! recorre ( actual ,1)) { return f a l s e ; } l i s t a [ a c t u a l ] = nuevo ; numRegs++; return true ; }

Cuando deseamos agregar un registro de tal manera de mantener la lista en orden, debemos, como ya dijimos, encontrarle el lugar que le toca, entre un registro lexicogrcamente menor o igual a l y el primero mayor que l. Esto lo hacemos a e e

254

Ordenamientos usando estructuras de datos

en las l neas 149: 152: del listado 8.8 en la pgina 252. Nos colocamos al principio a del vector, poniendo el ndice que vamos a usar para recorrerlo en 0 l nea 149:. A continuacin, mientras no se nos acaben los registros del arreglo y estemos viendo o registros lexicogrcamente menores al que buscamos condicionales en l a neas 150: y 151: incrementamos el ndice, esto es, pasamos al siguiente. Podemos salir de la iteracin porque se deje de cumplir cualquiera de las dos o condiciones: que ya no haya elementos en el arreglo o que ya estemos entre uno menor o igual y uno mayor. En el primer caso habremos salido porque el ndice lleg al nmero de registros almacenados actual == numRegs en cuyo caso o u simplemente colocamos al nuevo registro en el primer lugar sin ocupar del arreglo. No hay peligro en esto pues al entrar al mtodo vericamos que todav hubiera e a lugares disponibles. En el caso de que haya encontrado un lugar entre dos elementos del arreglo, tenemos que recorrer a todos los que son mayores que l para hacer lugar. Esto e se hace en la l nea 158:, donde de paso preguntamos si lo pudimos hacer seguramente s porque ya hab amos vericado que hubiera lugar. Una vez recorridos los elementos del arreglo, colocamos el nuevo elemento en el lugar que se desocup gracias al corrimiento, y avisamos que todo estuvo bien l o neas 161: y 162: no sin antes incrementar el contador de registros.

Cdigo 8.9 Quitando a un estudiante de la base de datos o


165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183:

(CursoEnVector)

/ Q u i t a e l r e g i s t r o s o l i c i t a d o ( p o r nombre ) de l a l i s t a . @param nombre E l e s t u d i a n t e que s e d e s e a e l i m i n a r . / p u b l i c boolean q u i t a E s t ( S t r i n g nombre ) { int actual ; nombre=nombre . t r i m ( ) . t o L o w e r C a s e ( ) ; i f ( numRegs == 0 ) r e t u r n f a l s e ; // E s t v a ca l a l i s t a : no s e pudo a actual = 0; // E l p r i m e r o de l a l i s t a w h i l e ( a c t u a l < numRegs && ! ( l i s t a [ a c t u a l ] . daNombre ( ) . t o L o w e r C a s e ( ) . e q u a l s ( nombre . t o L o w e r C a s e ( ) ) ) ) { a c t u a l ++; } i f ( a c t u a l == numRegs ) return f a l s e ; // No s e e n c o n t r o regresa ( actual , 1 ) ; return true ; }

Tambin el mtodo que quita a un estudiante va a cambiar. El mtodo hace e e e

8.1 Base de datos en un arreglo

255

lo siguiente: localiza al registro que contenga al nombre completo, que llega como parmetro l a neas 174: a 178: del listado 8.9 en la pgina opuesta. a Una vez que encontr el registro en el que est ese nombre, se procede a o a desaparecerlo, recorriendo a los registros que estn despus que l un lugar a a e e la izquierda, encimndose en el registro que se est quitando; esto se hace con la a a llamada a regresa(actual,1) en la l nea 181: del listado 8.9 en la pgina opuesta. Al a terminar de recorrer a los registros hacia la izquierda, se decrementa el contador de registros numRegs. La ubicacin de este decremento en el mtodo regresa l o e nea 85: del listado 8.5 en la pgina 250 se justica bajo la ptica de que numRegs a o indica la posicin del primer registro vac otra opcin hubiera sido poner el o o; o decremento en quitaEst, que estar justicado bajo la ptica de que numRegs a o cuenta el nmero de estudiantes en la base de datos. Como numRegs juega ambos u papeles la respuesta a dnde poner el decremento no es unica. El diagrama de o Warnier correspondiente se encuentra en la Figura 8.1.

Figura 8.1

Algoritmo para eliminar a un estudiante


6 6 9 9Colocarse al principio 9 9 9 9 6 9 9 9 9 9 9 9 9 8 9 9 9 recorrer 9 8 9Localizar el nombre 9 Pasar al 9 9 9 (mientras haya 9 9 9 9 9 siguiente 9 9 9 9 9 y actual 9 9 buscado) 9 7 9 7 9 9 9 6 9 9 9 9 9 9 9 9 9 Eliminar 9 8 9 Recorre un lugar a la izquierda 9 8 a un 9se encontr o a partir de donde se encontr o estudiante 9 9 9 9 9 9 9Avisa que s 9 9 9 9 9 9 9 7 9 9 9 9 9 9 9 9 6 9 9 9 9 9 8 9 9 9 9se encontr o Avisa que no 9 9 9 9 7 7

El mtodo que busca una subcadena en alguno de los campos en el arreglo e cambia la forma en que nos colocamos al principio: colocarse al principio ahora implica poner al ndice que vamos a usar para recorrerlo en 0 l nea 191: del

256

Ordenamientos usando estructuras de datos

listado 8.10 mientras que tomar el siguiente quiere decir incrementar en 1 el ndice que se est usando para recorrer el arreglo l a nea 194: del listado 8.10.

Cdigo 8.10 Bsqueda de una subcadena en algn campo del arreglo o u u


184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199:

(CursoEnVector)

/ Busca a l r e g i s t r o que c o n t e n g a a l a s u b c a d e n a . @param i n t c u a l C u a l e s e l campo que s e va a c o m pa r ar . @param S t r i n g s u b c a d La c a d e n a que s e e s t b u s c a n d o . a @ r e t u r n s i n t E l r e g i s t r o d e s e a d o o 1. / public I n f o E s t u d i a n t e buscaSubcad ( i n t cual , S t r i n g subcad ) { int actual ; subcad = subcad . trim ( ) . toLowerCase ( ) ; actual = 0; w h i l e ( a c t u a l < numRegs && ( l i s t a [ a c t u a l ] . daCampo ( c u a l ) . i n d e x O f ( s u b c a d . t o L o w e r C a s e ( ) ) ) == 1) a c t u a l ++; i f ( a c t u a l < numRegs ) return l i s t a [ a c t u a l ] ; else return n u l l ; }

Otra diferencia es que cuando en un arreglo preguntamos si ya se nos acabaron los elementos, lo que hacemos es preguntar si el ndice ya alcanz al nmero de o u registros l nea 195: y no si la referencia es nula. Por lo tanto, recorremos el arreglo mientras nuestro ndice sea menor que el nmero de registros el u ndice sea vlido y no tengamos enfrente en la posicin actual del arreglo a quien a o estamos buscando. Una vez recorrido el arreglo deberemos averiguar si encontramos o no la subcadena. Si el ndice lleg a ser el nmero de registros, entonces no lo encontr. Si o u o no lleg, el entero contenido en el o ndice corresponde a la posicin de la subcadena o encontrada. El mtodo procede, entonces, a regresar el registro que contiene a la e subcadena. Una pregunta natural es por qu no regresamos simplemente el e ndice en el que se encuentra el registro? La respuesta es muy sencilla. El ndice tiene sentido como mecanismo de acceso al arreglo. Sin embargo, el arreglo es un dato privado de VectorCurso, por lo que desde CursoMenu, y desde cualquier otra clase, no se tiene acceso a l. Entonces, el conocer una posicin en el arreglo, desde fuera de e o VectorCurso, no slo no nos sirve, sino que va contra los principios del encapsulao miento, en el que los datos son privados en un 99 % de los casos (para hacer un dato pblico deber estar sumamente justicado). Adicionalmente, la clase que u a maneja el men queda prcticamente idntica a como estaba para el manejo de u a e

8.1 Base de datos en un arreglo

257

las listas, y esto es algo deseable. De esa manera podemos decir que la clase MenuVector no tiene que saber cmo estn implementadas las estructuras de datos o a o los mtodos de la clase VectorLista, sino simplemente saber usarlos y saber que e le tiene que pasar como parmetro y qu espera como resultado. Excepto por a e los mtodos que agregan y quitan estudiantes, que los volvimos booleanos para e que informen si pudieron o no, todos los dems mtodos mantienen la rma que a e ten en la implementacin con listas ligadas. Vale la pena decir que podr an o amos modicar los mtodos de las listas ligadas a que tambin contestaran si pudieron e e o no, excepto que en el caso de las listas ligadas siempre podr an.

Cdigo 8.11 Listar todos los registros de la base de datos o


200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213:

(CursoEnVector)

/ L i s t a todos l o s r e g i s t r o s d e l Curso . @param C o n s o l a c o n s d nde e s c r i b i r . o / public void l i s t a T o d o s ( Consola cons ) { int actual ; f o r ( a c t u a l = 0 ; a c t u a l < numRegs ; a c t u a l ++) { cons . imprimeln ( l i s t a [ a c t u a l ] . d a R e g i s t r o ( ) ) ; } i f ( a c t u a l == 0 ) { c o n s . i m p r i m e l n ( "No hay registros en la base de datos " ) ; } }

Nos falta revisar nada ms dos mtodos: el que lista todo el contenido de la base a e de datos y el que lista solamente los que cazan con cierto criterio. Para el primer mtodo nuevamente se aplica la transformacin de que colocarse al principio de la e o lista implica poner al ndice que se va a usar para recorrerla en 0 l nea 207: en el listado 8.11. Nuevamente nos movemos por los registros incrementando el ndice en 1, y vericamos al salir de la iteracin si encontramos lo que buscbamos o no. o a En el caso del mtodo que lista a los que cazan con cierto criterio listado 8.12 e en la siguiente pgina nuevamente se recorre el arreglo de la manera que ya vimos, a excepto que cada uno que contiene a la subcadena es listado. Para saber si se list o o no a alguno, se cuentan los que se van listando l nea 223: en el listado 8.12 en la siguiente pgina. Si no se encontr ningn registro que satisciera las condiciones a o u dadas, se da un mensaje de error manifestndolo. a

258

Ordenamientos usando estructuras de datos

Cdigo 8.12 Listando los que cumplan con algn criterio o u


214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241:

(CursoEnVector)

/ Imprim e l o s r e g i s t r o s que c a z a n con un c i e r t o p a t r n . o @param C o n s o l a c o n s D i s p o s i t i v o en e l que s e va a e s c r i b i r . @param i n t c u a l Con c u l campo s e d e s e a c o m pa r ar . a @param S t r i n g s u b c a d Con e l que queremos que c a c e . / p u b l i c v o i d losQueCazanCon ( C o n s o l a cons , i n t c u a l , S t r i n g subcad ) { int i = 0; subcad = subcad . toLowerCase ( ) ; int actual ; / R e c o r r e m o s b u s c a n d o e l r e g s i t r o / f o r ( a c t u a l = 0 ; a c t u a l < numRegs ; a c t u a l ++) { i f ( l i s t a [ a c t u a l ] . daCampo ( c u a l ) . i n d e x O f ( s u b c a d ) != 1) { i ++; cons . imprimeln ( l i s t a [ a c t u a l ] . d a R e g i s t r o ( ) ) ; } } / S i no s e e n c o n t r n i n g u n r e g i s t r o / o i f ( i == 0 ) { c o n s . i m p r i m e l n ( "No se encontr ning n registro " + o u "que cazara " ) ; } }

8.2 Mantenimiento del orden con listas ligadas


Tenemos ya una clase que maneja a la base de datos en una lista ligada (ListaCurso). Podemos modicar levemente ese programa para beneciarnos de la herencia y hacer que Estudiante herede de la clase InfoEstudiante, y de esa manera reutilizar directamente el cdigo que ya tenemos para InfoEstudiante. Todo o lo que tenemos que hacer es agregarle los campos que InfoEstudiante no tiene y los mtodos de acceso y manipulacin para esos campos. La programacin de la e o o

8.2 Mantenimiento del orden con listas ligadas

259

clase utilizando herencia se puede observar en el listado 8.13.

Cdigo 8.13 Denicin de la clase Estudiante para los registros o o

(Estudiante) 1/3

1: import i c c 1 . i n t e r f a z . C o n s o l a ; 2: / 3: Base de d a t o s , a b a s e de l i s t a s de r e g i s t r o s , que emula l a l i s t a 4: de un c u r s o de l i c e n c i a t u r a . T i e n e l a s o p c i o n e s n o r m a l e s de una 5: b a s e de d a t o s y f u n c i o n a m e d i a n t e un Men u 6: / 7: c l a s s E s t u d i a n t e extends I n f o E s t u d i a n t e { 8: protected E s t u d i a n t e s i g u i e n t e ; 9: protected S t r i n g c l a v e ; 10: p u b l i c s t a t i c f i n a l i n t CLAVE = 4 ; 11: / C o n s t r u c t o r s i n p a r m e t r o s . / a 12: public Estudiante () { 13: super ( ) ; 14: clave = null ; 15: siguiente = null ; 16: } 17: / 18: C o n s t r u c t o r a p a r t i r de d a t o s de un e s t u d i a n t e . 19: Los campos v i e n e n s e p a r a d o s e n t r e s p o r comas , m i e n t r a s 20: que l o s r e g i s t r o s v i e n e n s e p a r a d o s e n t r e s p o r punto 21: y coma . 22: @param S t r i n g , S t r i n g , S t r i n g , S t r i n g l o s v a l o r e s p a r a 23: cada uno de l o s campos que s e van a l l e n a r . 24: @ r e t u r n E s t u d i a n t e una r e f e r e n c i a a una l i s t a 25: / 26: p u b l i c E s t u d i a n t e ( S t r i n g nmbre , S t r i n g c nt a , S t r i n g c l v e , 27: String crrera ) { 28: super ( nmbre , c nt a , c r r e r a ) ; 29: clave = clve . trim ( ) ; 30: siguiente = null ; 31: } 32: / 33: R e g r e s a e l c o n t e n i d o d e l campo c l a v e . 34: / 35: public String getClave () { 36: return c l a v e ; 37: } 38: / 39: A c t u a l i z a e l campo c l a v e con e l v a l o r que p a s a como 40: parmetro . a 41: / 42: public void s e t C l a v e ( S t r i n g c l v e ) { 43: clave = clve ; 44: }

260

Ordenamientos usando estructuras de datos

Cdigo 8.13 Denicin de la clase Estudiante para los registros o o


45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91:

(Estudiante)2/3

/ R e g r e s a e l campo que c o r r e s p o n d e a l s i g u i e n t e r e g i s t r o en l a l i s t a / public Estudiante getSiguiente () { return s i g u i e n t e ; } / R e g r e s a e l campo s e l e c c i o n a d o d e l r e g i s t r o dado . @param i n t E s t u d i a n t e E l n mero d e l campo y e l r e g i s t r o . u @ r e t u r n s S t r i n g La c a d e n a s o l i c i t a d a / p u b l i c S t r i n g getCampo ( i n t c u a l ) { S t r i n g cadena ; switch ( c u a l ) { case I n f o E s t u d i a n t e .NOMBRE: c a d e n a = getNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) ; break ; case I n f o E s t u d i a n t e . CUENTA : cadena = getCuenta ( ) . tr i m ( ) . toLowerCase ( ) ; break ; case E s t u d i a n t e . CARRERA : cadena = g e t C a r r e r a ( ) . tr i m ( ) . toLowerCase ( ) ; break ; case E s t u d i a n t e . CLAVE : cadena = g e t C l a v e ( ) . tr i m ( ) . toLowerCase ( ) ; break ; default : c a d e n a = " Campo no existente " ; } return cadena ; } / A c t u a l i z a e l campo s i g u i e n t e con l a r e f e r e n c i a que s e l a pasa . @param s i g La r e f e r e n c i a a c o l o c a r . / public void s e t S i g u i e n t e ( E s t u d i a n t e s i g ) { siguiente = sig ; \ } / Arma una c a d e n a con e l c o n t e n i d o de t o d o e l r e g i s t r o . @ r e t u r n Una c a d e n a con e l r e g i s t r o d e s e a d o . / public String getRegistro () { r e t u r n super . g e t R e g i s t r o ()+"\t"+c l a v e . t r i m ( ) ; }

8.2 Mantenimiento del orden con listas ligadas

261 (Estudiante)3/3

Cdigo 8.13 Denicin de la clase Estudiante para los registros o o


92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: } / A c t u a l i z a t o d o e l r e g i s t r o de un j a l n . o @param S t r i n g e l nombre , S t r i n g cuenta String carrera String clave . / p u b l i c v o i d s e t R e g i s t r o ( S t r i n g nmbre , S t r i n g c nta , String clve , String crrera ) super . s e t R e g i s t r o ( nmbre , c nta , c r r e r a ) ; clave = clve . trim ( ) ; }

Hay que notar que lo que programamos como de acceso privado cuando no tombamos en consideracin la herencia, ahora se convierte en acceso protegido, a o para poder extender estas clases. Algunos de los mtodos que enunciamos en esta clase son, simplemente, mtoe e dos nuevos para los campos nuevos. Tal es el caso de los que tienen que ver con clave y siguiente. El mtodo getCampo se redene en esta clase, ya que ahora tiene e que considerar ms posibilidades. Tambin el mtodo getRegistro es una redenia e e cin, aunque usa a la denicin de la superclase para que haga lo que correspond o o a a la superclase. Los constructores tambin son interesantes. Cada uno de los constructores, e tanto el que tiene parmetros como el que no, llaman al correspondiente consa tructor de la superclase, para que inicialice los campos que tiene en comn con la u superclase. La palabra super se est utilizando de dos maneras distintas. Una de ellas, a en el constructor, estamos llamando al constructor de la superclase usando una notacin con argumentos. En cambio, en el mtodo getRegistro se usa igual que o e cualquier otro objeto, con la notacin punto. En este segundo caso nos referimos o al super-objeto de this, a aqul denido por la superclase. e

8.2.1.

Revisita de la clase ListaCurso


Vimos en el cap tulo anterior prcticamente todos los mtodos relativos al a e manejo de listas. Nos falt unicamente el mtodo que agrega registros a la base o e de datos, manteniendo el orden. Como en la parte anterior seguiremos teniendo al

262

Ordenamientos usando estructuras de datos

nombre como la llave (key) de nuestros registros, y es el que va a denir el orden. Igual podr amos tener el nmero de cuenta o la carrera. u Para agregar un registro tenemos que distinguir entre tres situaciones distintas: (a) La lista est vac en cuyo caso le toca ser la cabeza de la lista. a a, (b) Ya hay registros en la lista pero todos van despus que el que se est agree a gando. (c) Le toca entre dos registros que ya estn en la lista, o bien es el ultimo (ambos a casos se tratan igual). El primer caso es sencillo, ya que simplemente inauguramos la lista con el registro que se nos da. El segundo caso no es igual al tercero porque el nuevo registro tiene que quedar en la cabeza de la lista ver gura 8.2, donde las echas punteadas son las referencias que deben de quedar. Por lo que la referencia que queda antes es la de la cabeza de la lista y la que queda despus es la que antes e era la primera.

Figura 8.2

Agregando al principio de la lista


lista Juan Pedro Samuel Yuridia

nuevo

Alberto

En el caso de que no sea el primero que se agrega, y que no le toque al principio de la lista, se debe recorrer la lista hasta que a su izquierda haya uno menor y a su derecha uno mayor. Esto se logra, simplemente recorriendo la lista y parando cuando se encuentre el nal de la lista, o bien el primero que va a tener una llave mayor que el nuevo. Una vez que se encontr el lugar, se modican las referencias o al siguiente para que quede insertado en la lista ver gura 8.3 en la pgina a opuesta.

8.2 Mantenimiento del orden con listas ligadas

263

Figura 8.3

Agregando en medio de la lista


lista Juan Pedro Samuel Yuridia

nuevo

Ricardo

El diagrama de Warnier que describe este proceso se encuentra en la gura 8.4 en la siguiente pgina y la programacin del mtodo se puede ver en el listado 8.14. a o e

Cdigo 8.14 Agregar un registro manteniendo el orden o


105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131:

(ListaCurso)1/2

/ Agrega un e s t u d i a n t e en o r d e n en l a l i s t a . @param E s t u d i a n t e nuevo E l r e g i s t r o a a g r e g a r @ r e t u r n s b o o l e a n S i pudo o no h a c e r l o / p u b l i c boolean a g r e g a E s t O r d e n ( E s t u d i a n t e nuevo ) { S t r i n g scompara = nuevo . daNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) ; i f ( l i s t a == n u l l ) // Es e l p r i m e r o que s e mete l i s t a = nuevo ; numRegs++; return true ; } i f ( l i s t a . daNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) . compareTo ( scompara ) > 0 ) { // Le t o c a e l p r i m e r l u g a r de l a l i s t a , p e r o // no e s e l u n i c o nuevo . p o n S i g u i e n t e ( l i s t a ) ; l i s t a = nuev ; numRegs++; return true ; } Estudiante actual = l i s t a . daSiguiente () , anterior = lista ; w h i l e ( a c t u a l != n u l l && a c t u a l . daNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) . compareTo ( scompara ) <= 0 ) { anterior = actual ; actual = actual . daSiguiente (); }

264

Ordenamientos usando estructuras de datos

Cdigo 8.14 Agregar un registro manteniendo el orden o


132: 133: 134: 135: 136: 137:

(ListaCurso)2/2

// S i a c t u a l == n u l l l e t o c a a l f i n a l de l a l i s t a nuevo . p o n S i g u i e n t e ( a c t u a l ) ; a n t e r i o r . p o n S i g u i e n t e ( nuevo ) ; numRegs++; return true ; }

Figura 8.4

Agregando un registro en orden


6 9lista 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 9lista 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 7

 r

Colcalo en la cabeza de la lista o


6 9 Le toca al 9 9 9 9 principio 9 9 9 9 9 de la lista 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 6 9 Insrtalo antes del e 8 9 7

primer elemento de la lista

Agrega registro en orden 9 9

 r

6 9anterior Primero de 9 9 9 9 9 la lista 9 9 9 9actual Segundo de 9 9 9 9 9 9 9 6 la lista 9 9 9 9 9 Recorre 9anterior 9 Le toca al 9 actual 9 9 8 9 9 9 9 9 9 9 principio la lista 9 9 9 9 9 9 9 9 8 9 de la lista 9 (mientras 9 9 9 9 9 9 9 9 9 9 9 la llave 9 9 9 9 9 9 9 9 9 9 siguiente 9 sea menor 9actual 9 9 9 9 9 9 9 9 9 9 9 9 9 9 o igual) 9 7 9 9 9 9 9 9 9 9 9 7 7 Insrtalo entre anterior y actual e

Debemos insistir en que se debe tener cuidado el orden en que se cambian las referencias. Las referencias que vamos a modicar son la de nuevo y la siguiente en anterior, que es la misma que tenemos almacenada en actual. Por ello, el orden para cambiar las referencias podr haber sido a
138: 139: a n t e r i o r . p o n S i g u i e n t e ( nuevo ) ; nuevo . p o n S i g u i e n t e ( a c t u a l ) ;

8.3 *Ordenamiento usando rboles a

265

Lo que debe quedar claro es que una vez modicado anterior.siguiente, esta referencia ya no se puede usar para colocarla en nuevo.siguiente.

8.3 *Ordenamiento usando rboles a


Mantener una lista de cadenas ordenada alfabticamente es costoso si la lista e la tenemos ordenada en un arreglo o en una lista ligada. El costo se expresa en trminos de operaciones que debemos realizar para llevar a cabo una insercin, e o remocin, modicacin, etc. de la lista. Por ejemplo, cuando tenemos la lista en o o un arreglo, agregar a alguien quiere decir: Encontrarle lugar Recorrer a todos los que estn despus de l un lugar, para hacerle lugar al a e e nuevo. Copiar el nuevo a ese lugar. Similarmente, para quitar a alguien de la lista, debemos recorrer a todos los que se encuentren despus de l para mantener a las cadenas en espacio contiguo. e e Para el caso de listas ligadas, la remocin e insercin de registros es un poco o o ms econmica, pero para encontrar a alguien en promedio tenemos que hacer a o n{2 comparaciones, donde n es el tamao de la lista. n Deseamos mantener la lista ordenada, pero queremos bajar los tiempos de localizacin de un registro, asumiendo que sta es la operacin ms frecuente o e o a que deseamos hacer. Un mtodo bastante eciente de almacenar informacin que e o se debe mantener ordenada (con respecto a algn criterio) es el de utilizar una u estructura de datos llamada rbol binario. a Empecemos por denir lo que es un rbol, para pasar despus a ver, espec a e camente, a los rboles binarios. Un rbol es una estructura de datos dinmica, a a a no lineal, homognea. Est compuesta por nodos y los nodos estn relacionados e a a entre s de la siguiente manera: Cada nodo tiene a un unico nodo apuntando a l. A ste se le llama el nodo e e padre. Cada nodo apunta a cero o ms nodos. A estos nodos se les llama los hijos. a A los nodos que no tienen hijos se les llama hojas. Existe un unico nodo privilegiado, llamado la raz del rbol, que no tiene a padre: corresponde al punto de entrada de la estructura. A cada nodo le corresponde un nivel en el rbol, y se calcula sumndole 1 a a al nivel de su padre. La ra tiene nivel 1. z

266

Ordenamientos usando estructuras de datos

La profundidad del rbol es el mximo de los niveles de sus nodos. a a Cada nodo del rbol va a tener un campo para la informacin que deseamos a o organizar, y n referencias, donde n es el nmero mximo de hijos que puede u a tener un nodo. Un rbol es n-ario si el mximo nmero de hijos que puede tener es n. Es a a u binario, si el mximo nmero de hijos es 2; terciario para tres hijos, y as sucea u sivamente. Podemos representar rboles con un nmero arbitrario de hijos para a u cada nodo, pero dejaremos ese tema para cursos posteriores. Tambin podemos e denir los rboles recursivamente: a
6 9Un nodo que no tiene hijos 8 Un rbol n-ario es a 9 7

Un nodo que tiene como hijos a n rboles a

A esta denicin le corresponde el esquema en la gura 8.5. o

Figura 8.5

Denicin recursiva de un rbol o a


ra z ......
Info

Arbol

Arbol

Arbol
En estos momentos revisaremos unicamente a los rboles binarios, por ser stos a e un mecanismo ideal para organizar a un conjunto de datos de manera ordenada. Un rbol binario, entonces, es un rbol donde el mximo nmero de hijos para a a a u cada nodo es dos. Si lo utilizamos para organizar cadenas, podemos pensar que dado un rbol, cada nodo contiene una cierta cadena. Todas las cadenas que se a encuentran en el subrbol izquierdo son menores a la cadena que se encuentra en a la ra Todas las cadenas que se encuentran en el subrbol derecho, son mayores z. a

8.3 *Ordenamiento usando rboles a

267

a la que se encuentra en la ra Un rbol binario bien organizado se muestra en z. a la gura 8.6.

Figura 8.6

Arbol binario bien organizado


H

Cuando todos los nodos, excepto por las hojas del ultimo nivel, tienen exac tamente el mismo nmero de hijos, decimos que el rbol est completo. Si la prou a a fundidad del subrbol izquierdo es la misma que la del subrbol derecho, decimos a a que el rbol est equilibrado 2 . El rbol del esquema anterior no est ni completo a a a a ni equilibrado. Para el caso que nos ocupa, la clase correspondientes a cada Estudiante vuelve a extender a la clase InfoEstudiante, agregando las referencias para el subrbol a izquierdo y derecho, y los mtodos de acceso y manipulacin de estos campos. La e o programacin se puede ver en el listado 8.15 en la siguiente pgina. o a Como se ve de la declaracin del registro ArbolEstudiante, tenemos una eso tructura recursiva, donde un registro de estudiante es la informacin, con dos o referencias a registros de estudiantes.
2

En ingls, balanced e

268

Ordenamientos usando estructuras de datos

Cdigo 8.15 Clase ArbolEstudiante para cada registro o nodo o


1: c l a s s A r b o l E s t u d i a n t e extends I n f o E s t u d i a n t e { 2: / R e f e r e n c i a s a s u b r b o l i z q u i e r d o y d e r e c h o \ a 3: protected ArbolEstudiante izqrdo , 4: / 5: C o n s t r u c t o r con l a i n f o r m a c i n como a r g u m e n t o s . o 6: / 7: p u b l i c A r b o l E s t u d i a n t e ( S t r i n g nmbre , S t r i n g c nt a , 8: String crrera ) { 9: super ( nmbre , c nt a , c r r e r a ) ; 10: izqrdo = dercho = null ; 11: } 12: / 13: Proporciona la r e f e r e n c i a del subrbol izquierdo . a 14: / 15: public ArbolEstudiante getIzqrdo () { 16: return izqrdo ; 17: } 18: / 19: Proporciona l a r e f e r e n c i a al subrbol derecho . a 20: / 21: public ArbolEstudiante getDercho () { 22: return dercho ; 23: } 24: / 25: Actualiza el subrbol izquierdo . a 26: / 27: p u b l i c v o i d s e t I z q r d o ( A r b o l E s t u d i a n t e nuevo ) { 28: i z q r d o = nuevo ; 29: } 30: / 31: Actualiza e l subrbol derecho . a 32: / 33: p u b l i c v o i d s e t D e r c h o ( A r b o l E s t u d i a n t e nuevo ) { 34: d e r c h o = nuevo ; 35: } 36: }

(ArbolEstudiante)

8.3.1.

Construccin de rboles binarios para ordenamientos o a


Los rboles de ordenamiento se van construyendo conforme se van insertando a los nodos. Cuando se inicia la insercin y el rbol est vac el primer dato que o a a o, llega se coloca en la ra A partir de ese momento, si el dato nuevo es menor al z.

8.3 *Ordenamiento usando rboles a

269

de la ra va a quedar en el subrbol izquierdo, mientras que si no es menor va z, a a quedar en el subrbol derecho. Esto quiere decir que si dos registros tienen la a misma llave (en este caso, nombres iguales pero que el resto de la informacin o diere) el segundo que llegue va a quedar a la derecha del primero que lleg (en o el subrbol derecho del nodo que contiene al que lleg primero). En el diagrama a o del rbol binario que dimos arriba, y si consideramos que la llave es la letra que a aparece, un posible orden de llegada de los registros pudo ser H, P, Y, R, M, A, E, C, F, D, T, B, N. No cualquier orden de llegada produce el mismo rbol. En este ejemplo, si los registros van llegando ya ordenados a de menor a mayor, el rbol que se obtiene es el que se puede ver en la gura 8.7. a

Figura 8.7

Arbol que se forma si los registros vienen ordenados


A B D E F G H M N P R T Y Esto es lo que conocemos como un rbol degenerado, pues degener en una a o lista. Esta es una situacin poco afortunada, pues cuando utilizamos un rbol para o a ordenar, queremos que los datos lleguen con un cierto desorden, que nos garantiza

270

Ordenamientos usando estructuras de datos

un rbol ms equilibrado. Dados n registros en un rbol, la menor profundidad a a a se alcanza cuando el rbol est equilibrado, mientras que la mayor profundidad se a a alcanza cuando tenemos un rbol degenerado como el que acabamos de mostrar. a Hay varios resultados importantes respecto a rboles binarios que se reejan en la a eciencia de nuestras bsquedas. En un rbol equilibrado con n nodos, una hoja u a est, en promedio, a distancia log2 n; mientras que la distancia de la cabeza de a una lista a alguno de sus elementos es en promedio de n{2. Una forma de buscar que el rbol quede equilibrado es mediante el uso de una a ra que contenga un dato fantasma, y que este dato fantasma resulte ser un buen z dato de en medio para el rbol. Por ejemplo, dado que la M se encuentra a a la mitad del alfabeto, un buen dato fantasma para la ra pudiera ser un registro z con Ms en l. De esa manera, al menos la mitad de los datos quedar a su e a izquierda y la otra mitad a su derecha. En general, asumimos que los datos vienen desordenados, y en este caso tenemos una distribucin adecuada para armar el o a rbol.

8.3.2.

La clase ArbolOrden
Queremos reutilizar en la medida de lo posible las clases que tenemos tanto para el manejo de la estructura de datos como del men que invoca a los mtodos u e de aqulla. Para ello mantendremos las rmas de los mtodos pblicos de la clase e e u ListaCurso, excepto que en todo lugar donde aparece un objeto de la clase Estudiante nosotros vamos a usar uno de la clase ArbolEstudiante. Hay dos mtodos en e esta clase que no vamos a utilizar porque la unica manera en que vamos a meter registros es manteniendo el orden y que son agregaEst y agregaEstFinal. Lo que vamos a hacer con estos dos mtodos es que regresen el valor false ya que no va a e ser cierto que agreguemos as . Otro aspecto importante es que como la estructura de datos es recursiva, lo ideal es manejarla con mtodos recursivos. Pero como la rma de cada uno de los e mtodos pblicos es ja, lo que haremos en cada uno de los mtodos pblicos es e u e u desde all llamar por primera vez al mtodo recursivo que va a hacer el trabajo, y e el mtodo recursivo, que va a ser privado, es el que se va a encargar de todo. e

8.3.3.

Insercin o
El algoritmo que nos permite insertar un registro en el rbol es como se muestra a en el diagrama de la gura 8.8 en la pgina opuesta. Queda programado, de a

8.3 *Ordenamiento usando rboles a

271

acuerdo al algoritmo anterior, como se puede apreciar en el listado 8.16 en la siguiente pgina. a Del algoritmo en la gura 8.8 podemos ver que unicamente podemos agregar un registro cuando le encontramos lugar como una hoja. Lo que tenemos que hacer es ir bajando por la izquierda o la derecha del rbol, dependiendo del resultado a de comparar al registro que se desea agregar con el que se encuentra en la ra del z subrbol en turno. El algoritmo se traduce bastante directamente a cdigo. a o

Figura 8.8

Agregar un registro manteniendo el orden

6 9 9 9 6 9 9 9 8 9 9 9Arbol vac 9 o Colcalo en la ra 9 o z 9 7 9 9 9 9 9 9 9 9 6 6 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 Hay subrbol 9 9 a 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 izquierdo 9 9 9 9 Dato menor 9 9 9 9 9 8 9 9 9 9 9 9 9 que el de la 9 9 9 9 9 Inserta 9 9 9 9 9 9 9 9 8 ra z 9 9 nodo 9 9 9 9 9 9 9 9 en 9 9 9 9 Hay subrbol 9 9 9 a 9 9 9 8 9 a rbol 9 9 9 9 9 izquierdo 9Arbol vac o 9 9 9 9 9 9 9 7 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 6 9 9 9 9 9 9 a 9 Hay subrbol 9 9 9 9 9 9 9 9 9 9 9 9 9 9 derecho 9 9 9 9 Dato menor 9 9 9 9 9 8 9 9 9 9 9 9 9 que el de la 9 9 9 9 9 9 9 9 9 9 9 9 9 9 ra z 9 9 9 9 Hay subrbol 9 9 9 a 9 9 9 9 9 9 9 9 7 7 9 9 derecho 9 9 9 7

6 9 9 9 8 7 6 9 9 9 8 7 5

Inserta registro en el 9 subrbol izquierdo a 9 9

Coloca el registro como 9 subrbol izquierdo a 9 9

Inserta registro en el subrbol derecho a


6 9 8 9 7

Coloca el registro como subrbol derecho a

272

Ordenamientos usando estructuras de datos

Cdigo 8.16 Agregar un registro en un rbol binario ordenado o a

(ArbolOrden)

1: p u b l i c c l a s s A r b o l O r d e n { 2: / La r a z d e l r b o l / a 3: private ArbolEstudiante r a i z ; 4: private S t r i n g grupo ; 5: p r i v a t e i n t numRegs ; 6: / Agrega un r e g i s t r o a l f i n a l de l a l i s t a 7: @param A r b o l E s t u d i a n t e nuevo E l r e g i s t r o a a g r e g a r . 8: / 9: p u b l i c boolean a g r e g a E s t F i n a l ( A r b o l E s t u d i a n t e nuevo ) { 10: return f a l s e ; 11: } 12: / Agrega un r e g i s t r o a l p r i n c i p i o de l a l i s t a 13: @param I n f o E s t u d i a n t e nuevo A q u i e n s e va a a g r e g a r 14: / 15: p u b l i c boolean a g r e g a E s t ( A r b o l E s t u d i a n t e nuevo ) { 16: return f a l s e ; 17: } 18: / Agrega un r e g i s t r o donde l a t o c a p a r a m an t e n e r l a l i s t a 19: ordenada 20: @param A r b o l E s t u d i a n t e nuevo A q u i e n s e va a a g r e g a r 21: @ r e t u r n s b o o l e a n S i pudo o no a g r e g a r 22: / 23: p u b l i c boolean a g r e g a E s t O r d e n ( A r b o l E s t u d i a n t e nuevo ) { 24: i f ( r a i z == n u l l ) { // Es e l p r i m e r o 25: r a i z = nuevo ; 26: return true ; 27: } 28: r e t u r n meteEnArbol ( r a i z , nuevo ) ; 29: } 30: / Tengo l a s e g u r i d a d de l l e g a r ac con r a i z != n u l l / a 31: p r i v a t e boolean meteEnArbol ( A r b o l E s t u d i a n t e r a i z , 32: A r b o l E s t u d i a n t e nuevo ) { 33: i n t compara = nuevo . daNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) 34: . compareTo ( r a i z . daNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) ) ; 35: i f ( compara < 0 && r a i z . g e t I z q r d o ( ) == n u l l ) { 36: r a i z . p o n I z q r d o ( nuevo ) ; 37: return true ; 38: } 39: i f ( compara >= 0 && r a i z . g e t D e r c h o ( ) == n u l l ) { 40: r a i z . s e t D e r c h o ( nuevo ) ; 41: return true ; 42: } 43: i f ( compara < 0 ) 44: r e t u r n meteEnArbol ( r a i z . g e t I z q r d o ( ) , nuevo ) ; 45: else 46: r e t u r n meteEnArbol ( r a i z . g e t D e r c h o ( ) , nuevo ) ; 47: }

8.3 *Ordenamiento usando rboles a

273

8.3.4.

Listar toda la base de datos


Tenemos tres posibles recorridos para listar la informacin en un rbol binao a rio ordenado, dependiendo de cul sea el orden que sigamos para ir listando el a contenido de cada registro: Recorrido en preorden Recorrido simtrico e Recorrido en postorden El siguiente algoritmo que vamos a revisar es el que se encarga de mostrar o recorrer la lista. Si nuestra lista unicamente tuviera tres registros, y el rbol a estuviera equilibrado, se ver como en la gura 8.9 (el orden de llegada pudo a haber sido primero Anita y despus Octavio, pero si hubiera llegado primero e Anita u Octavio el rbol no se hubiera armado equilibrado). a

Figura 8.9

Ejemplo simple para recorridos de rboles a


ra z Manuel

Anita

Octavio

Como podemos ver en este esquema, para listar el contenido de los nodos en orden tenemos que recorrer el rbol de la siguiente manera: a Muestra el hijo izquierdo Muestra la ra z Muestra el hijo derecho y este orden de recorrido nos entrega la lista en el orden adecuado. A este recorrido se le conoce como inorden o simtrico, pues la ra se visita entre los dos hijos. e z Tenemos otros rdenes de recorrido posibles. Por ejemplo, si visitramos primero o a a la ra despus al hijo izquierdo y despus al hijo derecho, tendr z, e e amos lo que se conoce como preorden. Y si primero visitramos a los dos hijos y por ultimo a la a ra tendr z, amos lo que se conoce como postorden o notacin polaca (este nombre o se utiliza porque los rboles son muy utiles no nada ms para ordenamientos, sino a a que se utilizan para denotar la precedencia en operaciones aritmticas). Dado que e

274

Ordenamientos usando estructuras de datos

los rboles son estructuras recursivas, el hijo izquierdo es, a su vez, un rbol (lo a a mismo el hijo derecho). Por ello, si pensamos en el caso ms general, en que el a hijo izquierdo pueda ser un rbol tan complicado como sea y el hijo derecho lo a mismo, nuestro algoritmo para recorrer un rbol binario arbitrario quedar como a a se muestra en el esquema de la gura 8.10.

Figura 8.10

Recorrido simtrico de un rbol e a


3 6 9Hay hijo izquierdo Visita subrbol izquierdo a 9 9 9 9 9 9 9 9 6 9 9 9 8 9 9 9Hay hijo izquierdo 9 9 9 7 9 9 8

Visita subrbol a

3 9 9 9Hay hijo derecho 9 Visita subrbol derecho a 9 9 9 9 9 9 9 6 9 9 9 8 9 9 9 9Hay hijo derecho 9 9 7 7

Procesa la ra z

En nuestro caso, el proceso del nodo ra de ese subrbol en particular consiste z a en escribirlo. El mtodo pblico que muestra toda la lista queda con la rma como e u la ten en las otras dos versiones que hicimos, y programamos un mtodo privado a e que se encargue ya propiamente del recorrido recursivo, cuya rma ser a
p r i v a t e v o i d l i s t a A r b o l ( C o n s o l a cons , A r b o l E s t u d i a n t e r a i z )

Debemos recordar que ste es un mtodo privado de la clase ArbolOrden, por e e lo que siempre que lo invoquemos tendr impl a cito un objeto de esta clase como primer argumento. Los mtodos listaTodos y listaArbol quedan programados como e se muestra en los listados 8.17 en la pgina opuesta y 8.18 en la pgina opuesta a a respectivamente.

8.3 *Ordenamiento usando rboles a

275 (ArbolOrden)

Cdigo 8.17 Listado de la base de datos completa o


208: 209: 210: 211: 212: 213: 214: 215: 216: 217:

/ L i s t a t o d o s l o s r e g i s t r o s d e l C u r s o . @param C o n s o l a c o n s d nde e s c r i b i r . o / public void l i s t a T o d o s ( Consola cons ) { i f ( r a i z == n u l l ) { c o n s . i m p r i m e l n ( "No hay registros en la base de datos " ) ; return ; } l i s t a A r b o l ( cons , r a i z ) ; }

Cdigo 8.18 Recorrido simtrico del rbol o e a


218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228:

(ArbolOrden)

/ R e c o r r i d o s i m t r i c o r e c u r s i v o de un r b o l . e a @param C o n s o l a c o n s Donde e s c r i b i r . A r b o l E s t u d i a n t e r a i z La r a z d e l s u b r b o l . a / p r i v a t e v o i d l i s t a A r b o l ( C o n s o l a cons , A r b o l E s t u d i a n t e r a i z ) i f ( r a i z == n u l l ) return ; l i s t a A r b o l ( cons , r a i z . g e t I z q r d o ( ) ) ; cons . imprimeln ( r a i z . d a R e g i s t r o ( ) ) ; l i s t a A r b o l ( cons , r a i z . g e t D e r c h o ( ) ) ; }

8.3.5.

Conservacin de la aleatoriedad de la entrada o


Quisiramos guardar el contenido del rbol en disco, para la prxima vez eme a o pezar a partir de lo que ya tenemos. Si lo guardamos en orden, cuando lo volvamos a cargar va a producir un rbol degenerado, pues ya vimos que lo peor que nos a puede pasar cuando estamos cargando un rbol binario es que los datos vengan en a orden (ya sea ascendente o descendente). Por ello, tal vez ser ms conveniente a a recorrer el rbol de alguna otra manera. Se nos ocurre que si lo recorremos en a preorden, vamos a producir una distribucin adecuada para cuando volvamos a o cargar el directorio. Esta distribucin va a ser equivalente a la original, por lo que o estamos introduciendo un cierto factor aleatorio. El algoritmo es igual de sencillo que el que recorre en orden simtrico y se muestra en la gura 8.11 en la siguiente e pgina. a

276

Ordenamientos usando estructuras de datos

Figura 8.11

Recorrido en preorden de un rbol a


6 9Procesa la ra z 9 3 9 9 9Hay hijo izquierdo 9 Recrrelo en preorden o 9 9 9 9 9 9 9 5 9 9 9 9 9Hay hijo izquierdo 9 9 9 8

Recorrido en preorden 9 9

9 9Hay hijo derecho 9 9 9 9 9 9 9 9 9 9 9 9 9 9Hay hijo derecho 9 9 9 7

Recrrelo en preorden o
6 8 7

8.3.6.

Bsquedas u

Para encontrar un registro dado en el rbol, debemos realizar algo similar a a cuando lo insertamos. Compara con la llave de la ra Si son iguales, ya lo enconz. tramos. Si el que busco es menor, recursivamente busco en el subrbol izquierdo. a Si el que busco es mayor, recursivamente busco en el rbol derecho. Decido que a no est en el rbol, si cuando tengo que bajar por algn hijo, ste no existe. La a a u e unica diferencia entre el algoritmo que inserta y el que busca, es la reaccin cuan o do debemos bajar por algn hijo (rama) que no existe. En el caso de la insercin u o tenemos que crear al hijo para poder acomodar ah la informacin. En el caso de o la bsqueda, nos damos por vencidos y respondemos que la informacin que se u o busca no est en el rbol. El algoritmo se muestra en la gura 8.12 en la pgina a a a opuesta. Si no se encuentra al estudiante, el mtodo deber regresar una referencia nula. e a La programacin de este mtodo se puede apreciar en el listado 8.19 en la pgina o e a opuesta.

8.3 *Ordenamiento usando rboles a

277

Figura 8.12

Bsqueda en un rbol ordenado u a

3 6 9nombre = campo Regresa esta ra z 9 9 9 9 9 9 9 5 9 6 9 9 9 Encuentra nombre en 9 Hay hijo 9 9 9 9 9 9 9 9 izquierdo 9 subrbol izquierdo a 9 9 9 9 9 9 8 9 9 9nombre 6 9 campo 9 9 9 9 9 9 9 9 Hay hijo 8 9 9 9 9 9 9 8 9 9 izquierdo 9 Encuentra nombre 7 7 en subrbol a 9 9 9 9 9 5 9 6 9 9 9 Encuentra nombre en 9 Hay hijo 9 9 9 9 9 9 9 9 derecho 9 subrbol derecho a 9 9 9 9 9 9 8 9 9 9nombre 6 9 campo 9 9 9 9 9 9 9 9 Hay hijo 8 9 9 9 9 9 9 9 9 9 9 derecho 9 9 7 7 7

Cdigo 8.19 Bsqueda del registro con el nombre dado o u


229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246:

(ArbolOrden)

/ L o c a l i z a e l nodo d e l r b o l en e l que e s t e s t e nombre . a a @param A r b o l E s t u d i a n t e r a i z La r a z d e l s u b r b o l en e l que va a a buscar . @param S t r i n g nombre E l nombre que e s t b u s c a n d o . a @ r e t u r n s A r b o l E s t u d i a n t e La r e f e r e n c i a de donde s e e n c u e n t r a e s t e r e g i s t r o , o n u l l s i no l o e n c o n t r . o / p r i v a t e A r b o l E s t u d i a n t e buscaDonde ( A r b o l E s t u d i a n t e r a i z , S t r i n g nombre ) { i f ( r a i z == n u l l ) return n u l l ; i n t compara = nombre . t r i m ( ) . t o L o w e r C a s e ( ) . compareTo ( r a i z . daNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) ) ; i f ( compara == 0 ) return r a i z ; i f ( compara < 0 ) r e t u r n buscaDonde ( r a i z . g e t I z q r d o ( ) , nombre ) ; else r e t u r n buscaDonde ( r a i z . g e t D e r c h o ( ) , nombre ) ; }

278

Ordenamientos usando estructuras de datos

Figura 8.13

Bsqueda de una subcadena u


6 9subrbol vac a o 9 9 9 9 9 9 9 9 9 9cadena en ra 9 z 9 9 9 9 9 9 9 9 9 9 8 3

regresa nulo
3

regresa la ra z
5

Busca cadena Hay rbol izquierdo a en subrbol 9 a 9

donde
3

Busca cadena en
subrbol izquierdo a

9 9 9 9 9 9 9donde nulo 9 9 9 9 9 9 9 9 9 9 9 9 9Hay subrbol derecho a 7

Regresa donde
5

Regresa bsqueda en u subrbol derecho a

El otro tipo de bsqueda que tenemos en nuestro programa es el de localizar a u algn registro que contenga a la subcadena pasada como parmetro, en el campo u a pasado como parmetro. Para dar satisfaccin a esta solicitud debemos recorrer el a o a rbol hasta encontrar la subcadena en alguno de los registros. El problema es que como se trata de una subcadena no nos ayuda el orden en el rbol. El algoritmo a para esta bsqueda se muestra en la gura 8.13 mientras que la programacin se u o muestra en el listado 8.20.

Cdigo 8.20 Bsqueda de subcadena en determinado campo o u


177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187:

(ArbolOrden)1/2

/ Busca a l r e g i s t r o que c o n t e n g a a l a s u b c a d e n a . @param i n t c u a l C u a l e s e l campo que s e va a c o m pa r ar . @param S t r i n g s u b c a d La c a d e n a que s e e s t b u s c a n d o . a @ r e t u r n s i n t E l r e g i s t r o d e s e a d o o 1. / public A r b o l E s t u d i a n t e buscaSubcad ( i n t cual , S t r i n g subcad ) subcad = subcad . trim ( ) . toLowerCase ( ) ; r e t u r n p r e O r d e n ( c u a l , subcad , r a i z ) ; }

8.3 *Ordenamiento usando rboles a

279 (ArbolOrden) 2/2

Cdigo 8.20 Bsqueda de subcadena en determinado campo o u


177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204:

/ R e c o r r e e l r b o l en p r e o r d e n h a s t a e n c o n t r a r una s u b c a d e n a . a @param i n t c u a l Campo en e l c u a l b u s c a r . @param S t r i n g s u b c a d Cadena a l o c a l i z a r . @param A r b o l E s t u d i a n t e r a i z R az d e l r b o l donde va a b u s c a r . a @ r e t u r n s A r b o l E s t u d i a n t e E l p r i m e r r e g i s t r o que cumpla / p r i v a t e A r b o l E s t u d i a n t e p r e O r d e n ( i n t c u a l , S t r i n g subcad , ArbolEstudiante raiz ) { i f ( r a i z == n u l l ) { return n u l l ; } S t r i n g elCampo = r a i z . daCampo ( c u a l ) . t r i m ( ) . t o L o w e r C a s e ( ) ; i f ( elCampo . i n d e x O f ( s u b c a d ) != 1) { return r a i z ; } A r b o l E s t u d i a n t e donde = n u l l ; i f ( r a i z . g e t I z q r d o ( ) != n u l l ) { donde = p r e O r d e n ( c u a l , subcad , r a i z . g e t I z q r d o ( ) ) ; } i f ( donde != n u l l ) { r e t u r n donde ; } i f ( r a i z . g e t D e r c h o ( ) != n u l l ) { r e t u r n p r e O r d e n ( c u a l , subcad , r a i z . g e t D e r c h o ( ) ) ; } else { return n u l l ; } }

8.3.7.

Listado condicionado de registros

Otro mtodo importante en nuestro sistema es el listado de aquellos registros e que contengan, en el campo elegido, una subcadena. En este caso, al igual que en el listado de todos los registros, deberemos recorrer todo el rbol en orden a simtrico, de tal manera que al llegar a cada nodo, si es que el nodo contiene a e la subcadena en el lugar adecuado, lo listemos, y si no no. El algoritmo para este mtodo se parece mucho al que lista a todos los registros, excepto que al visitar la e ra antes de imprimir, verica si cumple la condicin. El algoritmo se muestra en z, o la gura 8.14 en la siguiente pgina. La programacin de este algoritmo se puede a o ver en el listado 8.21 en la siguiente pgina. a

280

Ordenamientos usando estructuras de datos

Figura 8.14

Seleccin de registros que cumplen una condicin o o

6 5 9 Visita subrbol a 9 9Hay hijo izquierdo 9 9 9 izquierdo 9 9 9 9 9 9 9 6 9 9 9 8 9 9 9Hay hijo izquierdo 9 9 9 7 9 9 9 6 9 9 9 9 subcadena en 3 9 9 9 9 9 Reporta el registro 9 9 9 9 ra z 9 9 8 8 Visita subrbol Procesa la ra a z 5 9 9 9 9 9 subcadena en 9 9 9 9 9 9 9 7 9 9 ra z 9 9 5 9 9 9 Visita subrbol a 9 9Hay hijo derecho 9 9 9 derecho 9 9 9 9 9 9 9 6 9 9 9 8 9 9 9Hay hijo derecho 9 9 9 7 7

Cdigo 8.21 Listado de registros que contienen a una subcadena o


227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243:

(ArbolOrden) 1/2

/ Imprim e l o s r e g i s t r o s que c a z a n con un c i e r t o p a t r n . o @param C o n s o l a c o n s D i s p o s i t i v o en e l que s e va a e s c r i b i r . @param i n t c u a l Con c u l campo s e d e s e a c o m pa r ar . a @param S t r i n g s u b c a d Con e l que queremos que c a c e . / p u b l i c v o i d losQueCazanCon ( C o n s o l a cons , i n t c u a l , S t r i n g subcad ) { subcad = subcad . trim ( ) . toLowerCase ( ) ; int cuantos = 0; i f ( r a i z == n u l l ) c o n s . i m p r i m e l n ( "No hay registros en la base de" + " datos " ) ; else c u a n t o s = b u s c a C a z a n ( cons , c u a l , subcad , r a i z ) ; i f ( c u a n t o s == 0 ) c o n s . i m p r i m e l n ( "No hay registros que cacen ." ) ; }

8.3 *Ordenamiento usando rboles a

281 (ArbolOrden) 2/2

Cdigo 8.21 Listado de registros que contienen a subcadena o


244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263:

/ R e c o r r e e l r b o l v e r i f i c a n d o s i c a z a n . a @param C o n s o l a c o n s Medio p a r a e s c r i b i r . i n t c u a l . Campo en e l que s e va a b u s c a r . S t r i n g s u b c a d Cadena a b u s c a r . A r b o l E s t u d i a n t e r a i z R az d e l r b o l a r e c o r r e r . a @ r e t u r n s i n t E l t o t a l de r e g i s t r o s que c a z a r o n / p r i v a t e i n t b u s c a C a z a n ( C o n s o l a cons , i n t c u a l , S t r i n g subcad , ArbolEstudiante raiz ) { int este = 0; i f ( r a i z == n u l l ) return 0; e s t e = b u s c a C a z a n ( cons , c u a l , subcad , r a i z . g e t I z q r d o ( ) ) ; i f ( r a i z . daCampo ( c u a l ) . t r i m ( ) . t o L o w e r C a s e ( ) . i n d e x O f ( s u b c a d ) != 1) { cons . imprimeln ( r a i z . d a R e g i s t r o ( ) ) ; e s t e ++; } e s t e += b u s c a C a z a n ( cons , c u a l , subcad , r a i z . g e t D e r c h o ( ) ) ; return e s t e ; }

8.3.8.

Eliminacin de nodos o

Hemos llegado a la parte ms complicada de manejar en un rbol binario, a a que es la eliminacin de alguno de los nodos del rbol. Si el nodo es una hoja, o a realmente no hay ningn problema: simplemente hay que regresar al padre del u nodo y nulicar el apuntador a esa hoja. Una manera de localizar al padre de un nodo es el volver a realizar la bsqueda u desde la ra pero recordando en todo momento al nodo anterior que se visit. z, o Otra manera es la de poner a cada nodo a apuntar hacia su padre, lo que se puede hacer trivialmente en el momento de insertar a un nodo, pues al insertarlo tenemos un apuntado al padre (de quien lo vamos a colgar) y un apuntador al hijo (a quien vamos a colgar). Optamos por la primera solucin, para no modicar ya o el registro de cada estudiante. La programacin del mtodo que localiza al padre o e se da en el listado 8.22 en la siguiente pgina. El algoritmo es similar al que se a dio para la bsqueda de una subcadena, por lo que ya no lo mostramos. u

282

Ordenamientos usando estructuras de datos

Cdigo 8.22 Localizacin del padre de un nodo o o


147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169:

(ArbolOrden)

/ Busca a l p a d r e de un nodo , r e c o r r i e n d o e l r b o l d e s d e l a a r a z . @param A r b o l E s t u d i a n t e r a i z R az d e l s u b r b o l a A r b o l E s t u d i a n t e deQuien A q u i e n s e l e b u s c a e l padre @ r e t u r n s A r b o l E s t u d i a n t e La r e f e r e n c i a d e l p a d r e / private ArbolEstudiante buscaPadre ( ArbolEst udiante r a i z , A r b o l E s t u d i a n t e deQuien ) { ArbolEstudiante padre = n u l l ; i f ( r a i z . g e t I z q r d o ( ) == deQuien ) return r a i z ; i f ( r a i z . g e t D e r c h o ( ) == deQuien ) return r a i z ; i f ( r a i z . g e t I z q r d o ( ) != n u l l ) p a d r e = b u s c a P a d r e ( r a i z . g e t I z q r d o ( ) , deQuien ) ; i f ( p a d r e == n u l l && r a i z . g e t D e r c h o ( ) != n u l l ) r e t u r n b u s c a P a d r e ( r a i z . g e t D e r c h o ( ) , deQuien ) ; else return padre ; }

Como dijimos, una vez que se tiene al padre de una hoja, todo lo que hay que hacer es identicar si el nodo es hijo izquierdo o derecho y poner el apuntador correspondiente en null. Resuelta la eliminacin de una hoja, pasemos a ver la parte ms complicada, o a que es la eliminacin de un nodo intermedio. Veamos, por ejemplo, el rbol de la o a gura 8.6 en la pgina 267 y supongamos que deseamos eliminar el nodo etiquetado a con E. Cmo reacomodamos el rbol de tal manera que se conserve el orden o a correcto? La respuesta es que tenemos que intercambiar a ese nodo por el nodo menor de su subrbol derecho. Por qu? Si colocamos al nodo menor del subrbol a e a derecho en lugar del que deseamos eliminar, se sigue cumpliendo que todos los que estn en el subrbol izquierdo son menores que l, mientras que todos los que estn e a e e en el subrbol derecho son mayores o iguales que l: a e Para localizar el elemento menor del subrbol derecho simplemente bajamos a a la ra del subrbol derecho y de ah en adelante seguimos bajando por las ramas z a izquierdas hasta que ya no haya ramas izquierdas.El algoritmo para encontrar el elemento menor de un subrbol se encuentra en la gura 8.15 en la pgina opuesta. a a La programacin de este mtodo se muestra en el listado 8.23. o e

8.3 *Ordenamiento usando rboles a

283

Figura 8.15

Algoritmo para encontrar el menor de un subrbol a


6 9Baja a hijo derecho 9 9 8

Encuentra el menor

Baja por hijo izquierdo 9 (mientras haya) 9 9 7 Regresa el ultimo visitado

Cdigo 8.23 Localiza al menor del subrbol derecho o a


138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: / L o c a l i z a a l menor de un s u b r b o l . a @param A r b o l E s t u d i a n t e r a i z La r a z d e l s u b r b o l a @ r e t u r n s A r b o l E s t u d i a n t e La r e f e r e n c i a d e l menor / p r i v a t e A r b o l E s t u d i a n t e daMenor ( A r b o l E s t u d i a n t e r a i z ) A r b o l E s t u d i a n t e menor = r a i z ; w h i l e ( menor . g e t I z q r d o ( ) != n u l l ) menor = menor . g e t I z q r d o ( ) ; r e t u r n menor ; }

(ArbolOrden)

Una vez localizado el menor del subrbol derecho, lo intercambiamos con el a nodo que queremos eliminar, en este caso E. Con este intercambio, logramos mover a E hacia una hoja, y entonces ya podemos eliminarlo fcilmente. Cabe a aclarar que para intercambiar dos nodos preservando su estructura, basta con intercambiar el campo correspondiente a la informacin, dejando las referencias o sin tocar. Pero puede muy fcil suceder que el menor de un subrbol no sea a a forzosamente una hoja. Entonces, lo que debemos hacer con l es, nuevamente e intercambiarlo con el menor de su subrbol derecho. a Pudiera presentarse el caso de que el nodo no tuviera subrbol izquierdo. En a ese caso, debemos subir el subrbol derecho al lugar que ocupa ahora el nodo a que deseamos eliminar. Por ejemplo, si en el rbol anterior original deseramos a a eliminar al nodo que contiene una A (que no tiene subrbol izquierdo), sima plemente subimos al subrbol etiquetado con D y todas las relaciones entre a los nodos se mantienen. En realidad, si el nodo tiene slo a uno de los hijos (sea o el izquierdo o el derecho) el nodo se elimina fcilmente subiendo al subrbol que a a s tiene a su lugar.

284

Ordenamientos usando estructuras de datos

Otra situacin a considerar es cuando se desea eliminar a la ra del rbol. En o z a ese caso no vamos a tener padre, an cuando el unico nodo en el rbol sea ste. A u a e la ra se le debe tratar de manera un poco diferente. El algoritmo para eliminar z a un nodo se muestra en la gura 8.3.8. La programacin del mtodo se puede ver o e en el listado 8.24 en la pgina opuesta. a

Figura 8.16

Eliminacin de un nodo en un rbol o a


3

6 6 9 9El nodo es hoja 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9El nodo tiene slo o 9 9 9 9 9 9 9 9 9 9 subrbol derecho a 9 9 9 9 9 9 9 9 9 9 9 8 9 9 9nodo == ra z El nodo tiene slo 9 o 9 9 9 9 9 9 9 9 9subrbol izquierdo a 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9El nodo tiene 9 9 9 9 9 9 9 9 9 9 ambos hijos 9 7 9 9 8 Elimina 6 nodo 9 9 9 9El nodo es hoja 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9El nodo tiene slo 9 o 9 9 9 9 9 9 9 9 9 subrbol derecho 9 a 9 9 9 9 9 9 9 9 9 9 8 9 9 9 9nodo == ra z El nodo tiene slo 9 o 9 9 9 9 9 9 9 9subrbol izquierdo 9 a 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9El nodo tiene 9 9 9 9 9 9 9 9 9 9 ambos hijos 9 7 7

Elimina a la ra z
5

Pon al subrbol derecho a en la ra z


5

Pon al subrbol izquierdo a en la ra z


6 9Localiza menor en subrbol derecho a 8 9 7 3

Intercambia a menor y nodo Elimina a quien qued en menor o

Anula el apuntador del padre


5

Sube al subrbol derecho a al lugar del nodo


5

Sube al subrbol izquierdo a al lugar del nodo


6 9Localiza menor en subrbol derecho a 8 9 7

Intercambia a menor y nodo Elimina a quien qued en menor o

8.3 *Ordenamiento usando rboles a

285 (ArbolOrden) 1/3

Cdigo 8.24 Eliminacin de un nodo en el rbol o o a


57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103:

/ D e f i n e s i un nodo d e l r b o l e s h o j a / a p r i v a t e boolean e s H o j a ( A r b o l E s t u d i a n t e nodo ) { i f ( nodo . g e t I z q r d o ( ) == n u l l && nodo . g e t D e r c h o ( ) == n u l l ) return true ; return f a l s e ; } / Q u i t a e l r e g i s t r o s o l i c i t a d o ( p o r nombre ) de l a l i s t a @param S t r i n g nombre E l nombre d e l r e g i s t r o / p u b l i c boolean q u i t a E s t ( S t r i n g nombre ) { A r b o l E s t u d i a n t e donde = buscaDonde ( r a i z , nombre ) ; i f ( donde == n u l l ) return f a l s e ; i f ( donde == r a i z && e s H o j a ( r a i z ) ) { raiz = null ; return true ; } i f ( donde == r a i z . g e t I z q r d o ( ) && e s H o j a ( r a i z . g e t I z q r d o ( ) ) ) { r a i z . ponIzqrdo ( null ) ; return true ; } i f ( donde == r a i z . g e t D e r c h o ( ) && e s H o j a ( r a i z . g e t D e r c h o ( ) ) ) { r a i z . ponIzqrdo ( null ) ; return true ; } r e t u r n b o r r a A ( donde ) ; } p r i v a t e boolean b o r r a A ( A r b o l E s t u d i a n t e donde ) { A r b o l E s t u d i a n t e conQuien ; i f ( donde == r a i z . g e t I z q r d o ( ) && e s H o j a ( r a i z . g e t I z q r d o ( ) ) ) { r a i z . ponIzqrdo ( null ) ; return true ; } i f ( donde == r a i z . g e t D e r c h o ( ) && e s H o j a ( r a i z . g e t D e r c h o ( ) ) ) { r a i z . ponIzqrdo ( null ) ; return true ; } ArbolEstudiante padre ; i f ( donde == r a i z ) padre = n u l l ; else p a d r e = b u s c a P a d r e ( r a i z , donde ) ;

286

Ordenamientos usando estructuras de datos

Cdigo 8.24 Eliminacin de un nodo en el rbol o o a


104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148:

(ArbolOrden)2/3

i f ( donde . g e t I z q r d o ( ) == n u l l && donde . g e t D e r c h o ( ) != n u l l && p a d r e == n u l l ) { // Es l a r a z y t i e n e nada ms s u b r b o l d e r e c h o } a a r a i z = donde . g e t D e r c h o ( ) ; return true ; } i f ( donde . g e t I z q r d o ( ) != n u l l && donde . g e t D e r c h o ( ) == n u l l && p a d r e == n u l l ) { // S l o hay s u b r b o l i z q u i e r d o o a r a i z = donde . g e t I z q r d o ( ) ; return true ; } i f ( p a d r e == n u l l ) { // T i e n e a l o s do s s u b r b o l e s a conQuien = daMenor ( donde . g e t D e r c h o ( ) ) ; donde . ponNombre ( conQuien . daNombre ( ) ) ; donde . ponCuenta ( conQuien . daCuenta ( ) ) ; donde . p o n C a r r e r a ( conQuien . d a C a r r e r a ( ) ) ; r e t u r n b o r r a A ( conQuien ) ; } i f ( donde . g e t I z q r d o ()== n u l l && donde . g e t D e r c h o ( ) == n u l l ) { // Es h o j a i f ( p a d r e . g e t I z q r d o ()==donde ) { padre . ponIzqrdo ( n u l l ) ; return true ; } i f ( p a d r e . g e t D e r c h o ( ) == donde ) { padre . setDercho ( n u l l ) ; return true ; } } i f ( donde . g e t I z q r d o ( ) == n u l l ) { // S l o t i e n e s u b r b o l d e r e c h o o a i f ( p a d r e . g e t I z q r d o ( ) == donde ) { p a d r e . p o n I z q r d o ( donde . g e t D e r c h o ( ) ) ; return true ; } i f ( p a d r e . g e t D e r c h o ( ) == donde ) { p a d r e . s e t D e r c h o ( donde . g e t D e r c h o ( ) ) ; return true ; } }

8.3 *Ordenamiento usando rboles a

287 (ArbolOrden) 3/3


{

Cdigo 8.24 Eliminacin de un nodo en el rbol o o a


149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: i f ( donde . g e t D e r c h o ( ) == n u l l && p a d r e != n u l l ) i f ( p a d r e . g e t I z q r d o ( ) == donde ) { p a d r e . p o n I z q r d o ( donde . g e t I z q r d o ( ) ) ; return true ; } i f ( p a d r e . g e t D e r c h o ( ) == donde ) { p a d r e . s e t D e r c h o ( donde . g e t I z q r d o ( ) ) ; return true ; } } // Ambos s u b r b o l e s e x i s t e n a conQuien = daMenor ( donde . g e t D e r c h o ( ) ) ; donde . ponNombre ( conQuien . daNombre ( ) ) ; donde . ponCuenta ( conQuien . daCuenta ( ) ) ; donde . p o n C a r r e r a ( conQuien . d a C a r r e r a ( ) ) ; r e t u r n b o r r a A ( conQuien ) ; } // b o r r a A

Por ultimo, el mecanismo para modicar algn registro no puede ser, sim u plemente, modicar la informacin, pues pudiera ser que la llave cambiara y el o registro quedara fuera de orden. La estrategia que vamos a utilizar para modicar la informacin de un registro es primero borrarlo y luego reinsertarlo, para evitar o que se modique la llave y se desacomode el rbol. a

8.3.9.

La clase MenuOrden
Utilizamos lo que tenemos programado en MenuLista para copiarlo a MenuOrden. Sustituimos la llamada a agregaEst por una llamada a agregaEstOrden para que los vaya acomodando bien en el rbol. Asimismo, todos los parmetros o vaa a lores de retorno que antes eran de la clase Estudiante ahora pasan a ser de la clase ArbolEstudiante. Adicionalmente, en el mtodo main cuando se declaran y crean los objetos e miCurso y miMenu, se cambian las clases a que ahora sean de las clases ArbolOrden y MenuArbol respectivamente. Como tuvimos mucho cuidado en mantener la interfaz tal cual, con todos los mtodos conservando su rma, no hay necesidad de realizar e ningn otro cambio. u Con esto damos por terminado cmo mantener ordenada una lista de acuerdo o a cierta llave, que corresponde a uno de los campos. Hay muchos algoritmos para ordenar una lista, pero no es material de estos temas, por lo que no lo revisaremos ac. a

Manejo de errores en ejecucin o

9.1 Tipos de errores


Todos hemos padecido en algn momento errores de ejecucin. Java tiene meu o canismos muy poderosos para detectar errores de ejecucin de todo tipo, a los que o llama excepciones. Por ejemplo, si se trata de usar una referencia nula, Java terminar (abortar) el programa con un mensaje de error. Generalmente el mensaje a a tiene la forma: Exception in thread "main" java.lang.NullPointerException at MenuLista.main(MenuLista.java:151) En el primer rengln del mensaje Java nos dice el tipo de excepcin que caus que o o o el programa abortara, que en este caso es NullPointerException, y a partir del segundo rengln aparece la historia de la ejecucin del programa, esto es, los o o registros de activacin montados en el stack de ejecucin en el momento en que o o sucede el error. Hay muchos errores a los que Java va a reaccionar de esta manera, como por ejemplo usar como ndice a un arreglo un entero menor que cero o mayor o igual al tamao declarado (ArrayIndexOutOfBoundsException) o una divisin n o entre 0 (ArithmeticException).

290

Manejo de errores en ejecucin o

La manera como responde la mquina virtual de Java cuando sucede un error a de ejecucin es que lanza lo que se conoce como una excepcin. Una excepcin o o o es un objeto de alguna clase que hereda de la clase Throwable y que contiene informacin respecto a dnde se produjo el error y qu tipo de error es. Dependiendo o o e de la clase a la que pertenece el objeto, fue el tipo de error que hubo. Tambin e el programador puede crear sus propias excepciones, como un mecanismo ms de a control del ujo del programa. Dos clases importantes extienden a la clase Throwable, Error y Exception. El programador puede crear sus propias excepciones extendiendo a la clase Exception1 .

9.1.1.

Excepciones en tiempo de ejecucin (RuntimeException) o


Las excepciones de tiempo de ejecucin son las siguientes: o ArithmeticException Es lanzada cuando el programa intenta hacer una operacin o 2 aritmtica invlida, como la divisin de un entero entre 0 . Podemos ver un e a o ejemplo en el listado 9.1.

Cdigo 9.1 Ejemplo de una excepcin aritmtica o o e


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: import j a v a . i o . ; c l a s s Arit m Ex c { private static int calcula ( int k) return ( ( i n t )1000 / k ) ; }

p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { int k = 0; System . o u t . p r i n t l n ( "El valor de k es " + calcula (0)); } }

En este pequeo programa, el sistema lanzar una ArithmeticException si al n a invocar al mtodo calcula se le pasa el valor de 0. Java no tiene manera, e
Es conveniente que aquellas clases que heredan a la clase Exception su nombre termine con Exception y el resto del nombre sirva para describir el tipo de error que est detectando. a 2 Java contempla el valor innity, por lo que una divisin entre 0 cuyo resultado va a ser real o no lanza esta excepcin. o
1

9.1 Tipos de errores

291 durante la compilacin, de evitar este error aritmtico. Lo que produce la o e ejecucin de este programa se puede ver en la gura 9.1. o

Figura 9.1

Ejecucin de AritmExc o
elisa@lambda Exception in at at elisa@lambda
ArrayStoreException

...ICC1/progs/excepciones % java AritmExc thread "main" java.lang.ArithmeticException: / by zero AritmExc.calcula(AritmExc.java:5) AritmExc.main(AritmExc.java:11) ...ICC1/progs/excepciones %

Es lanzada cuando el programa intenta guardar un objeto de tipo errneo en un arreglo. Veamos el ejemplo en el listado 9.2. o

Cdigo 9.2 Ejemplo de una excepcin de la subclase ArrayStoreException o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: c l a s s ArraySE { private s t a t i c void guardaObjeto ( Object [ ] a , i n t i , Object objeto ) { a [ i ] = objeto ; } p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { I n t e g e r [ ] i n t A r r a y = new I n t e g e r [ 5 ] ; g u a r d a O b j e t o ( i n t A r r a y , 0 , new S t r i n g ( "abc" ) ) ; } }

En la l nea 4:, que es donde se guarda a un objeto en un arreglo, la asignacin es perfectamente compatible. Java no puede detectar en el momento o de compilacin que va a tener un error al llamar a este mtodo, ya que los o e argumentos son de un tipo que extiende a Object, y por lo tanto permitidos. La ejecucin de este programa produce la salida que se muestra en la o gura 9.2.

Figura 9.2

Ejecucin de ArraySE o
elisa@lambda ...ICC1/progs/excepciones % java ArraySE Exception in thread "main" java.lang.ArrayStoreException at ArraySE.guardaObjeto(ArraySE.java:4) at ArraySE.main(ArraySE.java:9) elisa@lambda ...ICC1/progs/excepciones %

292
ClassCastException

Manejo de errores en ejecucin o

Es lanzada cuando el programa intenta aplicar un cast de una clase a un objeto de otra clase, y la conversin no est permitida. El proo a grama en el listado 9.3, al ejecutarse, lanza esta excepcin. o

Cdigo 9.3 Programa que lanza una excepcin ClassCastException o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: c l a s s ClassCE { private s t a t i c void guardaObjeto ( I n t e g e r [ ] a , i n t i , Object objeto ) { a [ i ] = ( Integer ) objeto ; } p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { I n t e g e r [ ] i n t A r r a y = new I n t e g e r [ 5 ] ; g u a r d a O b j e t o ( i n t A r r a y , 0 , new S t r i n g ( "abc" ) ) ; } }

Nuevamente, el programa est sintcticamente correcto ya que en un arreglo a a de objetos de la clase Integer podemos guardar un objeto al que le apliquemos esa envoltura (l nea 4:). Sin embargo, al pasarle como parmetro una cadena, a que es sintcticamente compatible con un objeto, el programa no puede a hacer la coercin, por lo que lanza una excepcin. La ejecucin del programa o o o ClassCE la podemos ver en la gura 9.3.

Figura 9.3

Ejecucin del programa ClassCE o


elisa@lambda ...ICC1/progs/excepciones % java ClassCE Exception in thread "main" java.lang.ClassCastException: java.lang.String at ClassCE.guardaObjeto(ClassCE.java:4) at ClassCE.main(ClassCE.java:9) elisa@lambda ...ICC1/progs/excepciones %
IllegalArgumentException

Esta excepcin es lanzada cuando mtodos de Java o deo e nidos por el usuario detecta que un argumento no es como se esperaba. Por ejemplo, si se desea sacar la ra cuadrada de un nmero negativo. z u IllegalMonitorStateException Tiene que ver con sincronizacin de procesos. Lo vereo mos ms adelante. a IndexOutOfBoundsException Es lanzado cuando se intenta usar un elemento de un arreglo que no existe, porque el ndice dado no est en el rango dado para a los ndices del arreglo.

9.1 Tipos de errores


NegativeArraySizeException

293

Se lanza si es que se intenta crear un arreglo con un nmero negativo de elementos. u NullPointerException Se lanza si se trata de usar al objeto referido por una referencia nula. SecurityException Se lanza cuando el proceso intenta ejecutar un mtodo que no e se puede ejecutar por razones de seguridad. Tiene que ver con el sistema de seguridad (SecurityManager). java.util.EmptyStackException Es lanzada cuando un programa, que utiliza la clase Stack de Java intenta tener acceso a un elemento del stack cuando el stack est vac a o. java.util.NoSuchElementException Error relacionado con enumeraciones. Hay dos aspectos ntimamente relacionados con las excepciones: el primero de ellos corresponde a las condiciones bajo las cuales una excepcin es disparada o o lanzada (thrown), mientras que el otro aspecto tiene que ver con la manera que tendr la aplicacin de reaccionar cuando una excepcin es lanzada. Dependiendo a o o del tipo de excepcin que fue lanzada, la aplicacin debe reaccionar de manera o o adecuada frente a ella y actuar en consecuencia. Algunas veces esta respuesta consistir de alguna forma en que la aplicacin se recupere y siga adelante; otras a o veces simplemente le permitir a la aplicacin morir dignamente. A la reaccin a o o frente a una excepcin se conoce como cachar la excepcin, para poder hacer algo o o con ella. En general, si algn mtodo de alguna clase que estemos utilizando avisa que u e puede lanzar una excepcin, la aplicacin que use esta clase deber prepararse o o a para detectar si se lanz o no la excepcin, y si se lanz, reaccionar de alguna o o o manera frente a ella. A los enunciados que comprenden la reaccin frente a la o excepcin que cachan la excepcin es a lo que se conoce como el manejador o o de la excepcin. o Se recomienda enfticamente que las excepciones de tiempo de ejecucin no a o sean cachadas (RuntimeException). Este tipo de excepcin indica errores de diseo o n en la aplicacin, por lo que si se decide atraparlas esto se deber hacer unicao a mente en una etapa de depuracin del programa, para obtener un poco ms de o a informacin sobre la causa de la excepcin. o o Sabemos que un mtodo es susceptible de lanzar una excepcin si en el encae o bezado del mtodo, a continuacin del parntesis que cierra la lista de parmetros, e o e a aparece el enunciado
throws lista de clases de Excepciones

y en el cuerpo del mtodo, o de alguno de los mtodos invocados dentro de ese e e cuerpo, deber aparecer un enunciado a

294

Manejo de errores en ejecucin o

throw new constructor de una excepcin o

En el caso de las excepciones en tiempo de ejecucin RuntimeException o los mtodos que pudieran incurrir en algn error de ese tipo no tienen que avisar e u que pudieran lanzar este tipo de excepciones. Asimismo, las excepciones sern a lanzadas, en su momento, impl citamente o por el usuario, pero sin necesidad de avisar que pudieran ocurrir. Cuando algn enunciado o mtodo detecta una excepcin, la mquina virtual u e o a 3 de Java busca en su entorno inmediato al manejador de la excepcin. Si lo encueno tra, lo ejecuta, y ya sea que procese la excepcin o que simplemente la propague o (la vuelva a lanzar). Si en el entorno inmediato no se encuentra al manejador de la excepcin, la JVM sigue buscando hacia afuera en la cadena de llamadas del o mtodo en cuestin a ver si encuentra a algn manejador de la excepcin. Si no e o u o lo encuentra en la aplicacin se lo pasa a la JVM, que procede a suspender el o programa con el mensaje de error correspondiente, y una historia de la cadena de llamadas hasta el punto donde la excepcin fue lanzada. o Si en el mtodo en el que se lanza una excepcin que no sea de tiempo de e o ejecucin no hay un manejador para la excepcin, el mtodo deber incluir el o o e a enunciado throws xexcepciny en su encabezado. o

9.2 La clase Exception


La clase Exception es una clase muy sencilla que tiene, realmente, muy pocos mtodos. De hecho, unicamente tiene dos constructores que se pueden usar e en aquellas clases que hereden a Exception. La clase Throwable, superclase de Exception, es la que cuenta con algunos mtodos ms que se pueden invocar desde e a cualquier excepcin, ya sea sta de Java o del programador. Veamos primero los o e dos constructores de Exception. public Exception() Es el constructor por omisin. La unica informacin que proo o porciona es el nombre de la excepcin. o public Exception(String msg) Construye el objeto pasndole una cadena, que proa porciona informacin adicional a la del nombre de la clase. o
3

en adelante JVM.

9.3 Cmo detectar y cachar una excepcin o o

295

La clase Throwable nos va a permitir un manejo ms preciso de las excepciones. a Tiene varios tipos de mtodos. e

Constructores:
public Throwable() Es el constructor por omisin. o public Throwable(String msg) Da la oportunidad de

construir el objeto con infor-

macin que se transmite en la cadena. o

Mtodos del stack de ejecucin: e o


public native Throwable llInStackTrace()

Coloca en el objeto una copia de la cadena dinmica del stack de ejecucin en ese momento. a o public void printStackTrace() Escribe en la pantalla el contenido del stack de ejecucin, respecto a la cadena de llamadas que llevaron al mtodo que est aboro e a tando. public void printStackTrace(PrintStream s) Lo mismo que el anterior, pero da la opcin de escribir a un archivo en disco. o

Mtodos de acceso a campos: e


public String getMessage()

Devuelve la cadena con que la excepcin fue creada. SI o fue creada con el constructor por omisin devuelve null. o

Mtodo descriptivo: e
public String toString()

Regresa en una cadena el nombre de la clase a la que pertenece y la cadena con la que fue creada, en su caso.

Como la clase Exception extiende a la clase Throwable, cualquier excepcin o que nosotros declaremos que extienda a Exception contar con los mtodos de a e Throwable. Las excepciones de tiempo de ejecucin siempre van a imprimir la cadena o producida por toString() y el stack de ejecucin producido por printStackTrace(). o De esa manera tenemos informacin muy puntual de cul fue el tipo de error y o a dnde exactamente se present. o o

296

Manejo de errores en ejecucin o

9.3 Cmo detectar y cachar una excepcin o o


Como ya mencionamos, una aplicacin comn y corriente puede lanzar diso u tintas excepciones de tiempo de ejecucin. Estas excepciones no tienen que ser o vigiladas y detectadas, sino que pasan a la JVM, quien suspende la ejecucin del o programa. Sin embargo, tambin pueden presentarse excepciones que no son de tiempo de e ejecucin y que s deben ser vigiladas y manejadas de alguna manera. Entre ellas o podemos mencionar las que son lanzadas en relacin con procesos de entrada y sao lida (IOException); cuando durante la ejecucin de una aplicacin no se encuentra o o una clase que se debe cargar (ClassNotFoundException); cuando se pretende hacer una copia de un objeto que no es copiable (CloneNotSupportedException); cuando se da alguna interrupcin en un proceso que se est ejecutando (InterruptedException); o a y algunas ms. Adems, el usuario puede declarar sus propias clases de excepcioa a nes, que pueden extender a una clase particular de excepciones o la clase genrica e Exception. La deteccin y manejo de una excepcin se lleva a cabo en un bloque que toma o o la forma que se muestra en la gura 9.4.

Figura 9.4

Deteccin y manejo de excepciones o


Sintaxis: try { xenunciados donde puede ser lanzada la excepciny o } }

xEnunciados que reaccionan frente a la excepciny o

Semantica: Se pueden agrupar tantos enunciados como se desee en la clusula try, por a lo que al nal de ella se puede estar reaccionando a distintas excepciones. Se elige uno y solo un manejador de excepciones, utilizando la primera excepcin que calique con ser del tipo especicado en las clusulas catch. o a La parte que aparece a continuacin de cada catch corresponde al manejador o

catch ( xtipo de excepciny o

xidentify )

9.3 Cmo detectar y cachar una excepcin o o

297

de cada clase de excepcin que se pudiera presentar en el cuerpo del try. El tipo o de excepcin puede ser Exception, en cuyo caso cualquier tipo de excepcin va o o a ser cachada y manejada dentro de ese bloque. En general, una excepcin que o extiende a otra puede ser cachada por cualquier manejador para cualquiera de sus superclases. Una vez que se lista el manejador para alguna superclase, no tiene sentido listar manejadores para las subclases. La manera como se ejecuta un bloque try en el que se pudiera lanzar una excepcin es la siguiente: o La JVM entra a ejecutar el bloque correspondiente. Como cualquier bloque en Java, todas las declaraciones que se hagan dentro del bloque son visibles unicamente dentro del bloque. Se ejecuta el bloque enunciado por enunciado. En el momento en que se presenta una excepcin, la ejecucin del bloque se o o interrumpe y la ejecucin prosigue buscando a un manejador de la excepcin. o o La ejecucin verica los manejadores, uno por uno y en orden, hasta que o encuentre el primero que pueda manejar la excepcin. Si ninguno de los o manejadores puede manejar la excepcin que se present, sale de la manera o o que indicamos hasta que encuentra un manejador, o bien la JVM aborta el programa. Si encuentra un manejador adecuado, se ejecuta el bloque correspondiente al manejador. Si no se vuelve a lanzar otra excepcin, la ejecucin contina en el enunciado o o u que sigue al ultimo manejador. El programa que se encuentra en el listado 9.4 cacha las excepciones posibles en el bloque del try mediante una clusula catch para la superclase Exception. Una a vez dentro del manejador, averigua cul fue realmente la excepcin que se dispar, a o o la reporta y sigue adelante con la ejecucin del programa. o

Cdigo 9.4 Manejo de una excepcin a travs de la superclase o o e


1: import j a v a . i o . ; 2: 3: c l a s s CatchExc { 4: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) 5: int k = 0; 6: int c ; 7: int i = 3; 8: i n t [ ] e n t e r o s = new i n t [ 3 ] ;

(CatchExc)1/2

298

Manejo de errores en ejecucin o

Cdigo 9.4 Manejo de una excepcin a travs de la superclase o o e


9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: } try

(CatchExc)2/2

{ System . o u t . p r i n t l n ( "Dame un entero para trabajar ." ) ; w h i l e ( ( c = System . i n . r e a d ( ) ) >= 0 && c <= 9 ) k = k 10 + ( c ( ( i n t ) 0 ) ) ; System . o u t . p r i n t l n ( "Le el valor " + k ) ; c = 1000 / k ; System . o u t . p r i n t l n ( "El valor final de c es " + c ) ; enteros [ i ] = c ;

} catch ( E x c e p t i o n e ) { System . o u t . p r i n t l n ( "La excepci n es de la clase :\n\t" + o e . getClass ()); } System . o u t . p r i n t l n ( "A punto de salir normalmente " + " del m todo main" ) ; e }

Este programa cacha cualquier excepcin que se presente en el bloque try y o la maneja, permitiendo que el programa termine normalmente. Dos ejecuciones consecutivas del mismo se pueden ver en la gura 9.5.

Figura 9.5

Excepciones de tiempo de ejecucin cachadas con una superclase o


elisa@lambda ...ICC1/progs/excepciones % java CatchExc Dame un entero para trabajar. 0 Lei el valor 0 La excepcin es de la clase :class java.lang.ArithmeticException o A punto de salir normalmente del mtodo main e elisa@lambda ...ICC1/progs/excepciones % java CatchExc Dame un entero para trabajar. 17 Le el valor 17 El valor final de c es 58 La excepcin es de la clase :class java.lang.ArrayIndex o OutOfBoundsException A punto de salir normalmente del mtodo main e

9.3 Cmo detectar y cachar una excepcin o o

299

Si queremos que el programa se interrumpa an despus de lanzada una exu e cepcin, podemos relanzarla en el manejador. En este caso, el mtodo en el que o e esto se hace debe avisar que se va a lanzar una excepcin, ya que habr una salida o a brusca del mismo. En el listado 9.5 modicamos la clase CatchExc CatchExc1 para que relance la excepcin y termine la ejecucin. En la gura 9.6 en la o o siguiente pgina vemos la ejecucin de esta nueva clase. a o

Cdigo 9.5 La excepcin es cachada y relanzada o o


1: import j a v a . i o . ; 2: 3: c l a s s CatchExc1 { 4: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) 5: throws E x c e p t i o n { 6: int k = 0; 7: int c ; 8: int i = 3; 9: i n t [ ] e n t e r o s = new i n t [ 3 ] ; 10: 11: try { 12: System . o u t . p r i n t l n ( "Dame un entero para trabajar ." ) ; 13: w h i l e ( ( c = System . i n . r e a d ( ) ) >= 0 && c <= 9 ) 14: k = k 10 + ( c ( ( i n t ) 0 ) ) ; 15: System . o u t . p r i n t l n ( "Le el valor "+ k ) ; 16: c = 1000 / k ; 17: System . o u t . p r i n t l n ( "El valor final de c es "+ c ) ; 18: enteros [ i ] = c ; 19: } 20: catch ( E x c e p t i o n e ) { 21: C l a s s en = e . g e t C l a s s ( ) ; 22: System . o u t . p r i n t l n ( "La excepci n es de la clase :" + en ) ; o 23: throw ( ( E x c e p t i o n ) en . n e w I n s t a n c e ( ) ) ; 24: } 25: System . o u t . p r i n t l n ( "A punto de salir normalmente " + 26: "del m todo main" ) ; e 27: } 28: }

Como la clase a la que pertenece la excepcin se obtiene con el mtodo geto e Class() de la clase Class, esto se hace durante ejecucin. Si bien se maneja la o excepcin mediante la clusula catch de la l o a nea 20:, se vuelve a relanzar, por lo que la l nea 25: del programa, que se encuentra a continuacin del bloque de la o excepcin, ya no se ejecuta. o

300

Manejo de errores en ejecucin o

Figura 9.6

Ejecucin con relanzamiento de la excepcin o o


elisa@lambda ...ICC1/progs/excepciones % java CatchExc1 Dame un entero para trabajar. 0 Le el valor 0 La excepcin es de la clase :class java.lang.Arithmetic o Exception Exception in thread "main" java.lang.ArithmeticException at java.lang.Class.newInstance0(Native Method) at java.lang.Class.newInstance(Class.java:237) at CatchExc1.main(CatchExc1.java:27) Un mtodo, ya sea de las bibliotecas de clases de Java o del programador, e puede lanzar una excepcin, y sta puede ser, nuevamente, una excepcin creada o e o por el programador o de las clases de Java. Por ejemplo, si deseamos que no haya divisiones por 0 an entre reales, podr u amos tener las clases que se muestran en los listados 9.6 y 9.7.

Cdigo 9.6 Creacin de excepciones propias o o


1: // A r c h i v o : D i v P o r C e r o E x c e p t i o n . j a v a 2: p u b l i c c l a s s D i v P o r C e r o E x c e p t i o n extends A r i t h m e t i c E x c e p t i o n 3: public DivPorCeroException () { 4: super ( ) ; 5: } 6: p u b l i c D i v P o r C e r o E x c e p t i o n ( S t r i n g msg ) { 7: super ( msg ) ; 8: } 9: } {

Cdigo 9.7 Deteccin de excepciones propias o o


// A r c h i v o : D i v P o r C e ro U s o . j a v a p u b l i c c l a s s D i v P o rC e r o Us o { public static float sqrt ( float x ) throws D i v P o r C e r o E x c e p t i o n { i f ( x < 0) throw new D i v P o r C e r o E x c e p t i o n ( "Se pide ra z de n mero negativo !" ) ; u else r e t u r n ( ( f l o a t ) Math . s q r t ( x ) ) ; }

(DivPorCeroUso)1/2

9.3 Cmo detectar y cachar una excepcin o o

301 (DivPorCeroUso)2/2

Cdigo 9.7 Deteccin de excepciones propias o o

p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { // Ac l e e ra m o s un v a l o r p a r a r a f l o a t r = ( ( f l o a t ) Math . random ( ) 1 0 0 0 0 ) ; f l o a t o t r o = ( f l o a t ) ( Math . random ( ) + . 5 ) ; System . o u t . p r i n t l n ( "Otro: "+ o t r o ) ; int signo = ( otro > 1 ? 0 : 1); i f ( s i g n o == 0 ) // Para f o r z a r l a e x c e p c i n o r = r ; // Tener r n e g a t i v o // Ac t e r m i n a m o s de o b t e n e r un v a l o r p a r a r a System . o u t . p r i n t l n ( "La ra z cuadrada de "+ r + " es " + sqrt ( r )); } }

Como la excepcin que construimos, DivPorCeroException, extiende a Arithmeo ticException y esta ultima no debe ser vigilada, la invocacin a sqrt no tiene que o hacerse dentro de un bloque try. Asimismo, el mtodo sqrt no tiene por qu avisar e e que va a lanzar una excepcin. o Sin embargo, si la excepcin hereda directamente a Exception o a cualquiera o que no sea RuntimeException, el mtodo no puede dejar de avisar que va a e lanzar una excepcin: al compilar a la clase DivPorCeroUso, asumiendo que Divo PorCeroException extiende a Exception, da un error que dice javac DivPorCeroUso.java DivPorCeroUso.java:7: unreported exception DivPorCeroException; must be caught or declared to be thrown throw new DivPorCeroException("Se pide raz de nmero u negativo!"); ^ 1 error Compilation exited abnormally with code 1 at Thu Oct 25 17:52:44 javac DivPorCeroUso.java Una vez corregido esto, la llamada al mtodo sqrt tiene que aparecer dentro e de un bloque try que se encargue de detectar la excepcin que lanza el mtodo. o e Estos cambios se pueden ver en los listados 9.8 en la siguiente pgina y 9.9 en la a siguiente pgina. a

302

Manejo de errores en ejecucin o

Cdigo 9.8 Declaracin de excepcin propia o o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: // A r c h i v o : D i v P o r C e r o E x c e p t i o n . j a v a } p u b l i c c l a s s D i v P o r C e r o E x c e p t i o n extends E x c e p t i o n public DivPorCeroException () { super ( ) ; } p u b l i c D i v P o r C e r o E x c e p t i o n ( S t r i n g msg ) { super ( msg ) ; } } // A r c h i v o : D i v P o r C e r o U s o . j a v a {

Cdigo 9.9 Clase que usa la excepcin creada o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: p u b l i c c l a s s D i v P o rC e r o Us o { public static float sqrt ( float x ) throws D i v P o r C e r o E x c e p t i o n i f ( x < 0) throw new D i v P o r C e r o E x c e p t i o n ( "Se pide ra z de n mero negativo !" ) ; u else r e t u r n ( ( f l o a t ) Math . s q r t ( x ) ) ; } p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { // Ac l e e ra m o s un v a l o r p a r a r a f l o a t r = ( ( f l o a t ) Math . random ( ) 1 0 0 0 0 ) ; f l o a t o t r o = ( f l o a t ) ( Math . random ( ) + . 5 ) ; System . o u t . p r i n t l n ( "Otro: "+ o t r o ) ; int signo = ( otro > 1 ? 0 : 1); i f ( s i g n o == 0 ) // Para f o r z a r l a e x c e p c i n o r = r ; // Trabajamos con r n e g a t i v o // Ac t e r m i n a m o s de o b t e n e r un v a l o r p a r a r a try { System . o u t . p r i n t l n ( "La ra z cuadrada de "+ r + " es " + s q r t ( r ) ) ; } catch ( D i v P o r C e r o E x c e p t i o n e ) { System . o u t . p r i n t l n ( e . g e t M e s s a g e ( ) ) ; System . e x i t ( 1); } } } {

Como la excepcin se maneja totalmente dentro del mtodo en cuestin, que o e o

9.3 Cmo detectar y cachar una excepcin o o

303

en este caso es main, la excepcin no se propaga hacia afuera de main, por lo que o el mtodo no tiene que avisar que pudiera lanzar una excepcin. e o Como ya mencionamos antes, las excepciones se pueden lanzar en cualquier momento: sin simplemente un enunciado ms. Por supuesto que un uso racional a de ellas nos indica que las deberemos asociar a situaciones no comunes o cr ticas, pero esto ultimo tiene que ver con la semntica de las excepciones, no con la a sintaxis. Tal vez el ejemplo del listado 9.9 en la pgina opuesta no muestre lo util que a pueden ser las excepciones, porque redenen de alguna manera una excepcin o que la JVM lanzar de todos modos. Pero supongamos que estamos tratando de a armar una agenda telefnica, donde cada individuo puede aparecer unicamente o una vez, aunque tenga ms de un telfono. Nuestros mtodos de entrada, al tratar a e e de meter un nombre, detecta que ese nombre con la direccin ya est registrado. o a En trminos generales, esto no constituye un error para la JVM, pero si para el e contexto de nuestra aplicacin. Una manera elegante de manejarlo es a travs de o e excepciones, como se muestra en los listados 9.10 a 9.12 en la siguiente pgina. a

Cdigo 9.10 Excepciones del programador (I) o


1: 2: 3: 4: 5: 6: 7: 8: 9: // A r c h i v o : R e g D u p l i c a d o E x c e p t i o n . j a v a p u b l i c c l a s s R e g D u p l i c a d o E x c e p t i o n extends E x c e p t i o n public RegDuplicadoException () { super ( ) ; } p u b l i c R e g D u p l i c a d o E x c e p t i o n ( S t r i n g msg ) { super ( msg ) ; } } {

Cdigo 9.11 Uso de excepciones del programador (I) o


1: 2: 3: 4: 5: 6: 7: 8: 9: // A r c h i v o : R e g N o E n c o n t r a d o E x c e p t i o n . j a v a p u b l i c c l a s s R e g N o E n c o n t r a d o E x c e p t i o n extends E x c e p t i o n public RegNoEncontradoException () { super ( ) ; } p u b l i c R e g N o E n c o n t r a d o E x c e p t i o n ( S t r i n g msg ) { super ( msg ) ; } } {

304

Manejo de errores en ejecucin o

Cdigo 9.12 Excepciones del programador y su uso (II) o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41:

(BaseDeDatos)1/2

// A r c h i v o : BaseDeDatos . j a v a p u b l i c c l a s s BaseDeDatos { i n t numRegs ; ... public void agrega ( R e g i s t r o reg ) throws R e g D u p l i c a d o E x c e p t i o n { ... i f ( actual . equals ( reg )) throw new R e g D u p l i c a d o E x c e p t i o n ( r e g . nombre ) ; ... } ... public void e l i m i n a ( R e g i s t r o reg ) throws R e g N o E n c o n t r a d o E x c e p t i o n { ... i f ( a c t u a l == n u l l ) throw new R e g N o E n c o n t r a d o E x c e p t i o n ( r e g . nombre ) ; ... } ... p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { ... w h i l e ( o p c i o n != 0 ) { try { switch ( opcion ) { case 1 : a g r e g a ( r e g ) ; reportaAgregado ( ) ; break ; case 2 : e l i m i n a ( r e g ) ; reportaEliminado (); break ; ... ... } // s w i t c h } // t r y catch ( R e g D u p l i c a d o E x c e p t i o n e ) { // p r o d u c e un manejo ad e c ua d o de l a r e p e t i c i n o System . o u t . p r i n t l n ( "El registro de: " + e . getMessage ( ) + "ya existe , por lo que no se agreg " ) ; o } // R e g D u p l i c a d o E x c e p t i o n

9.4 Las clases que extienden a Exception

305 (BaseDeDatos)2/2

Cdigo 9.12 Excepciones del programador y su uso (II) o


42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52:

catch ( R e g N o E n c o n t r a d o E x c e p t i o n e ) { // Pro duc e un manejo ade c ua do a l no e n c o n t r a r a l // nombre System . o u t . p r i n t l n ( "El registro de: " + e . getMessage ( ) + "no se encontr , por lo que no se elimin " ) ; o o } // R e g N o E n c o n t r a d o E x c e p t i o n } // w h i l e ... } // main } // c l a s s

El listado 9.12 en la pgina opuesta hace uso del hecho de que cuando en un a bloque se presenta una excepcin, la ejecucin salta a buscar el manejador de la o o excepcin y deja de ejecutar todo lo que est entre el punto donde se lanz la o e o excepcin y el manejador seleccionado. Como tanto agrega como elimina lanzan o excepciones, su invocacin tiene que estar dentro de un bloque try que va de la o l nea 24: a la l nea 35:. Si es que estos mtodos lanzan la excepcin, ya sea en la e o l nea 26: o 29:, ya no se ejecuta la l nea 27: en el primer caso y la l nea 30: en el segundo. Por lo tanto se est dando un control adecuado del ujo del programa, a utilizando para ello excepciones. Otra caracter stica que tiene este segmento de aplicacin es que como el bloque o try est dentro de una iteracin, y si es que se hubiere lanzado una excepcin en a o o alguno de los mtodos invocados, una vez que se lleg al nal del bloque try y e o habindose o no ejecutado alguno de los manejadores de excepciones asociados al e bloque try, la ejecucin regresa a vericar la condicin del ciclo, logrando de hecho o o que el programa no termine por causa de las excepciones. Esta forma de hacer las cosas es muy comn. Supongamos que le pedimos al usuario que teclee un nmero u u entero y se equivoca. Lo ms sensato es volverle a pedir el dato al usuario para a trabajar con datos adecuados, en lugar de abortar el programa.

9.4 Las clases que extienden a Exception


Hasta ahora, cuando hemos declarado clases que extienden a Exception no hemos agregado ninguna funcionalidad a las mismas. No solo eso, sino que uni

306

Manejo de errores en ejecucin o

camente podemos usar sus constructores por omisin, los que no tienen ningn o u parmetro. a El constructor por omisin siempre nos va a informar del tipo de excepcin que o o fue lanzado (la clase a la que pertenece), por lo que el constructor de Exception que tiene como parmetro una cadena no siempre resulta muy util. Sin embargo, a podemos denir una clase tan compleja como queramos, con los parmetros que a queramos en los constructores. Unicamente hay que recordar que si denimos alguno de los constructores con parmetros, automticamente perdemos acceso a a al constructor por omisin. Claro que siempre podemos invocar a super() en los o constructores denidos en las subclases. Podemos, en las clases de excepciones creadas por el usuario, tener ms mtoa e dos o informacin que la que nos provee la clase Exception o su superclase Tho rowable. Por ejemplo, la clase RegNoEncontradoException que diseamos para la n base de datos pudiera proporcionar ms informacin al usuario que simplemente el a o mensaje de que no encontr al registro solicitado; podr proporcionar los registros o a inmediato anterior e inmediato posterior al usuario. En ese caso, deber poder a armar estos dos registros. Para ello, podr amos agregar a la clase dos campos, uno para cada registro, y dos mtodos, el que localiza al elemento inmediato anterior e en la lista y el que localiza al inmediato posterior. En los listados 9.13 y 9.14 en la pgina opuesta podemos ver un bosquejo de cmo se lograr esto. a o a

Cdigo 9.13 Denicin de Excepciones propias o o

(RegNoEncontradoException)1/2

1: p u b l i c c l a s s R e g N o E n c o n t r a d o E x c e p t i o n extends E x c e p t i o n { 2: p r i v a t e R e g i s t r o regAnt , r e g P o s t ; 3: public RegNoEncontradoException () { 4: super ( ) ; 5: } 6: 7: p u b l i c R e g N o E n c o n t r a d o E x c e p t i o n ( S t r i n g msg ) { 8: super ( msg ) ; 9: } 10: 11: public RegNoEncontradoException ( R e g i s t r o a n t e r i o r , 12: Registro actual ) { 13: regAnt = b u s c a A n t e r i o r ( padre ) ; 14: regPost = buscaPosterior ( actual ) ; 15: } 16: 17: private Registro buscaAnterior ( Registro a n t e r i o r ) { 18: ... 19: }

9.4 Las clases que extienden a Exception

307 (RegNoEncontradoException)2/2
{

Cdigo 9.13 Denicin de Excepciones propias o o


20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: } private Registro buscaPosterior ( Actual ) ... } p u b l i c R e g i s t r o daRegAnt ( ) return regAnt ; } p u b l i c R e g i s t r o daRegPost ( ) return regPost ; } {

Cdigo 9.14 Denicin de excepciones propias (ejemplo) o o

(Ejemplo)

1: p u b l i c c l a s s E j e m p l o { 2: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 3: ... 4: try { 5: i f ( a c t u a l . s i g == n u l l ) { 6: throw new 7: RegNoEncontradoException ( a c t u a l . g e t A n t e r i o r ( ) , 8: actual ); 9: } // f i n de i f 10: ... 11: } // f i n de t r y 12: catch ( R e g N o E n c o n t r a d o E x c e p t i o n e ) { 13: System . o u t . p r i n t l n ( "El registro debi encontrarse " o 14: +" entre \n" ) ; 15: System . o u t . p r i n t l n ( "***" + e . daRegAnt ( ) . daNombre ( ) 16: + "***\n" + " y\n" ) ; 17: System . o u t . p r i n t l n ( "***" 18: + e . daRegPost ( ) . daNombre ( ) 19: + "***" ) ; 20: } // f i n de c a t c h 21: ... 22: } // main 23: } // c l a s s

En las l neas 6: a 8: tenemos un constructor adicional declarado para nuestra clase que extiende a Exception. Como se puede ver en las l neas 13: a 17:, el manejador de la excepcin hace uso de los campos y mtodos declarados en la o e

308

Manejo de errores en ejecucin o

excepcin, y que son llenados por el constructor, para proveer ms informacin al o a o usuario de la situacin presente en el momento de la excepcin. Hacindolo de esta o o e manera, en el momento de lanzar la excepcin se puede invocar un constructor o que recoja toda la informacin posible del contexto en el que es lanzada, para o reportar despus en el manejador. e Unicamente hay que recordar que todas aquellas variables que sean declaradas en el bloque try no son accesibles desde fuera de este bloque, incluyendo a los manejadores de excepciones. Insistimos: si se desea pasar informacin desde el o punto donde se lanza la excepcin al punto donde se maneja, lo mejor es pasarla o en la excepcin misma. Esto ultimo se consigue redeniendo y extendiendo a la o clase Exception. Adems de la informacin que logremos guardar en la excepcin, tenemos a o o tambin los mtodos de Throwable, como el que muestra el estado de los registros e e de activacin en el stack, o el que llena este stack en el momento inmediato anterior o a lanzar la excepcin. Todos estos mtodos se pueden usar en las excepciones o e creadas por el programador. Veamos en los listados 9.15 y 9.16 otro ejemplo de declaracin y uso de exo cepciones creadas por el programador. En este ejemplo se agrega un constructor y un atributo que permiten a la aplicacin recoger informacin respecto al orden o o en que se lanzan las excepciones y el contexto en el que esto sucede.

Cdigo 9.15 Excepciones creadas por el programador o


1: // A d e c u a c i n de l a s e x c e p c i o n e s o 2: c l a s s M i E x c e p c i o n 2 extends E x c e p t i o n { 3: private int i ; 4: public MiExcepcion2 () { 5: super ( ) ; 6: } 7: p u b l i c M i E x c e p c i o n 2 ( S t r i n g msg ) { 8: super ( msg ) ; 9: } 10: p u b l i c M i E x c e p c i o n 2 ( S t r i n g msg , i n t x ) 11: super ( msg ) ; 12: i = x; 13: } 14: public int val () { 15: return i ; 16: } 17: }

(MiExcepcion2)

9.4 Las clases que extienden a Exception

309 (CaracteristicasExtra)

Cdigo 9.16 Uso de excepciones creadas por el programador o

18: p u b l i c c l a s s C a r a c t e r i s t i c a s E x t r a { 19: p u b l i c s t a t i c v o i d f ( ) throws M i E x c e p c i o n 2 { 20: System . o u t . p r i n t l n ( " Lanzando MiExcepcion2 desde f()" ) ; 21: throw new M i E x c e p c i o n 2 ( ) ; 22: } 23: p u b l i c s t a t i c v o i d g ( ) throws M i E x c e p c i o n 2 { 24: System . o u t . p r i n t l n ( " Lanzando MiExcepcion2 desde g()" ) ; 25: throw new M i E x c e p c i o n 2 ( "Se origin en g()" ) ; o 26: } 27: p u b l i c s t a t i c v o i d h ( ) throws M i E x c e p c i o n 2 { 28: System . o u t . p r i n t l n ( " Lanzando MiExcepcion2 desde h()" ) ; 29: throw new M i E x c e p c i o n 2 ( "Se origin en h()" , 4 7 ) ; o 30: } 31: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 32: try { 33: f (); 34: } 35: catch ( M i E x c e p c i o n 2 e ) { 36: e . p r i n t S t a c k T r a c e ( System . e r r ) ; 37: } 38: try { 39: g(); 40: } 41: catch ( M i E x c e p c i o n 2 e ) { 42: e . p r i n t S t a c k T r a c e ( System . e r r ) ; 43: } 44: try { 45: h(); 46: } 47: catch ( M i E x c e p c i o n 2 e ) { 48: e . p r i n t S t a c k T r a c e ( System . e r r ) ; 49: System . e r r . p r i n t l n ( "e.val () = " + e . v a l ( ) ) ; 50: } 51: } 52: }

En esta aplicacin se muestra el uso de distintos constructores, las invocaciones o a los mtodos de Throwable y la extensin de la informacin que provee la clase e o o agregando campos. El resultado de la ejecucin se puede ver en la gura 9.7 en la o siguiente pgina. a

310

Manejo de errores en ejecucin o

Figura 9.7

Ejecucin de CaracteristicasExtra o
elisa@lambda ...ICC1/progs/excepciones % java CaracteristicasExtra Lanzando MiExcepcion2 desde f() MiExcepcion2 at CaracteristicasExtra.f(CaracteristicasExtra.java:23) at CaracteristicasExtra.main(CaracteristicasExtra.java:38) Lanzando MiExcepcion2 desde g() MiExcepcion2: Se origin en g() o at CaracteristicasExtra.g(CaracteristicasExtra.java:28) at CaracteristicasExtra.main(CaracteristicasExtra.java:44) Lanzando MiExcepcion2 desde h() MiExcepcion2: Se origin en h() o at CaracteristicasExtra.h(CaracteristicasExtra.java:33) at CaracteristicasExtra.main(CaracteristicasExtra.java:50) e.val() = 47 Recalcando lo que ya vimos respecto a excepciones, notamos varias cosas en este listado: Los mtodos f(), g() y h() tienen que avisar que lanzan una excepcin, ya e o que MiExcepcion2 no hereda de RuntimeException y por lo tanto se debe vigilar cuando se ejecute cualquiera de estos tres mtodos. Vale la pena decir e que aunque el lanzamiento de la excepcin fuera condicional, de cualquier o manera el mtodo tendr que avisar que existe la posibilidad de que lance e a la excepcin. o Como los mtodos lanzan excepciones, cada uno de ellos tiene que ser invoe cado en un bloque try. Como el bloque try consiste unicamente de la invocacin al mtodo, una vez o e ejecutado el manejador de la excepcin que se encuentra a continuacin del o o respectivo catch, la ejecucin contina en la siguiente l o u nea de cdigo. Es o por ello que aunque se lancen las excepciones, la ejecucin contina una vez o u ejecutado el manejador. Si alguno de los mtodos lanzaran alguna otra excepcin, el compilador e o exigir que hubiera un manejador por cada tipo de excepcin. Se puede a o cachar excepciones usando superclases, pero cada clase de excepcin lanzada o por un mtodo tiene que tener su manejador propio o uno que se reera a e la superclase. Si un mtodo lanza una excepcin y no la cacha en el mismo mtodo, su e o e encabezado tiene que especicar que lanza aquellas excepciones que no sean

9.5 El enunciado nally

311

cachadas en el mismo mtodo. e

9.4.1.

Relanzamiento de excepciones
Muchas veces el manejador de una excepcin hace algo de administracin de o o la clase y despus de esto simplemente vuelve a lanzar la excepcin. Si se le va a e o pedir a la excepcin que reporte el punto donde estaba la ejecucin en el momento o o en que fue lanzada la excepcin usando printStackTrace la excepcin lanzada o o va a tener registro del punto donde fue creada, no del punto desde donde es nalmente lanzada. Para que la excepcin actualice su informacin respecto al o o stack de ejecucin se utiliza el mtodo llInStackTrace al momento de relanzar la o e excepcin; esto va a hacer que el stack reeje el ultimo punto donde la excepcin o o fue lanzada y no donde fue creada.

9.5 El enunciado nally


Cuando tenemos un programa en el que estamos vigilando el lanzamiento de excepciones, vamos a tener cdigo que, por encontrarse despus del punto donde o e se lanz la excepcin y dentro del bloque try, no va a ser ejecutado. Por ejemplo, o o si estoy tratando de asignarle un valor a una variable y la ejecucin no pasa por o ese enunciado porque antes se lanz una excepcin. o o A continuacin de los bloques correspondientes a cachar las excepciones los o bloques catch podemos escribir un bloque de enunciados que se van a ejecutar ya sea que se haya lanzado una excepcin o no en el bloque try. La clusula nally o a siempre se ejecuta, no importa que se haya lanzado o no una excepcin. Veamos o un ejemplo muy sencillo en el listado 9.18 en la siguiente pgina. a

Cdigo 9.17 Ejemplo con la clusula nally (Excepcin) o a o


1: c l a s s T r e s E x c e p t i o n extends E x c e p t i o n { 2: public TresException () { 3: super ( ) ; 4: } 5: p u b l i c T r e s E x c e p t i o n ( S t r i n g msg ) { 6: super ( msg ) ; 7: } 8: }

312

Manejo de errores en ejecucin o

Cdigo 9.18 Ejemplo con la clusula nally (uso) o a


9: p u b l i c c l a s s F i n a l l y T r a b a j a { 10: s t a t i c int cuenta = 0; 11: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 12: while ( true ) { 13: try { 14: // Post i n c r e m e n t o . Es c e r o l a p r i m e r a v e z 15: i f ( c u e n t a++ == 0 ) { 16: throw new T r e s E x c e p t i o n ( ) ; 17: } 18: System . o u t . p r i n t l n ( "No hubo excepci n " ) ; o 19: } 20: catch ( T r e s E x c e p t i o n e ) { 21: System . e r r . p r i n t l n ( " TresException " ) ; 22: } 23: finally { 24: System . e r r . p r i n t l n ( "En la cl usula finally " ) ; a 25: i f ( c u e n t a == 2 ) { 26: break ; // s a l d e l w h i l e 27: } 28: } 29: } 30: } 31: }

Como se puede ver la salida de la ejecucin de este algoritmo en la gura 9.8, o el mensaje mandado por el bloque nally se imprime siempre, sin importar si hubo o no excepcin. o

Figura 9.8

Ejecucin de FinallyTrabaja o
elisa@lambda ...ICC1/progs/excepciones % java FinallyTrabaja TresException En la clusula finally a No hubo excepcin o En la clusula finally a

Es interesante tambin notar cmo, aunque se lance una excepcin, como el e o o bloque try est dentro de una iteracin, al salir de ejecutar todo el bloque asociado a o a la excepcin, la ejecucin contina con el while. o o u

9.5 El enunciado nally

313

nally funciona como una tarea que sirve para dar una ultima pasada al cdigo, o de tal manera de garantizar que todo quede en un estado estable. No siempre es necesario, ya que Java cuenta con recoleccin automtica de basura y destructores o a de objetos tambin automticos. Sin embargo, se puede usar para agrupar tareas e a que se desean hacer, por ejemplo en un sistema guiado por excepciones, ya sea que se presente un tipo de excepcin o no. Veamos un ejemplo con unos interruptores o elctricos en el listado 9.19. e

Cdigo 9.19 Otro ejemplo con la clusula nally o a


1: c l a s s S w i t c h { 2: boolean s t a t e = f a l s e ; 3: boolean r e a d ( ) { 4: return s t a t e ; 5: } 6: v o i d on ( ) { 7: s t a t e = true ; 8: } 9: void o f f ( ) { 10: state = false ; 11: } 12: }

(Switch)

Cdigo 9.20 Otro ejemplo con la clusula nally o a


1: c l a s s O n O f f E x c e p t i o n 1 extends E x c e p t i o n 2: public OnOffException1 () { 3: super ( ) ; 4: } 5: p u b l i c O n O f f E x c e p t i o n 1 ( S t r i n g msg ) { 6: super ( msg ) ; 7: } 8: } {

(OnOException1)

Cdigo 9.21 Otro ejemplo con la clusula nally o a


1: c l a s s O n O f f E x c e p t i o n 2 extends E x c e p t i o n 2: public OnOffException2 () { 3: super ( ) ; 4: } 5: p u b l i c O n O f f E x c e p t i o n 2 ( S t r i n g msg ) { 6: super ( msg ) ; 7: } 8: } {

(OnOException2)

314

Manejo de errores en ejecucin o

Cdigo 9.22 Otro ejemplo con la clusula nally o a


1: c l a s s O n O ff S w i t c h { 2: s t a t i c S w i t c h sw = new S w i t c h ( ) ; 3: s t a t i c void f ( ) 4: throws O n O f f E x c e p t i o n 1 , O n O f f E x c e p t i o n 2 { 5: } 6: }

(OnOSwitch)

Cdigo 9.23 Otro ejemplo con la clusula nally o a


1: p u b l i c c l a s s C o n F i n a l l y { 2: s t a t i c S w i t c h sw = new S w i t c h ( ) ; 3: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 4: try { 5: sw . on ( ) ; 6: // C d i g o que puede l a n z a r e x c e p c i o n e s o 7: O n O ff S w i t c h . f ( ) ; 8: } 9: catch ( O n O f f E x c e p t i o n 1 e ) { 10: System . e r r . p r i n t l n ( " OnOffException1 " ) ; 11: } 12: catch ( O n O f f E x c e p t i o n 2 e ) { 13: System . e r r . p r i n t l n ( " OnOffException2 " ) ; 14: } 15: finally { 16: sw . o f f ( ) ; 17: } 18: } 19: }

(ConFinally)

En esta aplicacin deseamos que, ya sea que se haya podido o no prender el o interruptor, la aplicacin lo apague antes de salir. o Los bloques try se pueden anidar para colocar de mejor manera las clusulas a nally, obligando a ejecutar de adentro hacia afuera. En el listado 9.24 tenemos un ejemplo de anidamiento de bloques try.

Cdigo 9.24 Anidamiento de bloques try o


1: c l a s s C u a t r o E x c e p t i o n extends E x c e p t i o n { 2: public CuatroException () { 3: super ( ) ; 4: } 5: p u b l i c C u a t r o E x c e p t i o n ( S t r i n g msg ) { 6: super ( msg ) ; 7: } 8: }

(CuatroException)

9.6 Restricciones para las excepciones

315 (SiempreFinally)

Cdigo 9.25 Anidamiento de bloques try o

1: p u b l i c c l a s s S i e m p r e F i n a l l y { 2: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 3: System . o u t . p r i n t l n ( " Entrando al primer bloque try" ) ; 4: try { 5: System . o u t . p r i n t l n ( " Entrando al segundo bloque try" ) ; 6: try { 7: throw new C u a t r o E x c e p t i o n ( ) ; 8: } 9: finally { 10: System . o u t . p r i n t l n ( " Finally en el segundo " 11: + " bloque try" ) ; 12: } 13: } 14: catch ( C u a t r o E x c e p t i o n e ) { 15: System . e r r . p r i n t l n ( " Cachando CuatroException en " + 16: "el primer bloque try" ) ; 17: } 18: finally { 19: System . e r r . p r i n t l n ( " Finally en primer bloque try" ) ; 20: } 21: } 22: }

Como en la mayor de los casos, la clusula nally se ejecuta de adentro hacia a a afuera. No importa que el primer try no tenga manejador para la excepcin, porque o al lanzarse la excepcin y no encontrar un manejador en su entorno inmediato, o simplemente va a salir y utilizar el manejador del bloque try ms externo. El a resultado de la ejecucin se puede ver en la gura 9.9. o

Figura 9.9

Ejecucin de SiempreFinally o
elisa@lambda ...ICC1/progs/excepciones % java SiempreFinally Entrando al primer bloque try Entrando al segundo bloque try Finally en el segundo bloque try Cachando CuatroException en el primer bloque try Finally en primer bloque try

316

Manejo de errores en ejecucin o

9.6 Restricciones para las excepciones


Cuando se redene el mtodo de una clase, el mtodo redenido no puede e e lanzar ms excepciones (o distintas) que el mtodo original. Esto es para que si a e alguien usa herencia para manejar ciertos objetos, no resulte que el mtodo en la e superclase ya no funciona porque el mtodo en la subclase lanza ms excepciones e a que el original. Esto es, si un mtodo en la superclase no lanza excepciones, ese e mtodo redenido en las subclases tampoco puede lanzar excepciones. e Lo que si puede hacer un mtodo redenido es lanzar excepciones que resultan e de extender a las excepciones que lanza el mtodo de la superclase. En este caso e no hay problema. Revisemos, por ejemplo, la aplicacin del listado 9.26. o

Cdigo 9.26 Manejo de excepciones con herencia o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: c l a s s B a s e b a l l E x c e p t i o n extends E x c e p t i o n } c l a s s F o u l extends B a s e b a l l E x c e p t i o n { } c l a s s S t r i k e extends B a s e b a l l E x c e p t i o n { } {

1/3

abstract class Inning { I n n i n g ( ) throws B a s e b a l l E x c e p t i o n { } v o i d e v e n t ( ) throws B a s e b a l l E x c e p t i o n { } a b s t r a c t v o i d a t B a t ( ) throws S t r i k e , F o u l ; void walk ( ) { } } c l a s s S t o r m E x c e p t i o n extends E x c e p t i o n { } c l a s s RainedOut extends S t o r m E x c e p t i o n { } c l a s s PopFoul extends F o u l { } i n t e r f a c e Storm { v o i d e v e n t ( ) throws RainedOut ; v o i d r a i n H a r d ( ) throws RainedOut ; }

9.6 Restricciones para las excepciones

317 (StormyInning)2/3

Cdigo 9.26 Manejo de excepciones con herencia o

29: p u b l i c c l a s s S t o r m y I n n i n g extends I n n i n g implements Storm { 30: / OK a n a d i r n u e v a s e x c e p c i o n e s p a r a l o s c o n s t r u c t o r e s , p e r o s e 31: deben m a n e j a r l a s e x c e p c i o n e s de l o s c o n s t r u c t o r e s b a s e : 32: / 33: S t o r m y I n n i n g ( ) throws RainedOut , B a s e b a l l E x c e p t i o n { 34: } 35: 36: S t o r m y I n n i n g ( S t r i n g s ) throws Foul , B a s e b a l l E x c e p t i o n { 37: } 38: / Los m todos n o r m a l e s s e t i e n e n que a d a p t a r a l a c l a s e b a s e : e 39: ! v o i d w a l k ( ) t h r o w s PopFoul {} // C o m p i l e E r r o r 40: I n t e r f a c e CANNOT add e x c e p t i o n s t o e x i s t i n g methods from t h e 41: base c l a s s : 42: ! p u b l i c v o i d e v e n t ( ) t h r o w s RainedOut { } 43: S i e l m todo no e x i s t e en l a c l a s e base , e n t o n c e s l a e 44: excepcin se vale : o 45: / 46: p u b l i c v o i d r a i n H a r d ( ) throws RainedOut { 47: } 48: 49: / Puedes e l e g i r no l a n z a r n i n g u n a e x c e p c i n , a n cuando l a o u 50: v e r s i n b a s e s l o haga : o 51: / 52: public void event ( ) { 53: } 54: 55: / Los m todos que r e d e f i n e n a m todos b s i c o s pueden l a n z a r e e a 56: excepciones heredadas : 57: / 58: v o i d a t B a t ( ) throws PopFoul { 59: } 60: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 61: try { 62: S t o r m y I n n i n g s i = new S t o r m y I n n i n g ( ) ; 63: s i . atBat ( ) ; 64: } catch ( PopFoul e ) { 65: System . e r r . p r i n t l n ( " PopFoul " ) ; 66: } catch ( RainedOut e ) { 67: System . o u t . p r i n t l n ( " Rained out" ) ; 68: } catch ( B a s e b a l l E x c e p t i o n e ) { 69: System . o u t . p r i n t l n ( " Generic Error " ) ; 70: }

318

Manejo de errores en ejecucin o

Cdigo 9.26 Manejo de excepciones con herencia o


71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: }

(StormyInning)3/3

// En l a v e r s i n d e r i v a d a no s e l a n z a un S t r i k e o try { // Qu p a s a s i s e u p c a s t ? e I n n i n g i = new S t o r m y I n n i n g ( ) ; i . atBat ( ) ; // Debes c a c h a r l a s e x c e p c i o n e s d e l m todo de l a v e r s i n e o // en l a c l a s e b a s e : } catch ( S t r i k e e ) { System . o u t . p r i n t l n ( " Strike " ) ; } catch ( F o u l e ) { System . o u t . p r i n t l n ( "Foul" ) ; } catch ( RainedOut e ) { System . o u t . p r i n t l n ( " Rained Out" ) ; } catch ( B a s e b a l l E x c e p t i o n e ) { System . o u t . p r i n t l n ( " Excepci n Gen rica Badall " ) ; o e e } }

9.6.1.

Apareamiento de excepciones
En general, el manejador de una excepcin se va a ejecutar en cualquiera de o las situaciones siguientes: La clase a la que pertenece la excepcin aparece en una clusula catch que o a corresponde al bloque try en el que se lanz la excepcin. o o Alguna de las superclases de la excepcin lanzada aparece en una clusula o a catch que corresponde al bloque try en el que se lanz la excepcin. o o Cualquiera de estas dos situaciones que se presente, se ejecutar el manejador a que aparezca primero. Si en la lista de manejadores aparecen tanto la superclase como la clase, y la superclase aparece primero, el compilador dar un mensaje de a error de que el segundo manejador nunca puede ser alcanzado. En la aplicacin o StormyInning del listado 9.26 se pueden observar todas las combinaciones de este tipo. Los comentarios ilustran tambin algunos puntos que no son vlidos. e a

9.7 Recomendaciones generales

319

9.7 Recomendaciones generales


Las excepciones en general se usan en cualquiera de las siguientes circunstancias:
I. II. III.

Arreglar el problema y llamar otra vez al mtodo que caus la excepcin. e o o Parchar el proceso y continuar sin volver a intentar el mtodo. e Calcular algn resultado alternativo en lugar del que el mtodo se supone que u e deb haber calculado. a Hacer lo que se pueda en el contexto actual y relanzar la excepcin para que o sea manejada en un contexto superior. Hacer lo que se pueda en el contexto actual y lanzar una excepcin distinta o para que sea manejada en un contexto superior. Terminar el programa. Simplicar el algoritmo. Hacer una aplicacin (o biblioteca) ms segura (se reeja a corto plazo en la o a depuracin y a largo plazo en la robustez de la aplicacin). o o

IV.

V.

VI. VII. VIII.

Con esto damos por terminado este tema, aunque lo usaremos extensivamente en los cap tulos que siguen.

Entrada y salida

10

10.1 Conceptos generales


Uno de los problemas que hemos tenido hasta el momento es que las bases de datos que hemos estado construyendo no tienen persistencia, esto es, una vez que se descarga la aplicacin de la mquina virtual (que termina) la informacin o a o que generamos no vive ms all. No tenemos manera de almacenar lo que consa a truimos en una sesin para que, en la siguiente sesin, empecemos a partir de o o donde nos quedamos. Prcticamente en cualquier aplicacin que programemos y a o usemos vamos a requerir de mecanismos que proporcionen persistencia a nuestra informacin. o En los lenguajes de programacin, y en particular en Java, esto se logra meo 1 diante archivos , que son conjuntos de datos guardados en un medio de almacenamiento externo. Los archivos sirven de puente entre la aplicacin y el medio o exterior, ya sea para comunicarse con el usuario o para, como acabamos de mencionar, darle persistencia a nuestras aplicaciones. Hasta ahora hemos usado extensamente la clase Consola, que es una clase programada por nosotros. Tambin hemos usado en algunos ejemplos del cap e tulo anterior dos archivos (objetos) que estn dados en Java y que son System.out a y System.err. Ambos archivos son de salida; el primero es para salida normal en consola y el segundo para salida, tambin en consola, pero de errores. Por ejemplo, e cuando un programa que aborta reporta dnde se lanz la excepcin, el reporte o o o

322 lo hace a System.err.

Entrada y salida

La razn por la que usamos nuestra propia clase hasta el momento es que en o Java prcticamente toda la entrada y salida puede lanzar excepciones; eso implica a que cada vez que usemos un archivo para leer, escribir, crearlo, eliminarlo, y en general cualquier operacin que tenga que ver con archivos, esta operacin tiene o o que ser vigilada en un bloque try, con el manejo correspondiente de las excepciones que se pudieran lanzar. Lo que hace nuestro paquete de entrada y salida es absorber todas las excepciones lanzadas para que cuando usan los mtodos de e estas clases ya no haya que vigilar las excepciones. El disear los mtodos de entrada y salida para que lancen excepciones en n e caso de error es no slo conveniente, sino necesario, pues es en la interaccin con o o un usuario cuando la aplicacin puede verse en una situacin no prevista, como o o datos errneos, un archivo que no existe o falta de espacio en disco para crear un o archivo nuevo. Un concepto muy importante en la entrada y salida de Java es el de ujos de datos. Java maneja su entrada y salida como ujos de carcteres (ya sea de 8 o a 16 bits). En el caso de los ujos de entrada, stos proporcionan carcteres, uno e a detrs de otro en forma secuencial, para que el programa los vaya consumiendo a y procesando. Los ujos de salida funcionan de manera similar, excepto que es el programa el que proporciona los carcteres para que sean proporcionados al a mundo exterior, tambin de manera secuencial. e En las guras 10.1 y 10.2 en la pgina opuesta vemos los algoritmos generales a para lectura y escritura, no nada ms para Java, sino que para cualquier lenguaje a de programacin. o

Figura 10.1

Algoritmo para el uso de ujos de entrada


6 9 9 9 9 9Abrir el ujo 9 9 9 8 9 9 9 9 9 7

Leer informacin 9 o 8 Lectura de carcteres 9 (mientras haya) a Leer informacin o 9 9 9


7

Cerrar el ujo

10.2 Jerarqu de clases a

323

Figura 10.2

Algoritmo para el uso de ujos de salida


6 9Abrir el ujo 9 9 9 9 9 8 9 9 9 7 6 9 8

Escribir informacin o Escritura de Escribir informacin o carcteres 9 (mientras haya) a 9 9 7 9 Cerrar el ujo

Podemos ver un esquema de este funcionamiento en las guras 10.3 y 10.4.

Figura 10.3

Funcionamiento de ujo de entrada


Dispositivo D A T O S Leer Aplicacin o

Figura 10.4

Funcionamiento de ujo de salida


Aplicacin o Escribir D A T O S Dispositivo

Los ujos de entrada se manejan a travs de clases espec e cas para ellos. Al construir el objeto se abre el ujo; se lee de l o escribe en l utilizando los distintos e e mtodos que tenga la clase para ello; se cierra invocando al mtodo close del objeto e e correspondiente. En lo que sigue elaboraremos mtodos para hacer persistente nuestra base de e datos. Antes trataremos de tener una visin ms general de cmo maneja Java la o a o entrada y salida.

324

Entrada y salida

10.2 Jerarqu de clases a


La entrada y salida se maneja en Java a travs de una jerarqu que incluye e a clases e interfaces. Tenemos bsicamente dos maneras de hacer entrada y salida: a la primera es leyendo y escribiendo bytes, mientras que la segunda es leyendo y escribiendo carcteres Unicode. Dado que Java es, fundamentalmente, un lenguaje a cuya caracter stica principal es su portabilidad, se dise un juego de carcteres no a universales, de dos bytes cada uno, que cubre prcticamente todos los alfabea tos conocidos. Para asegurar la portabilidad de datos, y dado que Java maneja internamente Unicode, es que se disearon estas clases que manejan carcteres. n a Cada uno de los tipos de entrada y salida tiene una superclase abstracta para lectura y otra para escritura. De ella se derivan clases concretas que permiten manipular de alguna forma lo que se est leyendo o escribiendo. Iremos describiendo a su uso conforme las vayamos presentando. La entrada y salida se ve siempre como un ujo, ya sea de bytes o de carcteres. a Se va tomando unidad por unidad y se procesa. Cuando este ujo se termina decimos que se acab el archivo y tendremos un eof (n de archivo). Generalmente o procesaremos la informacin hasta encontrar un eof, en cuyo momento daremos o n al proceso de los datos. Es por esta caracter stica que Java llama a su entrada y salida streams. Hablaremos de un ujo de bytes o de un ujo de carcteres a (omitiendo Unicode).

10.3 Entrada y salida de bytes


Un byte es un entero que ocupa 8 bits, y en general se da como unidad de medida para otros tipos que ocupan ms espacio. Los enteros que podemos almaa cenar en un byte van del -128 al 127. Sin embargo, cuando pensamos en cdigo o ASCII, pensamos en carcteres cuyo valor est entre 0 y 255. Para que podamos a a manejar as los bytes, la lectura (y la escritura) se har siempre en enteros o en a carcteres Unicode, de tal manera que el mtodo utilice unicamente el byte ms a e a bajo (al que corresponden las posiciones ms de la derecha). a En la gura 10.5 en la pgina opuesta se encuentra la jerarqu de clases para a a InputStream, mientras que en la gura 10.6 en la pgina 328 est el esquema de la a a jerarqu de clases para OutputStream. a

10.3 Entrada y salida de bytes

325

Figura 10.5

Jerarqu de clases para InputStream. a


DataInput InputStream ObjectInput

ObjectInputStream SequenceInputStream ByteArrayInputStream FilterInputStream FileInputStream PipedInputStream StringBuerInputStream

LineNumberInputStream DataInputStream BueredInputStream PushBackInputStreeam CheckedInputSteam CipherInputStream DigestInputStream InaterInputStream ProgressMonitorInputStream

A continuacin damos una muy breve explicacin en orden alfabtico de cul es o o e a el uso de cada una de las subclases para entrada. Todas las subclases se encuentran en el paquete java.io, excepto cuando indiquemos expl citamente que no es as .

326

Entrada y salida

public class BueredInputStream extends FilterInputStream


Lee desde un InputStream guardando lo que va leyendo en un buer. Esto permite a la aplicacin marcar una posicin o regresar a bytes ya le o o dos.

public class ByteArrayInputStream extends InputStream


Contiene un buer interno que contiene bytes, que se leen del ujo, cuando es necesario.

public class CheckedInputStream extends FilterInputStream


(Paquete: java.util.zip) Es un InputStream que mantiene una suma de vericacin (checksum) de o los datos que ha le do.

public class CipherInputStream extends FilterInputStream


(Paquete: javax.crypto) Est compuesto de un InputStream y un Cipher que permite entregar cia frados los bytes que lee de la entrada.

public class DataInputStream extends FilterInputStream implements DataInput


Lee datos primitivos (enteros, reales, booleanos, etc.) de un InputStream subyacente de manera independiente de la mquina. a

public class FileInputStream extends InputStream


El ujo de entrada reside en un archivo en disco.

public class FilterInputStream


Simplemente recibe el ujo de un ujo subyacente y los pasa a la aplicacin. Redene los mtodos de entrada para poder transformarla. o e

public class DigestInputStream extends FilterInputStream


(paquete: java.security) Actualiza el mensaje digerido (MesasageDigest) usando para ello los bytes que pasan por el ujo.

public class InaterInputStream extends FilterInputStream


(Paquete: java.util.zip) Implementa un ltro para descomprimir datos comprimidos con deate y para otros ltros de descompresin. o

public abstract class InputStream implements Closeable


Es la superclase de todas las clases que manejan ujos de entrada de bytes.

10.3 Entrada y salida de bytes

327

public class LineNumberInputStream extends FilterInputStream


(sobreseido2 ) Este ujo lleva la cuenta del nmero de l u neas que ha le do. Una l nea es una sucesin de bytes que terminan con zr, zn o un retorno de carro o seguido de una alimentacin de l o nea. Al entregar las l neas le das convierte cualquiera de los terminadores a zn.

class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants


Escribe datos primitivos y grcas de objetos de Java en un ujo de bytes. a Unicamente objetos que implementen la interfaz Serializable pueden ser escritos en este ujo.

public class PipedInputStream extends InputStream


Se usa con hilos paralelos de ejecucin, donde uno de los hilos usa un Pipeo dInputStream para adquirir datos y el otro usa un PipedOutputStream para entregar datos. Ambos hilos pueden hacer un proceso de la informacin o del ujo correspondiente.

public class ProgressMonitorInputStream extends FilterInputStream


(Paquete: javax.swing) Vigila el progreso al leer de un InputStream, presentando, en su caso, ventanas de dilogo. a

public class PushBackInputStream extends FilterInputStream


Trabaja sobre un InputStream subyacente y permite desleer un byte. Este proceso es util cuando estamos buscando, por ejemplo, un byte que tiene dos funciones: delimitar lo que est antes y empezar lo nuevo. En a este caso es conveniente desleerlo para el segundo papel que juega.

public class SequenceInputStream extends InputStream


Es capaz de concatenar, para lectura, a varios ujos de entrada como si fueran uno solo.

public class StringBuerInputStream extends InputStream


Permite crear una aplicacin en la que el ujo proviene de una cadena de o carcteres, en lugar de venir de un dispositivo. a La jerarqu de clases para los ujos de salida de bytes se da en la gura 10.6. a
Corresponde a deprecated, que indica que no se recomienda su uso porque ya no va a ser actualizada y soportada
2

328

Entrada y salida

Con sus marcadas excepciones, por el uso que se le pueda dar, hay una correspondencia entre ambas jerarqu as.

Figura 10.6

Jerarqu de clases para OutputStream. a

DataOutput OutputStream ObjectOutputStream ByteArrayOutputStream PipedOutputStream FileOutputStream FilterOutputStream PrintStream BueredOutputStream DataOutputStream ObjectOutput

La unica clase que no tiene contra parte en la jerarqu de entrada de bytes es a PrintStream:

public class PrintStream extends FilterOutputStream implements Appendable


Agrega funcionalidad a otro OutputStream aportando la habilidad de imprimir de manera conveniente diversos valores de datos. Adicionalmente, sus mtodos nunca lanzan excepciones y puede construirse de tal manera e que evace automticamente. u a

10.4 Entrada y salida de carcteres a

329

10.4 Entrada y salida de carcteres a


Cuando hablamos de carcteres en el contexto de Java nos referimos a carctea a res Unicode, donde cada uno ocupa 2 bytes (16 bits). Tenemos una jerarqu a similar a la que maneja bytes para carcteres Unicode, las superclases Writer y a Reader, cuyas jerarqu se muestra en las guras 10.7 y 10.8 respectivamente. En as todos los casos las subclases sombreadas se reeren a clases que van a hacer un proceso intermedio de los datos entre el origen y el destino de los mismos. Un esquema de qu signica esto se encuentra en la gura 10.14. En sta el origen de e e los datos puede ser la aplicacin y el destino el dispositivo, en el caso de que se o est efectuando escritura; o bien el origen es el dispositivo que entrega los datos e en crudo y la aplicacin la que los recibe en el destino ya procesados. o

Figura 10.7

Jerarqu de clases para Writer. a


BueredWriter CharArrayWriter

FilterWriter Writer PrintWriter PipedWriter StringWriter OutputStreamWriter

FileWriter

330

Entrada y salida

Figura 10.8

Jerarqu de clases para Reader. a

StringReader CharArrayReader PipedReader Reader BueredReader LineNumberReader PushbackReader FileReader

FilterReader InputStreamReader

Figura 10.9

Entrada/Salida con proceso intermedio (ltros)

Filtro

Origen

Destino

Tambin estas jerarqu corren paralelas a las que trabajan con bytes, por lo e as que no daremos una nueva explicacin de cada una de ellas. Se aplica la misma o

10.4 Entrada y salida de carcteres a

331

explicacin, excepto que donde dice byte hay que sustituir por carcter. Unio a camente explicaremos aquellas clases que no tienen contra parte en bytes.

public class StringWriter extends Writer


Escribe su salida en un buer de tipo String, que puede ser utilizado a su vez para construir una cadena.

public class OutputStreamWriter extends Writer


Funciona como un puente entre ujos de carcteres y ujos de bytes, a codicados de acuerdo a un conjunto de carcteres espec a co. Vale la pena hacer la aclaracin que en este caso los ujos que leen de y escriben o a archivos en disco extienden a las clases InputStreamReader y OutputStreamWriter respectivamente, ya que la unidad de trabajo en los archivos es el byte (8 bits) y no el carcter (16 bits). Por lo dems funcionan igual que sus contra partes en los a a ujos de bytes. Es conveniente mencionar que las versiones actuales de Java indican que las clases que se deben usar son las que derivan de Reader y Writer y no las que son subclases de InputStream y OutputStream. Ambas jerarqu (las de bytes y las as de carcteres) denen prcticamente los mismos mtodos para bytes y carcteres, a a e a pero para fomentar la portabilidad de las aplicaciones se ha optado por soportar de mejor manera las clases relativas a carcteres. a Sin embargo, como ya mencionamos, la entrada y salida estndar de Java es a a travs de clases que pertenecen a la jerarqu de bytes (System.in, System.out y e a System.err). Lo primero que queremos poder hacer es leer desde el teclado y escribir a pantalla. Esto lo necesitamos para la clase que maneja el men y de esta manera u ir abriendo las cajas negras que nos proporcionaba la clase Consola para este n.

10.4.1.

Entrada y salida estndar a


La entrada y salida desde teclado y hacia consola se hace a travs de la clase e System. Esta clase ofrece, adems de los objetos para este tipo de entrada y salida, a much simos mtodos que van a ser utiles en aplicaciones en general. La clase e System tiene tres atributos que son:
public static f i n a l PrintStream e r r ; public s t a t i c f i n a l InputStream in ; public s t a t i c f i n a l PrintStream out ;

332

Entrada y salida

Por ser objetos estticos de la clase se pueden usar sin construirlos. Todo a programa en ejecucin cuenta con ellos, por lo que los puede usar, simplemente o rerindose a ellos a travs de la clase System. e e El primero de ellos es un archivo al que dirigiremos los mensajes que se reeran a errores, y que no queramos mezclar con la salida normal. El segundo objeto es para leer de teclado (con eco en la pantalla) y el tercero para escribir en la pantalla. Las dos clases mencionadas son clases concretas que aparecen en la jerarqu de a clases que mostramos en las guras 10.5 en la pgina 325 y 10.6 en la pgina 328. a a Si bien la clase PrintStream se va a comportar exactamente igual a Consola, en cuanto a que interpreta enteros, cadenas, otantes, etc. para mostrarlos con formato adecuado, esto no sucede con la clase InputStream que opera de manera muy primitiva, leyendo byte por byte, y dejndole al usuario la tarea de pegar los a bytes para interpretarlos. Ms adelante revisaremos con cuidado todos los mtoa e dos de esta clase. Por el momento unicamente revisaremos los mtodos que leen e byte por byte, y que son:

public class InputStream implements Closeable


Constructores:

public InputStream()
Constructor por omisin o Mtodos: e

public int read() throws IOException


Lee el siguiente byte del ujo de entrada. Devuelve un valor entre 0 y 255. Si se acaba el archivo (o desde el teclado se oprime Ctrl-D) regresa -1.

public int read(byte[] b) throws IOException


Lee un nmero de bytes al arreglo. Regresa el nmero de bytes le u u do. Bloquea la entrada hasta tener datos de entrada disponibles, se encuentre el n de archivo o se lance una excepcin. Se leen, a lo ms, el nmero de o a u bytes dado por el tamao de b. n

public int read(byte[] b, int o , int len ) throws IOException


Lee a lo ms len bytes de datos desde el ujo de entrada y los acomoda en el a arreglo de bytes b. Regresa el nmero de bytes le u dos. El primer byte le do se acomoda en b[o]. Lanza una excepcin IndexOutOfBoundsException si o o es negativo, len es negativo o o len  b.length.

10.5 El manejo del men de la aplicacin u o

333

Como podemos ver de los mtodos de la clase InputStream, son muy primitivos e y dif ciles de usar. Por ello, como primer paso en la inclusin de entrada y salida o completa en nuestra aplicacin, para entrada utilizaremos una subclase de Reader, o BueredReader, ms actual y mejor soportada. a

10.5 El manejo del men de la aplicacin u o


En el caso de los ujos System.out y System.err no tenemos que hacer absolutamente nada pues existen como atributos estticos de la clase System, por lo a que los podemos usar directamente. Conviene, sin embargo, listar los mtodos y e atributos de la clase PrintStream, que es una subclase de FilterOutputStream, que es, a su vez, una subclase de OutputStream.

10.5.1.

La clase OutputStream
Esta es una clase abstracta que deja sin implementar uno de sus mtodos. El e constructor y los mtodos se listan a continuacin: e o

public class OutputStream implements Closeable, Flushable


Constructores:

public OutputStream()
Constructor por omisin. o Mtodos: e

public abstract void write(int b) throws IOException


Toma el entero b y escribe unicamente los 8 bits ms bajos, descartando a los otros 24 bits. El programador de clases que hereden de sta tienen que e denir este mtodo. e

public void write (byte[] b)


Escribe el contenido de los b.length bytes del arreglo b al ujo de salida.

334

Entrada y salida

public void write (byte[] b, int o , int len ) throws IOException


Escribe en el ujo de salida los bytes desde b[o] hasta b[o+len-1]. Si hay un error en el ndice o si b es null, lanza la excepcin correspondiente o (como son ArithmeticException ambas no hay que vigilarlas). Si hay algn u error de I/O se lanza la excepcin correspondiente. o

public void ush () throws IOException


Evaca el ujo, obligando a que los bytes que estn todav en el buer u e a sean escritos al medio f sico.

public void close () throws IOException


Cierra el ujo y libera los recursos del sistema asociados al ujo. Una vez cerrado el ujo, cualquier otro intento de escribir en l va a causar una e excepcin. Lanza una excepcin (IOException) si se intenta reabrir. En o o realidad no hace nada, sino que se tiene que reprogramar para que haga lo que tiene que hacer. Esta es la superclase de la clase que estamos buscando. De manera intermedia hereda a la clase FilterOutputStream, que procesa el ujo antes de colocarlo en el dispositivo de salida. Adems de los mtodos heredados de OutputStream, agrega a e los siguientes mtodos, adems de implementar al mtodo que lee de un entero. e a e Tiene la siguiente denicin: o

public class FilterOutputStream extends OutputStream


Campo:

protected OutputStream out


El ujo de salida subyacente a ser ltrado. Constructor:

public FilterOutputStream(OutputStream out)


Crea un ltro para el ujo out. Mtodo: e

public void write (int b) throws IOException


Implementa el mtodo abstracto write(int) de su superclase. e

10.5 El manejo del men de la aplicacin u o

335

El resto de los mtodos que hereda de OutputStream simplemente los redene e a que invoquen al mtodo correspondiente de su superclase, por lo que no los e listamos nuevamente. Sin embargo, como mencionamos antes, tanto out como err se construyen como objetos de la clase PrintStream, que presenta varios mtodos, e adems de los que hereda de FilterOutputStream (hereda, entre otros, el campo que a corresponde al ujo de salida FilterOutputStream out). Listaremos slo algunos de o estos mtodos. La lista exhaustiva se puede ver en la documentacin de Java. e o

public class PrintStream extends FilterOutputStream implements Appendable


Constructores:

public PrintStream(OutputStream out)


Construye un PrintStream que no auto-evaca. u

public PrintStream(OutputStream out, boolean autoFlush)


Construye un nuevo PrintStream. Si autoFlush es verdadero el buer va a evacuar cuando se escriba un arreglo de bytes, se invoque un mtodo e println o se escriba un carcter de l a nea nueva (zn).

public PrintStream(String leName) throws FileNotFoundException


Busca escribir en un archivo en disco con nombre leName. Crea el ujo intermedio OutputStreamWriter necesario. Mtodos: e

public void close ()


Cierra el ujo, evacuando y cerrando el ujo de salida subyacente.

public boolean checkError()


Evaca el ujo y verica su estado de error. Este es verdadero si el ujo u de salida subyacente lanza una IOException distinta de InterruptedIOException, y cuando se invoca al mtodo setError. e

protected void setError()


Establece en verdadero el estado de error del ujo.

public void write (int b)


Sobreescribe el mtodo de OutputStream escribiendo el byte ms bajo al e a dispositivo.

336

Entrada y salida

public void write (byte[] buf, int o , int len )


Hace lo mismo que OutputStream.

public void print (boolean b)


Escribe un valor booleano. Escribe en bytes el valor dado por String.valueOf(boolean).

public void print (char c)


Escribe un carcter, que se traduce a uno o ms bytes, dependiendo de la a a plataforma.

public void print (int i ) public void print ( xtipoy xidentify ) public void print (char[] s)
Escribe un arreglo de carcteres, convirtindolos a bytes. a e Escribe un entero, el valor dado por String.valueOf(int). El xtipoy puede ser long, oat, double y se escribe lo producido por String.valueOf(xtipoy).

public void print ( String s)


Escribe una cadena, tomando carcter por carcter y convirtindolo a a a e byte.

public void print (Object obj)


Usa el mtodo String.valueOf(Object) para escribir, en bytes, lo solicitado. e

public void println () public void println ( xtipoy xidentify )


Imprime unicamente un carcter de n de l a nea

Admite los mismos tipos de argumentos que print; al terminar de escribir el argumento, escribe un carcter de n de l a nea.

public PrintStream printf ( String format, Object. . . args)


Un mtodo para escribir una lista de argumentos con un formato dado e por format.

public PrintStream format( String format, Object ... args)


Mtodo equivalente a printf de esta misma clase. e
Nota: esta clase presenta much simos mtodos ms que no veremos por el momento. e a Para conocerlos consultar la documentacin de las clases de Java. o

10.5 El manejo del men de la aplicacin u o

337

Teniendo ya estas clases, y utilizando la salida estndar System.out y Sysa tem.err, podemos proceder a reprogramar el men en cuanto a las partes que u corresponden a la salida. Como la salida a consola en general no lanza excepciones, lo unico que hay que hacer es sustituir las escrituras en Consola por escrituras a estos dos archivos estndar. Sustituiremos cada comunicacin normal con el a o usuario donde aparezca cons.imprimeln por System.out.println; mientras que donde sean mensajes de error para el usuario las sustituiremos por System.err.println. Las l neas que se van a cambiar aparecen a continuacin, primero la original y a o continuacin la que fue cambiada y una breve explicacin, en su caso, de por qu. o o e Antes: import i c c 1 . i n t e r f a z . C o n s o l a ; Despus: e import j a v a . i o . ; Dejamos de importar nuestra clase de entrada y salida e importamos el paquete io de Java. Si bien no lo necesitamos todav IOException est en este paquete a, a y pudiramos necesitarla. e Antes: p r i v a t e void r e p o r t a N o ( C o n s o l a cons , S t r i n g nombre ) c o n s . i m p r i m e l n ( "El estudiante : z n z t" + nombre + " z n No esta en el grupo" ) ; } Despus: e p r i v a t e void r e p o r t a N o ( P r i n t S t r e a m out , S t r i n g nombre ) { o u t . p r i n t l n ( "El estudiante : z n z t" + nombre + " z n no esta en el grupo" ) ; } Consola antes se usaba para entrada y salida, pero ahora tenemos que tener un ujo de entrada y dos de salida. Este mtodo unicamente maneja el de salida, e por lo cambiamos su parmetros a que sea un ujo de salida del mismo tipo a que son out y err. {

338

Entrada y salida

Antes: S t r i n g nombre = c o n s . l e e S t r i n g ( "Dame el nombre del " + " estudiante empezando por " + " apellido paterno:" ) ; Despus: e S t r i n g nombre ; System . o u t . p r i n t ( "Dame el nombre del estudiante ," + " empezando por apellido paterno:" ) ; // Ac va l a l e c t u r a a Ya no tenemos la posibilidad de escribir un mensaje y leer al nal de este. As que partimos en dos el proceso, escribiendo primero el mensaje, sin dar cambio de l nea, para que empiece a leer a continuacin del mensaje (todav o a falta la lectura de la cadena). Antes: c o n s . i m p r i m e l n ( menu ) ; S t r i n g s o p c i o n = c o n s . l e e S t r i n g ( "Elige una opci n --> " ) ; o Despus: e System . o u t . p r i n t l n ( menu ) ; System . o u t . p r i n t ( "Elige una opci n --> " ) ; o // Ac v i e n e l o de l a l e c t u r a de l a o p c i n a o Estamos dejando pendiente lo referente a la lectura. Antes: case 0 : // S a l i r c o n s . i m p r i m e l n ( "Espero haberte servido. z n" + "Hasta pronto ..." ) ; Despus: e case 0 : // S a l i r System . o u t . p r i n t l n ( "Espero haberte servido. z n" + "Hasta pronto ..." ) ;

10.5 El manejo del men de la aplicacin u o

339

Antes:

} e l s e r e p o r t a N o ( cons , nombre ) ; Despus: e

c o n s . i m p r i m e l n ( "El estudiante : z n z t" + nombre + " z n Ha sido eliminado " ) ;

} e l s e r e p o r t a N o ( System . e r r , nombre ) ;

System . o u t . p r i n t l n ( "El estudiante : z n z t" + nombre + " z n Ha sido eliminado " ) ;

Antes: s u b c a d = c o n s . l e e S t r i n g ( "Dame la subcadena a "+ "buscar: " ) ; do { s c u a l = c o n s . l e e S t r i n g ( "Ahora dime de cu l campo :" a + "1: Nombre , 2: Cuenta ," + " 3: Carrera , 4: Clave" ) ; c u a l = "01234" . i n d e x O f ( s c u a l ) ; i f ( c u a l < 1) c o n s . i m p r i m e l n ( "Opci n no v lida" ) ; o a } while ( c u a l < 1 ) ; Despus: e try { System . o u t . p r i n t ( "Dame la subcadena a " + "buscar: " ) ; // L e e r s u b c a d e n a do { System . o u t . p r i n t ( "Ahora dime de cu l campo :" a + "1: Nombre 2: Cuenta 3: Carrera 4: Clave" ) ; // L e e r o p c i n o c u a l = "01234" . i n d e x O f ( s c u a l ) ; i f ( c u a l < 1) System . o u t . p r i n t l n ( "Opci n no v lida" ) ; o a } while ( c u a l < 1 ) ;

340

Entrada y salida

donde = ( E s t u d i a n t e ) miCurso . b u s c a S u b c a d ( c u a l , s u b c a d ) ; i f ( donde != n u l l ) System . o u t . p r i n t l n ( donde . d a R e g i s t r o ( ) ) ; e l s e r e p o r t a N o ( System . e r r , s u b c a d ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "Error al dar los datos" + " para buscar" ) ; } // end o f t r y c a t c h Dejamos sin llenar las lecturas, aunque las colocamos en un bloque try. . . catch porque las lecturas pueden lanzar excepciones. Antes: case 4 : // L i s t a t o d o s miCurso . l i s t a T o d o s ( c o n s ) ; Despus: e case 4 : // L i s t a t o d o s miCurso . l i s t a T o d o s ( System . o u t ) ; Simplemente pasamos como parmetro el archivo de la consola. a Antes: d e f a u l t : // E r r o r , v u e l v e a p e d i r c o n s . i m p r i m e l n ( "No diste una opci n v lida .\n" + o a "Por favor vuelve a elegir." ) ; return 0; Despus: e d e f a u l t : // E r r o r , v u e l v e a p e d i r System . o u t . p r i n t l n ( "No diste una opci n v lida .\n" o a + "Por favor vuelve a elegir." ) ;

En cuanto a la clase ListaCurso, se tienen que hacer las siguientes modicaciones:

10.5 El manejo del men de la aplicacin u o

341

Antes: import i c c 1 . i n t e r f a z . C o n s o l a ; Despus: e import j a v a . i o . ; Antes: p u b l i c void l i s t a T o d o s ( C o n s o l a c o n s ) { ... cons . imprimeln ( a c t u a l . d a R e g i s t r o ( ) ) ; ... c o n s . i m p r i m e l n ( "No hay registros en la base de datos" ) ; Despus: e p u b l i c void l i s t a T o d o s ( P r i n t S t r e a m c o n s ) { ... cons . p r i n t l n ( a c t u a l . d a R e g i s t r o ( ) ) ; ... c o n s . p r i n t l n ( "No hay registros en la base de datos" ) ; Antes: p u b l i c void losQueCazanCon ( C o n s o l a cons , i n t c u a l , S t r i n g subcad ) { ... cons . imprimeln ( a c t u a l . d a R e g i s t r o ( ) ) ; ... c o n s . i m p r i m e l n ( "No se encontr ning n registro " o u + "que cazara" ) ; Despus: e p u b l i c void losQueCazanCon ( P r i n t S t r e a m cons , i n t c u a l , S t r i n g subcad ) { ... cons . p r i n t l n ( a c t u a l . d a R e g i s t r o ( ) ) ; ... c o n s . p r i n t l n ( "No se encontr ning n registro " o u + "que cazara" ) ;

342

Entrada y salida

Tambin en lo que toca a la interfaz implementada por ListaCurso, ParaListas, hay e que modicar los encabezados de estos dos mtodos: e Antes: p u b l i c void l i s t a T o d o s ( C o n s o l a c o n s ) ; p u b l i c void losQueCazanCon ( C o n s o l a cons , i n t c u a l , S t r i n g subcad ) ; Despus: e p u b l i c void l i s t a T o d o s ( P r i n t S t r e a m c o n s ) ; p u b l i c void losQueCazanCon ( P r i n t S t r e a m cons , i n t c u a l , S t r i n g subcad ) ;

Como se puede ver, y dado que escribir en consola (mediante PrintStream no lanza excepciones, el uso de esta clase en lugar de Consola para escribir no causa ningn problema. u

10.5.2.

Lectura desde teclado


Como ya mencionamos antes, si usamos directamente System.in tendremos que hacer un uso muy primitivo de la entrada, como convertir carcteres a cadenas, a a enteros, etc. La clase que nos proporciona la conversin desde el teclado es Dao taInputStream que es la que usamos en Consola excepto por la lectura de una cadena que se reporta como superada(deprecated. La clase DataInputStream, como se muestra en la gura 10.5 en la pgina 325, es una subclase de FilterIna putStream, que a su vez es una subclase de InputStream. En la documentacin o de la clase io.DataInputStream recomiendan, para leer cadenas, un ejemplar de la clase BueredReader, dado que, argumentan, a partir de Java 1.1 ya no pueden garantizar que la conversin de bytes (externos) a carcteres (internos) se haga o a de manera adecuada. Como en el manejo del men leemos unicamente cadenas, u utilizaremos un ejemplar de esta clase para ello. Como lo hicimos para nuestro archivo de salida, veamos cul es la l a nea hereditaria de esta clase, los mtodos e que hereda y los que redene.

10.5 El manejo del men de la aplicacin u o

343

public class Reader implements Readable, Closeable3


Campos:

protected Object lock


Campo que permite bloquear los accesos al archivo desde otra aplicacin o u otro hilo de ejecucin. o Constructores:

protected Reader() protected Reader(Object lock)


Ambos constructores entregan un objeto de la clase Reader; el segundo asigna un cerrojo externo. Mtodos: e

public int read() throws IOException


Lee un carcter; se bloquea hasta que se le proporcione uno, haya un error a de I/O o se alcance el nal del ujo. Regresa el carcter le (0,. . . ,65535) a do o -1, si el ujo se acab. o

public int read(char[] cbuf)) throws IOException


Lee carcteres y los acomoda a partir del lugar 0 en el arreglo de carcteres a a cbuf; se bloquea hasta que se le proporcione uno, haya un error de I/O o se alcance el nal del ujo. Regresa el nmero de carcteres le u a dos o -1 si el ujo se acab. o

public int read(char[] cbuf, int o , int len )) throws IOException


Lee a lo ms len carcteres y los acomoda a partir del lugar o en el a a arreglo de carcteres cbuf; se bloquea hasta que se le proporcione uno, a haya un error de I/O o se alcance el nal del ujo. Regresa el nmero de u carcteres le a dos o -1 si el ujo se acab. o

public int read(CharBuer target ) throws IOException


Regresa -1 si el ujo de los carcteres se acab o el nmero de carcteres a o u a agregados al buer.

public long skip(long n) throws IOException


Salta carcteres (le a dos); se bloquea hasta que se le proporcione uno, haya un error de I/O o se alcance el nal del ujo. Puede lanzar tambin la e excepcin IllegalArgumentException que no tiene que ser vigilada si se o le da un argumento negativo. Regresa el nmero de carcteres realmente u a saltados.
3

Estamos trabajando con JDK Standard Edition 5.0 o posteriores

344

Entrada y salida

public boolean ready() throws IOException


Dice si el ujo de entrada tiene carcteres listos (true) o si va a tener a que bloquear la entrada y esperar a que se proporcionen ms carcteres a a (false).

public abstract void close () throws IOException


Cierra el ujo y libera los recursos asociados a l. Cualquier accin sobre e o el ujo despus de cerrado va a lanzar una excepcin. Si se cierra un ujo e o ya cerrado no pasa nada. Como ya mencionamos, este ujo es sumamente primitivo. Podr amos leer a un arreglo de carcteres y posteriormente convertirlo a cadena, pero preferimos leer a directamente a una cadena. Un amigo nos sugiere la clase DataInputStream, que aunque hereda de la clase InputStream la revisamos. Esta clase hereda directamente de FilterInputStream, quien a su vez hereda de InputStream. Esta ultima presenta los mismos mtodos que Reader, excepto que, como ya vimos, trabaja con bytes e en lugar de carcteres (lee a un byte, a un arreglo de bytes, cuenta nmero de a u bytes,. . . ). El constructor es un constructor por omisin, y en lugar del mtodo o e booleano ready de Reader, tiene el siguiente mtodo, que no listamos al presentar e a esta clase:

public abstract class InputStream implements Closeable


Mtodo adicional: e

public int available () throws IOException


Regresa el nmero de bytes que se pueden leer de este ujo sin bloquear u la entrada. A este nivel de la jerarqu siempre regresa 0. a Al igual que en la clase Reader, el mtodo read() es abstracto, por lo que no se e pueden construir objetos de esta clase, sino que se tiene que recurrir a alguna de sus extensiones. Revisemos lo que nos interesa de la clase FilterInputStream, de la que hereda directamente DataInputStream:

public class FilterInputStream extends InputStream


Campo:

protected InputStream in
El ujo a ser ltrado (manipulado).

10.5 El manejo del men de la aplicacin u o

345

Constructor:

protected FilterInputStream(InputStream in)


Construye el ujo con in como ujo subyacente. Mtodos: e Hereda los mtodos ya implementados de InputStream e implementa los e declarados como abstractos en la superclase. Como los encabezados y su signicado se mantiene, no vemos el caso de repetir esta informacin. o Tampoco FilterInputStream nos acerca ms a leer valores, no carcteres. Veamos a a qu pasa en DataInputStream, que hereda el campo de FilterInputStream y los e mtodos de esta clase. Unicamente listaremos algunos de los que nos interesan y e que no estn en las superclases. No daremos una explicacin de cada mtodo ya a o e que los nombres son autodescriptivos.

public class DataInputStream extends FilterInputStream implements DataInput


Constructores:

public DataInputStream(InputStream in)


Este ujo hereda de FilterInputStream y trabaja como intermediario entre el dispositivo y la aplicacin, por lo que hay que proporcionarle el o dispositivo (in). Mtodos que se agregan: e

public nal int readUnsignedByte() throws IOException


Regresa un entero entre 0 y 255.

public nal short readShort() throws IOException


Lee un short.

public public public public public public

nal nal nal nal nal nal

int readUnsignedShort() throws IOException char readChar() throws IOException int readInt () throws IOException long readLong() throws IOException oat readFloat () throws IOException double readDouble() throws IOException

346

Entrada y salida

public nal String readLine () throws IOException public nal String readUTF() throws IOException
Lee una cadena que ha sido codicada con formato UTF-8.

public nal String readUTF(DataInput in) throws IOException


Lee una cadena que ha sido codicada con formato UTF-8 desde un ujo que implemente a DataInput. No existe una clase paralela a sta en la jerarqu de Reader. Sin embargo, al ree a visar la documentacin vemos que nuestro mtodo favorito, readLine(), est anuno e a ciado como descontinuado (deprecated ), lo que quiere decir que no lo mantienen al d y lo usar a amos arriesgando problemas. Pero tambin en la documentacin viee o ne la sugerencia de usar BueredReader, subclase de Reader, por lo que volteamos hacia ella para resolver nuestros problemas. Presentamos nada ms lo agregado a en esta clase, ya que los mtodos que hereda los [presentamos en la clase Reader e antes. Como su nombre lo indica, esta clase trabaja con un buer de lectura. No listaremos los mtodos que sobreescribe (overrides) ya que tienen los mismos e parmetros y el mismo resultado, as que los pensamos como heredados aunque a estn redenidos en esta clase. e

public class BueredReader extends Reader


Constructores:

public BueredReader(Reader in)


Construye el ujo con un buer de tamao por omisin (generalmente n o sucientemente grande).

public BueredReader(Reader in , int sz)


Construye el ujo con un buer de tamao sz. (generalmente sucienten mente grande). Puede lanzar la excepcin IllegalArgumentException si el o tamao del buer es negativo. n Mtodos adicionales: e

public String readLine () throws IOException


Lee una cadena hasta el n de linea, pero no guarda el n de l nea. La l nea se considera terminada con un n de l nea (zn), un retorno de carro o ambos. Como se puede ver, tampoco esta clase es muy verstil, pero como lo unico que a

10.5 El manejo del men de la aplicacin u o

347

queremos, por el momento, es leer cadenas, nos conformamos. Tenemos ya todo lo que necesitamos para sustituir nuestra clase Consola en el programa. Listaremos nuevamente la forma anterior de las lecturas y la forma que ahora toman. Notar que ahora tenemos que colocar cada lectura en un bloque try. . . catch porque todos los mtodos que llevan a cabo lectura pueden lanzar excepciones de entrada/salida. e Antes: p r i v a t e S t r i n g pideNombre ( C o n s o l a c o n s ) { S t r i n g nombre ; System . o u t . p r i n t ( "Dame el nombre del estudiante ," + " empezando por apellido paterno:" ) ; nombre=c o n s . l e e S t r i n g ( ) ; r e t u r n nombre ; } Despus: e p r i v a t e S t r i n g pideNombre ( B u f f e r e d R e a d e r c o n s ) throws I O E x c e p t i o n { S t r i n g nombre ; System . o u t . p r i n t ( "Dame el nombre del estudiante ," + " empezando por apellido paterno:" ) ; try { nombre=c o n s . r e a d L i n e ( ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "Error al leer nombre" ) ; throw e ; } // end o f t r y c a t c h r e t u r n nombre ; }

El mismo trato se da a los mtodos pideCarrera, pideClave y pideCuenta, ya que e todos estos mtodos leen cadenas. En el men los cambios que tenemos que hacer e u se listan a continuacin: o Antes: s o p c i o n = c o n s . l e e S t r i n g ( "Elige una opci n --> " ) ; o

348

Entrada y salida

Despus: e System . o u t . p r i n t ( "Elige una opci n --> " ) ; o try { sopcion = cons . readLine ( ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "Error al leer opci n" ) ; o throw new I O E x c e p t i o n ( "Favor de repetir elecci n " ) ; o } // end o f t r y c a t c h Antes: case AGREGA : // Agrega E s t u d i a n t e nombre = pideNombre ( c o n s ) ; cuenta = pideCuenta ( cons ) ; c a r r e r a = p i d e C a r r e r a ( cons ) ; c l a v e = pide Clave ( cons ) ; miCurso . a g r e g a E s t O r d e n (new E s t u d i a n t e ( nombre , c u e n t a , c l a v e , c a r r e r a ) ) ; Despus: e case AGREGA : // Agrega E s t u d i a n t e try { nombre = pideNombre ( c o n s ) ; cuenta = pideCuenta ( cons ) ; c a r r e r a = p i d e C a r r e r a ( cons ) ; c l a v e = pide Clave ( cons ) ; miCurso . a g r e g a E s t O r d e n (new E s t u d i a n t e ( nombre , c u e n t a , c l a v e , c a r r e r a ) ) ; } catch ( I O E x c e p t i o n e ) { System . o u t . p r i n t l n ( "Error al leer datos del " + " estudiante .\ nNo se pudo agregar" ) ; } // end o f t r y c a t c h Antes: case QUITA : // Q u i t a e s t u d i a n t e nombre = pideNombre ( c o n s ) ; donde = ( E s t u d i a n t e ) miCurso . b u s c a S u b c a d ( E s t u d i a n t e .NOMBRE, nombre ) ;

10.5 El manejo del men de la aplicacin u o

349

i f ( donde != n u l l ) { nombre = donde . daNombre ( ) ; miCurso . q u i t a E s t ( nombre ) ; System . o u t . p r i n t l n ( "El estudiante :\n\t" + nombre + "\n Ha sido eliminado " ) ; } e l s e r e p o r t a N o ( nombre ) ; Despus: e case QUITA : // Q u i t a e s t u d i a n t e try { nombre = pideNombre ( c o n s ) ; donde = ( E s t u d i a n t e ) miCurso . b u s c a S u b c a d ( E s t u d i a n t e .NOMBRE, nombre ) ; i f ( donde != n u l l ) { nombre = donde . daNombre ( ) ; miCurso . q u i t a E s t ( nombre ) ; System . o u t . p r i n t l n ( "El estudiante :\n\t" + nombre + "\nHa sido eliminado " ) ; } e l s e r e p o r t a N o ( nombre ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No se proporcionaron bien" + " los datos .\ nNo se pudo eliminar." ) ; } // end o f t r y c a t c h Antes: case BUSCA : // Busca s u b c a d e n a s u b c a d = c o n s . l e e S t r i n g ( "Dame la subcadena a " + "buscar: " ) ; do { s c u a l = c o n s . l e e S t r i n g ( "Ahora dime de cu l " a + "campo : 1: Nombre 2: Cuenta , 3: Carrera" + ", 4: Clave -->" ) ; c u a l = "01234" . i n d e x O f ( s c u a l ) ; i f ( c u a l < 1) System . o u t . p r i n t l n ( "Opci n no v lida" ) ; o a } while ( c u a l < 1 ) ;

350

Entrada y salida

Antes:

(contina. . . ) u donde = ( E s t u d i a n t e ) miCurso . buscaSubcad ( cual , subcad ) ; i f ( donde != n u l l ) System . o u t . p r i n t l n ( donde . d a R e g i s t r o ( ) ) ; else reportaNo ( subcad ) ;

Despus: e case BUSCA : // Busca s u b c a d e n a try { System . o u t . p r i n t ( "Dame la subcadena a "+ "buscar: " ) ; subcad = cons . r e a d L i n e ( ) ; do { System . o u t . p r i n t ( "Ahora dime de cu l " a + "campo : 1: Nombre 2: Cuenta" + " 3: Carrera 4: Clave -->" ) ; s c u a l = cons . readLine ( ) ; c u a l = "01234" . i n d e x O f ( s c u a l ) ; i f ( c u a l < 1) System . o u t . p r i n t l n ( "Opci n no v lida" ) ; o a } while ( c u a l < 1 ) ; donde = ( E s t u d i a n t e ) miCurso . buscaSubcad ( cual , subcad ) ; i f ( donde != n u l l ) System . o u t . p r i n t l n ( donde . d a R e g i s t r o ( ) ) ; else reportaNo ( subcad ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "Error al dar los " + "datos para buscar" ) ; } // end o f t r y c a t c h Antes: case LISTAALGUNOS : // L i s t a con c r i t e r i o s u b c a d = c o n s . l e e S t r i n g ( "Da la subcadena que " + " quieres contengan los " + " registros :" ) ; do { s c u a l = c o n s . l e e S t r i n g ( "Ahora dime de cu l campo:" a + " 1: Nombre 2: Cuenta 3: Carrera 4: Clave -->" ) ;

10.5 El manejo del men de la aplicacin u o

351

Antes:

(contina. . . ) u c u a l = "01234" . i n d e x O f ( s c u a l ) ; i f ( c u a l < 1) System . o u t . p r i n t l n ( "Opci n no v lida" ) ; o a } while ( c u a l < 1 ) ; miCurso . losQueCazanCon ( System . out , c u a l , s u b c a d ) ;

Despus: e case LISTAALGUNOS : // L i s t a con c r i t e r i o try { System . o u t . p r i n t l n ( "Da la subcadena que " + " quieres contengan los " + " registros :" ) ; subcad = cons . r e a d L i n e ( ) ; do { System . o u t . p r i n t ( "Ahora dime de cu l campo:" a + "1: Nombre 2: Cuenta 3: Carrera 4: Clave -->" ) ; s c u a l = cons . readLine ( ) ; c u a l = "01234" . i n d e x O f ( s c u a l ) ; i f ( c u a l < 1) System . o u t . p r i n t l n ( "Opci n no v lida" ) ; o a } while ( c u a l < 1 ) ; miCurso . losQueCazanCon ( System . out , c u a l , s u b c a d ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "Error al dar los datos" + "para listar" ) ; } // end o f t r y c a t c h Antes: p u b l i c s t a t i c void main ( S t r i n g [ ] a r g s ) ... C o n s o l a c o n s o l a = new C o n s o l a ( ) ; {

Despus: e p u b l i c s t a t i c void main ( S t r i n g [ ] a r g s ) { ... B u f f e r e d R e a d e r c o n s o l a = new B u f f e r e d R e a d e r (new I n p u t S t r e a m R e a d e r ( System . i n ) ) ;

352

Entrada y salida

Antes: w h i l e ( ( o p c i o n = miMenu . daMenu ( c o n s o l a , miCurso ) ) != 1); Despus: e w h i l e ( o p c i o n != 1) { try { o p c i o n = miMenu . daMenu ( c o n s o l a , miCurso ) ; } catch ( I O E x c e p t i o n e ) { System . o u t . p r i n t l n ( "Opci n mal elegida" ) ; o o p c i o n =0; } // end o f t r y c a t c h } // w h i l e

10.6 Redireccionamiento de in, out y err


Muchas veces queremos que los resultados de un programa, o los mensajes de error, en lugar de ir a los dispositivos estndar (todos a la consola) se graben en a algn archivo en disco para poder examinarlos con calma. Adems de la manera en u a que Unix permite redireccionar la salida, podemos, desde el programa, conseguir esto. Para ello contamos con mtodos en java.lang.System que permiten hacerlo. e Ellos son:
p u b l i c f i n a l s t a t i c v o i d s e t I n ( I n p u t S t r e a m newIn ) p u b l i c f i n a l s t a t i c v o i d s e t O u t ( OutputStream newOut ) p u b l i c f i n a l s t a t i c v o i d s e t E r r ( P r i n t S t r e a m newErr )

A continuacin dos ejemplos: o


System . s e t I n ( new F i l e I n p u t S t r e a m ( " misdatos .txt" ) ) ; System . s e t O u t ( new P r i n t S t r e a m ( new F i l e O u t p u t S t r e a m ( " misdatos .out" ) ) ) ;

10.7 Persistencia de la base de datos

353

10.7 Persistencia de la base de datos


Hasta ahora unicamente hemos trabajado con la consola o bien con redirec cionamiento de la consola, pero no hemos entrado a la motivacin principal de o este cap tulo y que consiste en lograr guardar el estado de nuestra base de datos para que pueda ser utilizado posteriormente como punto de partida en la siguiente ejecucin. o Podemos almacenar, en primera instancia, la base de datos como un conjunto de cadenas, y para ello podemos volver a utilizar a los ujos BueredReader y BueredWriter que ya conocemos, pero en esta ocasin queremos que el ujo o subyacente sea un archivo en disco y no un ujo estndar. Revisemos entonces la a clase FileReader y FileWriter que me van a dar esa facilidad. Estos ujos extienden, respectivamente, a InputStreamReader y OutputStreamWriter, que a su vez heredan, respectivamente, de Reader y Writer. De esta jerarqu unicamente hemos a revisado la clase Reader, as que procedemos a ver las otras clases de la jerarqu a que vamos a requerir.

public abstract class Writer implements Appendable, Closeable, Flushable


Campo:

protected Object lock


Sincroniza el acceso a este ujo. Constructores:

protected Writer()
Construye un ujo de salida de carcteres a ser sincronizado por l mismo. a e

protected Writer(Object lock)


Construye un ujo de carcteres que ser sincronizado usando lock. a a Mtodos: e

public Writer append(char c) throws IOException


Agrega el argumento al ujo this.

public Writer append(CharSequence csq) throws IOException


CharSequence es una interfaz de Java que bsicamente proporciona mtoa e dos para convertir una sucesin de carcteres, codicados en cualquier o a cdigo de 16 bits en cadenas o subsucesiones. Agrega el argumento al o ujo this.

354

Entrada y salida

public Writer append(CharSequence csq, int start , int end) throws IOException
Agrega al ujo this la subsucesin de csq que empieza en start y termina o en el carcter inmediatamente a la izquierda de end. a

public abstract void close () throws IOException


Cierra el ujo vacindolo primero. Una vez cerrado, cualquier intento de a vaciarlo o escribir en l provocar una excepcin de entrada/salida. No e a o importa que un ujo se intente cerrar una vez cerrado.

public abstract void ush () throws IOException


Provoca que todas las escrituras pendientes sean vaciadas al ujo proporcionado por el sistema operativo. Sin embargo, el sistema operativo podr no vaciar su buer. a

public void write (int c) throws IOException


Escribe un unico carcter en el ujo, tomando los 16 bits bajos del entero a proporcionado. Este mtodo deber ser sobreescrito por las subclases e a correspondientes.

public void write (char[] cbuf) throws IOException


Escribe en el ujo los carcteres presentes en el arreglo cbuf. a

public abstract void write(char [], int o , int len ) throws IOException
Escribe en el ujo el contenido del arreglo cbuf a partir del carcter en la a posicin o y un total de len carcteres. o a

public void write ( String str ) throws IOException


Escribe el contenido de la cadena en el ujo.

public void write ( String str , int o , int len ) throws IOException
Escribe la subcadena de str desde la posicin o un total de len carcteres. o a Realmente el unico mtodo con el que hay que tener cuidado es el que escribe e un carcter (entero), porque el resto de los mtodos se construyen simplemente a e invocando a ste. e Las clases que heredan directamente de Reader y Writer son, respectivamente,

10.7 Persistencia de la base de datos

355

InputStreamReader y OutputStreamWriter que pasamos a revisar. Primero revisaremos la clase InputStreamReader.

public class InputStreamReader extends Reader


Hereda el campo lock de Reader y los mtodos denidos en la superclase. e Listamos los que son redenidos en esta subclase. Constructores:

public InputStreamReader(InputStream in)


Construye un ujo de entrada sobre el ujo de bytes que se le proporcione (y que debe existir como objeto). Bsicamente va a traducir bytes en a carcteres. a

public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException


Construye un ujo de entrada sobre un ujo de bytes. Va a traducir de acuerdo al cdigo nombrado en charsetName. Si el cdigo no existe lanza o o una excepcin de cdigo invlido. o o a

public InputStreamReader(InputStream in, Charset cs)


Construye un ujo sobre in y que va a traducir los bytes de acuerdo al cdigo dado en cs. o Mtodos: e

public void close () throws IOException


Cierra el ujo correspondiente. Lanza una excepcin si hay algn error de o u entrada/salida.

public String getEncoding()


Regresa el nombre del cdigo de traduccin de bytes que es el usado por o o este ujo.

public int read() throws IOException


Redene el mtodo correspondiente en Reader (recurdese que en Reader e e era un mtodo abstracto). e

public int read(char[] cbuf, int oset , int length) throws IOException
Redene el mtodo correspondiente en Reader. e

public boolean ready() throws IOException


Redene el mtodo ready en Reader. e

356

Entrada y salida

Siguiendo en orden en la jerarqu ahora revisaremos OutputStreamWriter. a,

public class OutputStreamWriter extends Writer


Hereda el campo lock de la superclase. Listaremos los constructores y los mtodos abstractos de la superclase que se implementan en esta clase. e Constructores:

public OutputStreamWriter(OutputStream out)


Construye un ujo de salida que va a convertir carcteres en bytes. Se a monta sobre un ujo de salida de bytes.

public OutputStreamWriter(OutputStream out, Charset cs)


Construye un ujo de salida de carcteres a bytes, montado sobre un ujo a de salida de bytes, out que usa la codicacin dada por cs. o

public OutputStreamWriter(OutputStream out, CharsetEncoder enc)


Construye un OutputStreamWriter sobre un ujo de bytes out y que usa la codicacin dada por enc. o Mtodos: e

public void close () throws IOException


Cierra el ujo vaciando el buer de salida. Lanza una excepcin si tiene o problemas de entrada/salida.

public void ush () throws IOException


Implementa el mtodo correspondiente en Writer. e

public String getEncoding()


Regresa el nombre del cdigo que se est usando para escribir los bytes o a correspondientes a los carcteres en memoria. a

public void write (int c) throws IOException


Implementa el mtodo correspondiente en Writer. e

public void write (char[] cbuf, int o int len ) throws IOException
Implementa al mtodo correspondiente en Writer. e

public void write ( String str , int o int len ) throws IOException
Implementa al mtodo correspondiente en Writer. e

10.7 Persistencia de la base de datos

357

Ahora s ya podemos pasar a revisar las clases FileReader y FileWriter que here dan respectivamente de InputStreamReader y OutputStreamWriter. Empezaremos por el ujo de entrada. En esta subclase unicamente se denen los constructores, ya que se heredan precisa y exactamente los mtodos implementados en InputSe treamReader.

public class FileReader extends InputStreamReader


Dado que sta es una subclase de InputStreamReader, va a leer bytes y e convertirlos en carcteres. Al igual que su superclase, tambin trabaja a e sobre un InputStream. Constructores:

public FileReader ( String leName) throws FileNotFoundException


Crea un ujo para lectura de disco cuyo nombre es leName. Si no encuentra el ujo lanza una excepcin de archivo no encontrado. o

public FileReader ( File le ) throws FileNotFoundException


File es una representacin abstracta de un archivo del sistema operativo o en cuestin, e incluye aspectos como denir el separador y terminador de o archivos, la ruta en el disco, la identicacin del archivo en disco, etc. o (ver la denicin de esta clase en la documentacin de Java). Crea un o o ujo para lectura identicado con le. Si no encuentra el ujo lanza una excepcin de archivo no encontrado. o

public FileReader ( FileDescriptor fd) throws FileNotFoundException


Un FileDescriptor es un objeto que describe a un archivo en disco (ver documentacin de Java). Crea un ujo de lectura desde disco, donde el o archivo est asociado al FileDescriptor. Si no lo encuentra, lanza una exa cepcin de archivo no encontrado. o Mtodos: e Los heredados de Reader y de InputStreamReader, que ya revisamos. Para los ujos de salida que escriben a disco tenemos una situacin similar a o la de archivos de entrada, pues lo unico que se dene para la subclase FileWri ter son los constructores. Para el resto de los mtodos y campos se heredan las e implementaciones dadas por OutputStreamWriter. Veamos la denicin. o

358

Entrada y salida

public class FileWriter extends OutputStreamWriter


Enlaza a un ujo de salida de bytes con un archivo en disco. Lo unico que se implementa a nivel de esta subclase son los constructores. Constructores:

FileWriter ( File le ) throws IOException


Construye un ujo a disco sobre el ujo le, que podr ser, a su vez, un a FileWriter o cualquier OutputStream o subclases de sta. La excepcin la e o lanza si le es un directorio y no un archivo, no existe pero no puede ser creado o no puede ser abierto por cualquier otra razn. o

FileWriter ( File le , boolean append) throws IOException


Construye un ujo a disco sobre el ujo le, que podr ser, a su vez, a un FileWriter o cualquier OutputStream o subclases de sta. La excepcin e o la lanza si le es un directorio y no un archivo, no existe pero no puede ser creado o no puede ser abierto por cualquier otra razn. Si append es o verdadero, la escritura se realiza al nal del archivo; si es falsa se realiza al principio del archivo, como en el constructor sin parmetro booleano. a

FileWriter ( FileDescriptor fd)


Construye un ujo a disco asociado con el FileDescriptor.

FileWriter ( String leName) throws IOException


Construye el ujo dado un nombre. Lanza una excepcin de entrada/salida o por las mismas causas que los otros constructores.

FileWriter ( String leName, boolean append) throws IOException


Construye el ujo dado un nombre. Si append es verdadera entonces escribe al nal del archivo; si el archivo no existe lo crea. Lanza una excepcin o de entrada/salida por las mismas causas que los otros constructores.

Hay que recordar que la herencia permite que donde quiera que aparezca una clase como parmetro, los argumentos pueden ser objetos de cualquiera de sus suba clases. Con esto en mente pasamos a implementar las opciones en el men de leer u de un archivo en disco o escribir a un archivo en disco para guardar la informacin o generada en una sesin dada. o

10.7 Persistencia de la base de datos

359

10.7.1.

Cmo guardar datos en un archivo en disco o


La informacin en disco se guarda invariablemente en bytes. De hecho, un o archivo en disco es, simplemente, una sucesin muy grande de bytes, que puede o ser interpretado de muy diversas maneras: podemos tomar los bytes de cuatro en cuatro, e interpretar cada cuatro bytes como un entero; o podemos tomarlos de seis en seis e interpretar a cada grupo como un doble. En ningn otro caso es ms u a cierto el dicho de que todo depende del color del cristal con que se mira que en la lectura (interpretacin) de archivos en disco. Y esta interpretacin depende del o o enunciado con que se tenga acceso al archivo y el tipo de ujo que se utilice para ello. Como mencionamos al principio, por lo pronto escribiremos y leeremos (recuperaremos) cadenas, representadas por sucesiones de bytes y separadas entre s por carcteres de n de l a nea. Dada esta situacin utilizaremos para entrada o objetos de la clase FileReader y para salida objetos de la clase FileWriter. Agregaremos a nuestro men tres opciones nuevas, leer registros desde disco, u escribir registros a disco y agregar registros en disco a un archivo que ya tiene informacin. Qu archivo usar deber ser una decisin que se toma una vez elegida o e a o alguna de estas opciones. Consideramos que cualquiera de estas acciones puede llevarse a cabo en cualquier momento del proceso y por lo tanto, la eleccin del o archivo debe hacerse en el momento en que se elige la opcin. La otra opcin o o pudiese ser elegir el archivo al entrar al proceso. Pero eso obligar al usuario, ya a sea que quiera o no leer de/guardar en archivos de disco, proporcionar el nombre para los mismos, algo que no consideramos adecuado. Este curso de accin tambin o e evitar que se pudiera leer o escribir, en una misma sesin, ms de un archivo. a o a Por lo tanto, la denicin del archivo a usar estar situada dentro de la opcin o a o correspondiente. Podemos implementar ya esta opcin. Lo primero que hacemos es declarar tres o constantes simblicas para usarlas en el switch, y que son: o s t a t i c f i n a l i n t AGREGA = 1 , ... LEER = 6 , GUARDAR = 7 , PEGARDISCO = 8 ; La opcin de guardar lo que llevamos en un archivo en disco debe seguir el o guin dado en la gura 10.10. o Para identicar el archivo que deseamos usar, algo que tendremos que hacer en los tres casos, construimos un mtodo que lea el nombre del archivo de disco e

360 en consola, y que queda como se ve en el listado 10.1.

Entrada y salida

Figura 10.10

Algoritmo para guardar la base de datos en disco


6 9 9 9 9 9 9 9Inicio 9 9 9 9 9 9 9 9 Guardar Base de 9 8 6 9Identicar archivo 9 9 8

Abrir el archivo 9Colocarse al principio 9 9 7 de la base de datos

Datos en archivo en disco

5 5 9 9 9 Procesar registro Escribir registro en disco 9Proceso 9 9 9 9 (mientras haya) Pasar al siguiente 9 9 9 9 9 9 3 9 9 7

F inal

Cerrar el archivo

Cdigo 10.1 Mtodo que solicita al usuario nombre de archivo o e


50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74:

(MenuListaIO)

/ P i d e a l u s u a r i o e l nombre d e l a r c h i v o en e l que d e s e a e s c r i b i r o d e l que d e s e a l e e r . @param c o n s D i s p o s i t i v o d e l que va a l e e r e l nombre @param l e c t u r a True : l e c t u r a , f a l s e : e s c r i t u r a . @return e l archivo s o l i c i t a d o . / p u b l i c S t r i n g pideNombreArch ( B u f f e r e d R e a d e r cons , i n t c a s o ) throws I O E x c e p t i o n { S t r i n g m e n s a j e = "Por favor dame el nombre del archivo \n" + ( c a s o == LEER ? "del que vas a leer registros " : ( c a s o == GUARDAR ? "en el que vas a guardar la base de datos " : "en el que vas a agregar registros " ) ) + ":\t" ; S t r i n g nombre ; try { System . o u t . p r i n t ( m e n s a j e ) ; nombre = c o n s . r e a d L i n e ( ) ; } catch ( I O E x c e p t i o n e ) { throw e ; } // end o f t r y c a t c h r e t u r n nombre ; }

Al mtodo pideNombreArch le pasamos el ujo que estamos usando para comue

10.7 Persistencia de la base de datos

361

nicarnos con el usuario. Queremos que el mensaje sea preciso respecto a qu vamos e a hacer con el archivo, pero como usamos el mismo mtodo simplemente le pasae mos de cual caso se trata caso para que pueda armar el mensaje correspondiente (l neas 59: a 64:). Despus entramos a un bloque try. . . catch en el que vamos a e leer del usuario el nombre del archivo. El mtodo, como lo indica su encabezado, e exporta la excepcin que pudiera lanzarse al leer el nombre del archivo. Estamos o listos ya para programar el algoritmo de la gura 10.10 en la pgina opuesta. El a cdigo lo podemos ver en el listado 10.2. o

Cdigo 10.2 Cdigo para guardar la base de datos o o


200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234:

(MenuListaIO)

case GUARDAR: try { s A r c h i v o = pideNombreArch ( cons ,GUARDAR ) ; a r c h i v o O u t = new B u f f e r e d W r i t e r ( new F i l e W r i t e r ( s A r c h i v o ) ) ; System . o u t . p r i n t l n ( "Abr archivo " ) ; E s t u d i a n t e l i s t a = ( ( E s t u d i a n t e ) miCurso . d a L i s t a ( ) ) ; w h i l e ( l i s t a != n u l l ) { a r c h i v o O u t . w r i t e ( l i s t a . daNombre ( ) ) ; archivoOut . newLine ( ) ; a r c h i v o O u t . w r i t e ( l i s t a . daCuenta ( ) ) ; archivoOut . newLine ( ) ; archivoOut . write ( l i s t a . daCarrera ( ) ) ; archivoOut . newLine ( ) ; archivoOut . w r i t e ( l i s t a . daClave ( ) ) ; archivoOut . newLine ( ) ; System . o u t . p r i n t l n ( l i s t a . daNombre ()+"\n" ) ; System . o u t . p r i n t l n ( l i s t a . daCuenta ()+"\n" ) ; System . o u t . p r i n t l n ( l i s t a . d a C a r r e r a ()+"\n" ) ; System . o u t . p r i n t l n ( l i s t a . d a C l a v e ()+"\n" ) ; l i s t a = l i s t a . daSiguiente (); } // end o f w h i l e ( l i s t a != n u l l ) archivoOut . f l u s h ( ) ; archivoOut . c lose ( ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude abrir archivo " ) ; } // end o f t r y c a t c h finally { try { i f ( a r c h i v o O u t != n u l l ) { archivoOut . c lose ( ) ; } } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( No pude c e r r a r a r c h i v o ) ; } // end o f f i n a l l y r e t u r n GUARDAR;

362

Entrada y salida

Colocamos toda la opcin en un bloque try. . . catch porque queremos suspender o en cuanto se presente una primera excepcin, ya sea que no podemos abrir el o archivo o que haya algn registro que no podemos escribir. El bloque tiene clusula u a nally l neas 226: a 233: para que en caso de que haya algn problema se proceda u a cerrar el archivo. Se verica antes de intentar cerrarlo que el archivo exista y se haya logrado abrir. En las l neas 202: y 203: solicitamos el nombre del archivo a usar y procedemos a abrir el archivo. En este punto la unica excepcin que pudo haber sido lanzada o es en la interaccin con el usuario, ya que la apertura de un archivo en disco o dif cilmente va a lanzar una excepcin. o En la l nea 205: nos colocamos al principio de la lista. Como el mtodo mie Curso.daLista() regresa un objeto de tipo Object tenemos que hacer un casting. Una vez que estamos al principio de la lista, procedemos a escribir registro por registro l neas 207: a 210:. Escribimos campo por campo, en el orden en que estn en el registro, separando los campos entre s por un carcter de n de l a a nea archivoOut.newLine() propio del sistema operativo en el que est trabajane do la aplicacin. En las l o neas 212: a 215: simplemente se hace eco de lo que se escribi en el disco como medida de vericacin. o o En seguida se pasa al siguiente registro para procesarlo de la misma manera. Al terminar simplemente se cierra el archivo, haciendo persistente el contenido de la lista en memoria.

10.7.2.

Cmo leer registros de un archivo de disco o

El algoritmo para leer registros de una archivo en disco es la imagen del proceso para guardar. Este se puede ver en la gura 10.11. Para identicar el archivo del que vamos a leer usamos el mismo mtodo, e excepto que con un mensaje apropiado. Al abrir el archivo automticamente nos a encontraremos frente al primer registro. A partir de ah suponemos que el archivo , est correcto y que hay cuatro cadenas sucesivas para cada registro que vamos a a leer. El cdigo que corresponde a esta opcin se encuentra en el listado 10.3 en la o o pgina opuesta. a

10.7 Persistencia de la base de datos

363

Figura 10.11

Algoritmo para leer registros desde disco


6 9 9 9 9 9 9 9Inicio 9 9 9 9 9 9 9 9 9 8 6 9Identicar archivo 9 9 8

Leer registros a la Base de Datos desde un archivo en disco

Abrir el archivo 9Colocarse al principio 9 9 7 del archivo

5 5 9 9 9 Procesar registro Leer registro de disco 9P roceso 9 9 9 9 (mientras haya) Pasar al siguiente 9 9 9 9 9 9 3 9 9 7

F inal

Cerrar el archivo

Cdigo 10.3 Opcin para leer registros desde disco o o


234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258:

(MenuListaIO) 1/2

case LEER : // L e e r de d i s c o try { s A r c h i v o = pideNombreArch ( cons , LEER ) ; a r c h i v o I n = new B u f f e r e d R e a d e r ( new F i l e R e a d e r ( s A r c h i v o ) ) ; w h i l e ( ( nombre = a r c h i v o I n . r e a d L i n e ( ) ) != n u l l ) { cuenta = a r c h i v o I n . readLine ( ) ; carrera = archivoIn . readLine ( ) ; clave = archivoIn . readLine ( ) ; miCurso . a g r e g a E s t F i n a l ( new E s t u d i a n t e ( nombre , c u e n t a , c a r r e r a , c l a v e ) ) ; } // end o f w h i l e ( ( nombre = a r c h i v o I n . r e a d L i n e ( ) ) != n u l l ) } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { System . o u t . p r i n t l n ( "El archivo " + s A r c h i v o + " no existe ." ) ; throw e ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude abrir archivo " ) ; } catch ( E x c e p t i o n e ) { System . o u t . p r i n t l n ( "NO alcanzaron los datos " ) ; i f ( c a r r e r a == n u l l ) { c a r r e r a = "????" ; System . o u t . p r i n t l n ( "No hubo carrera " ) ; } // end o f i f ( c a r r e r a == n u l l )

364

Entrada y salida

Cdigo 10.3 Opcin para leer registros desde disco o o


234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253:

(MenuListaIO) 2/2

i f ( c u e n t a == n u l l ) { c u e n t a = " 000000000 " ; System . o u t . p r i n t l n ( "No hubo cuenta " ) ; } // end o f i f ( c u e n t a == n u l l ) i f ( c l a v e == n u l l ) { c l a v e = "????" ; System . o u t . p r i n t l n ( "No hubo clave " ) ; } // end o f i f ( c l a v e == n u l l ) } // end o f c a t c h finally { i f ( a r c h i v o I n != n u l l ) { try { archivoIn . close (); } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude cerrar el" + " archivo de lectura " ) ; } // end o f t r y c a t c h } // end o f i f ( a r c h i v o I n != n u l l ) } // end o f f i n a l l y r e t u r n LEER ;

Nuevamente tenemos que encerrar nuestro proceso en un bloque try. . . catch, ya que as nos lo exigen los mtodos de entrada/salida que estamos utilizando. El e proceso principal, si todo marcha bien, consiste en leer el nombre del archivo en la l nea 239: y luego proceder a abrirlo. Una vez abierto el archivo se van a leer cuatro cadenas para considerarlas como los datos para un registro de estudiante, el cual se agrega a la base de datos en las l neas244: y 245:. Conforme se van leyendo las cadenas va avanzando el archivo, por lo que no hay necesidad de avanzarlo. Sin embargo, al leer el nombre s vericamos si se alcanz el n de archivo; estamos o suponiendo que los registros vienen completos en grupos de cuatro cadenas y en el orden en que se intentan leer. En la l nea 239: se verica que no se haya alcanzado el n de archivo; si se alcanz el n de archivo, el mtodo regresar una referencia o e a nula. Podemos encontrarnos con varios errores en este proceso. El primero de ellos es que pretendamos leer de un archivo que no existe. En este caso se lanza una excepcin de la clase FileNotFoundException que manejamos parcialmente: escribimos o un mensaje y exportamos la excepcin, ya que no se va a poder hacer nada. o El siguiente error que podr amos tener en nuestro proceso es que los grupos de cuatro cadenas no estn completos y no haya en el archivo un mltiplo de e u cuatro en el nmero de las cadenas. En este caso, al intentar leer cadenas nos u encontraremos ms all del n de archivo l a a neas 242: a 245: lo que lanzar una a

10.7 Persistencia de la base de datos

365

excepcin de la clase IOException, que es atrapada en la l o nea 251: y manejada simplemente no agregando ese registro incompleto y absorbiendo la excepcin. o La ausencia de sucientes datos tambin puede lanzar una excepcin de tie o po aritmtico, lo que preveremos en la l e nea 253: donde tratamos de averiguar cules fueron los datos que no pudimos leer, mandando el mensaje adecuado y a absorbiendo la excepcin. o Sin importar si hubo una excepcin o no trataremos de cerrar el archivo si es o que ste se abri, mediante una clusula nally en las l e o a neas 243: a 252: en la que, si al cerrar el archivo se lanz una excepcin, sta se absorbe despus del o o e e mensaje correspondiente.

10.7.3.

Cmo agregar a un archivo ya creado o

El algoritmo para agregar registros a un archivo creado previamente es exactamente igual que el que crea un archivo nuevo, excepto que al abrir el archivo hay que indicar que se busca uno que ya existe; adicionalmente, en lugar de escribir en el archivo procedemos a agregar (append ). Usando entonces el mismo algoritmo que para guardar, con los cambios que acabamos de mencionar, el cdigo para o esta opcin queda como se muestra en el listado 10.4. o

Cdigo 10.4 Opcin de agregar registros a un archivo en disco o o


300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314:

(MenuListaIO) 1/2

case PEGARDISCO : try { s A r c h i v o = pideNombreArch ( cons , PEGARDISCO ) ; a r c h i v o O u t = new B u f f e r e d W r i t e r ( new F i l e W r i t e r ( s A r c h i v o , t r u e ) ) ; E s t u d i a n t e l i s t a = ( ( E s t u d i a n t e ) miCurso . d a L i s t a ( ) ) ; w h i l e ( l i s t a != n u l l ) { a r c h i v o O u t . append ( l i s t a . daNombre ( ) ) ; archivoOut . newLine ( ) ; a r c h i v o O u t . append ( l i s t a . daCuenta ( ) ) ; archivoOut . newLine ( ) ; a r c h i v o O u t . append ( l i s t a . d a C a r r e r a ( ) ) ; archivoOut . newLine ( ) ; a r c h i v o O u t . append ( l i s t a . d a C l a v e ( ) ) ; archivoOut . newLine ( ) ;

366

Entrada y salida

Cdigo 10.4 Opcin de agregar registros a un archivo en disco o o


300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324:

(MenuListaIO) 2/2

System . o u t . p r i n t l n ( l i s t a . daNombre ()+"\n" ) ; System . o u t . p r i n t l n ( l i s t a . daCuenta ()+"\n" ) ; System . o u t . p r i n t l n ( l i s t a . d a C a r r e r a ()+"\n" ) ; System . o u t . p r i n t l n ( l i s t a . d a C l a v e ()+"\n" ) ; l i s t a = l i s t a . daSiguiente (); } // end o f w h i l e ( l i s t a != n u l l ) archivoOut . f l u s h ( ) ; archivoOut . c lose ( ) ; } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { System . e r r . p r i n t l n ( "El archivo " + s A r c h i v o + "no existe !!" ) ; throws e ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude abrir archivo " ) ; } // end o f t r y c a t c h finally { try { i f ( a r c h i v o O u t != n u l l ) { archivoOut . c lose ( ) ; } // end o f i f ( a r c h i v o O u t != n u l l ) } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude cerrar el archivo " ) ; } // end o f t r y c a t c h } // end o f f i n a l l y r e t u r n PEGARDISCO ;

Los unicos cambios en esta opcin son: o En las l neas 303: y 304:, donde se usa otro constructor para el ujo, el que permite indicar si se usa un archivo ya existente para agregar a l. e En las l neas 307: a 313: se usa el mtodo append en lugar del mtodo write, e e ya que deseamos seguir agregando al nal del archivo. En la l nea 308: se trata de atrapar una excepcin de archivo no encontrado, o ya que en el caso de querer agregar a un archivo ste debe existir, miene tras que en el contexto de crear un archivo nuevo, esta excepcin no puede o presentarse. Como se pudo observar, el manejo de ujos en Java tiene un trato uniforme y, una vez que se manejan las excepciones, no representa mayor problema. Un punto que hay que vigilar, sin embargo, es la construccin de ujos que se montan o sobre otros ujos. Ac hay que tener cuidado en usar el constructor adecuado a

10.8 Escritura y lectura de campos que no son cadenas

367

y proporcionar un ujo que pertenezca a la clase que indica el constructor. La eleccin del ujo adecuado depende de qu es lo que queremos hacer con l y o e e cules son las operaciones ms frecuentes que esperamos. Los ujos que hemos a a usado hasta ahora manejan de manera idnea lectura y escritura de cadenas, pero o puede suceder que sta no sea la operacin ms frecuente o la que busquemos e o a facilitar. Un comentario nal: cuando en nuestra aplicacin requerimos de ujos para o montar en ellos al ujo nal con el que quer amos, los construimos al vuelo, es decir, de manera annima sin asignarles un identicador. Todas estas construco ciones las pod amos haber hecho en dos pasos, primero construir el archivo ms a primitivo (el que tiene contacto con el sistema) asignndole un identicador y a posteriormente construir el ujo de nuestra aplicacin. Como no se utiliza el aro chivo base de ninguna otra manera ms que para establecer la conexin no vimos a o necesario hacer esto ultimo.

10.8 Escritura y lectura de campos que no son cadenas


Supongamos que queremos tener en el disco una imagen similar a nuestra primera implementacin de la base de datos, una sucesin de carcteres, donde o o a sabemos donde empieza un registro y termina el otro porque contamos carcteres a (o bytes). Supongamos tambin que nuestro archivo es, de alguna manera, autoe descriptivo, esto es que tendr como encabezado del archivo (primera informacin) a o el nmero de campos y el tamao de cada uno de ellos. De lo anterior, nuestro u n archivo se ver como se muestra en la gura 10.12. Mostramos el contenido de a cada uno de los bytes4 en hexadecimal (4 bits para cada s mbolo). Por ejemplo, el encabezado de este archivo nos indica que cada registro consiste de cuatro (4) campos; el primer campo (nombre) ocupa 40 bytes; el segundo campo (cuenta) ocupa nueve bytes; el tercer campo (carrera) ocupa cuatro bytes; el cuarto campo (clave) ocupa 20 bytes. Cabe aclarar que la denominacin entre parntesis est fueo e a ra de la aplicacin; simplemente lo anotamos para tener una idea ms clara de o a qu es lo que estamos haciendo. Como los enteros que estamos manejando son e relativamente pequeos, los guardaremos en variables tipo short, que ocupa cada n una dos bytes. Las l neas punteadas indican la mscara que estamos aplicando a al archivo (cmo interpretamos la informacin) y el valor dentro de estas celdas o o indica el nmero en base 10 que est grabado. Abajo de cada celda se encuentra u a la posicin del byte correspondiente en el archivo, empezando de 0 (cero). o

368

Entrada y salida

Figura 10.12

Formato de un archivo binario autodescrito

n n n n Nm de tamao tamao tamao tamao u Primer campo campos campo1 campo2 campo3 campo4 hkkkikkkjhkkkikkkjhkkkikkkjhkkkikkkjhkkkikkkjhkkkkkkkkkkkkkkkkkkkkkkkkkikkkkkkkkkkkkkkkkkkkkkkkkkj short short short short short

410
0 1

4010
2 3

910
4 5

410
6 7

2010
8 9

z ...
16 . . . . . . 49

0 0 0 4 0 0 2 8 0 0 0 9 0 0 0 4 0 0 1 4 4 3 7 2 6 9 7A . .... .
10 11 12 13 14 15

Lo que conviene es que la clase para cada registro nos entregue el tamao de n cada campo. Podemos suponer que esto es as agregando a la clase InfoEstudiante , un arreglo con esta informacin: o
s h o r t [ ] tamanhos = { 4 , 4 0 , 9 , 4 , 2 0 } ;

quedando en tamanhos[0] el nmero de campos, en tamanhos[1] el tamao del primer u n campo y as sucesivamente. Agregamos a la clase un mtodo e
p u b l i c s h o r t getTamanho ( i n t campo ) r e t u r n tamanhos [ campo ] ; } {

que simplemente regresa el tamao del campo solicitado. n Podemos pensar en un archivo que es heterogneo, en el sentido de que lo que e llamamos el encabezado del mismo no tiene la forma que el resto de los elementos; stos se componen de n campos la n viene en los primeros dos bytes del archivo e con formato binario de un entero corto (short) con un total de k bytes que corresponde a la suma de los n enteros cortos que aparecen a partir del byte 2 del archivo. El encabezado del archivo consiste de 2pn 1q bytes. Una vez procesados estos n 1 enteros cortos, el resto del archivo lo podemos ver como un arreglo unidimensional de bytes (similarmente a como manejamos la base de datos en cadenas al principio). Deseamos insistir en lo que dijimos al principio de esta seccin: todos los o archivos en disco se componen de bytes; la manera de agrupar los bytes para obtener informacin que tenga sentido depende del software que se use para verlo, o de las mscaras que le apliquemos al archivo. Una vez que terminemos de armar a nuestro archivo con el formato que acabamos de ver, podrn observar el archivo a con alguno de los visores de su sistema operativo y vern que tambin los primeros a e 2pn 1q bytes podr tratar de interpretarlos como carcteres ASCII, no como an a variables de Java; por supuesto que si hacen esto la mayor de estos carcteres a a no se podrn ver en pantalla (por ejemplo, el 0 binario) o aparecern carcteres a a a que no guardan ninguna relacin con lo que ustedes esperar ver. o an

10.8 Escritura y lectura de campos que no son cadenas

369

Como queremos escribir y leer binario (imgenes de variables de Java) para el a encabezado del archivo, usaremos el ujo que nos permite hacer esto directamente y que es DataOutputStream y DataInputStream respectivamente. Esta ultima ya la revisamos en la pgina 345, por lo que pasamos a revisar la clase DataOutputSa tream, aunque van a ver que corre paralela al ujo correspondiente de entrada.

public class DataOutputStream extends FilterOutputStream implements DataOutput


Campos:

protected int written


Indica el nmero de bytes que se han escrito sobre este ujo hasta el u momento. Constructores:

public DataOutputStream(OutputStream out)


Construye sobre un ujo de salida que est conectado a algn dispositivo. e u Este es el unico constructor de este ujo. Mtodos: e

public void ush ()

throws IOException

Vac el buer de los bytes almacenados. Usa para ello el mtodo dado a e por el ujo de salida dado como argumento en el constructor.

public nal int size ()


Regresa el valor del campo written.

public void write (byte[] b, int o , int len )throws IOException


Escribe la porcin del arreglo de bytes b que empieza en o y tiene un o tamao mximo de len bytes. Si no se lanza una excepcin, written se n a o incrementa en len unidades.

void write (int b) public nal void write XXX (Y Y Y par)

throws IOException throws IOException

Implementa el mtodo write de la clase OutputStream. e Esta denominacin incluye en realidad a varios mtodos, que toman la o e representacin interna del tipo Y Y Y y lo transeren tal cual al ujo de o salida. A continuacin damos las combinaciones de XXX y Y Y Y que o tenemos en los distintos mtodos: e

370

Entrada y salida

Y Y Y Descripcin o boolean Escribe una booleana como un valor de 1 byte. Byte int Escribe un byte que corresponde a la parte baja del entero Bytes String Escribe la cadena como una sucesin de o bytes. Char int Escribe un carcter (los 2 bytes ms baa a jos del entero), el byte alto primero. Chars String Escribe la cadena como una sucesin de o carcteres (2 bytes por carcter). a a Double double Convierte el double a un long usando el mtodo doubleToLongBits de la clase e Double y luego escribe el valor obtenido como una sucesin de 8 bytes, el byte alto o primero. Float oat Convierte el valor oat a un valor entero (int) usando el mtodo oatToIntBits de e la clase Float para luego escribirlo como u7n entero de 4 bytes, el byte alto primero. Int int Escribe un entero en 4 bytes, byte alto primero. Long long Escribe el entero largo en 8 bytes, byte alto primero. Short int Escribe el entero corto en 2 bytes (los dos bytes ms bajos del entero) byte alto a primero. UTF String Escribe una cadena en el ujo usando codicacin UTF-8 modicada de manera o que es independiente de la computadora. Como se puede ver, este ujo sirve para escribir en disco imgenes (copias) a del contenido de variables en memoria, siguiendo el patrn de bits dado o para su codicacin binaria. Por esta ultima caracterizacin, a los archivos o o creados con este tipo de ujos se les conoce como archivos binarios, esto es, que los bytes deben interpretarse como si fueran variables en memoria.

XXX Boolean

10.8 Escritura y lectura de campos que no son cadenas

371

Es en este tipo de archivos donde realmente se puede utilizar el mtodo skip, e ya que el nmero de bytes que componen un registro lgico (que depende de la u o manera como lo tratemos de leer) es constante. Conocemos ya todo lo que requerimos para proponer una nueva opcin en o nuestro men, la que escribe y lee archivos binarios. Revisemos el cdigo agregado u o o modicado que se encuentra en los listados 10.5 para lo que tiene que ver con el proceso de la opcin y el listado 10.6 en la siguiente pgina para lo que tiene o a que ver con la opcin misma. o

Cdigo 10.5 Declaraciones de ujos binarios o


6: s t a t i c f i n a l i n t AGREGA = 1 ,

(MenuListaIO)

......
15: 16: LEERREGS = 9 , GUARDARREGS = 1 0 ;

......
128: 129: p u b l i c i n t daMenu ( B u f f e r e d R e a d e r cons , L i s t a C u r s o miCurso ) throws I O E x c e p t i o n {

......
141: 142: DataInputStream a r c h i v o R e g s I n = n u l l ; DataOutputStream a r c h i v o R e g s O u t = n u l l ;

......
156: 157: + "(9)\ tLeer de archivo binario \n" + "(A)\ tGuardar en archivo binario \n"

......
170: o p c i o n = " 0123456789 AB" . i n d e x O f ( s o p c i o n ) ;

...... Nuevamente optamos por declarar los ujos necesarios dentro del mtodo que e maneja el men. La razn de esto es que estas opciones se pueden elegir en cualu o quier momento y ms de una vez, en cada ocasin con ujos f a o sicos distintos, por lo que hacerlos globales a la clase o, peor an, al uso de la clase, amarrar a utilizar u a unicamente el ujo determinado antes de empezar, cuando existe la posibilidad de que no se elija esta opcin o que, como ya mencionamos, se desee hacer varias o copias de l;a informacin. En las l o neas 15:, 16: 156: a 170: simplemente agregamos dos opciones al men, y los mecanismos para manejarlas posponemos por el u momento el desarrollo de la opcin correspondiente dentro del switch . La declao

372

Entrada y salida

racin de los ujos la hacemos en las l o neas 141: y 142:. Tanto en este caso como en el los ujos BueredReader y BueredWriter podr amos haberlos declarado como objetos de las superclases correspondiente:
134: 135: Reader a r c h i v o I n = n u l l ; Writer archivoOut = null ;

141: 142:

InputStream archivoRegsIn = null ; OutputStream a r c h i v o R e g s O u t = n u l l ;

y determinar la subclase correspondiente en el momento de construirlos. De haberlo hecho as para usar los mtodos que se agregaron al nivel de Buered. . . , e como readLine y writeLine y Data. . . Stream como readShort y writeShort hubisemos tenido que hacer casting para que el compilador los identicara. e Pero, an cuando requerimos bsicamente un ujo de entrada y uno de salida, u a no podr amos tener nada ms una declaracin por funcin ya que la superclase a o o comn a Reader e InputStream es Object, y el usar a Object en la declaracin u o hubiese requerido de casting prcticamente en cada momento de uso de los ujos. a La asignacin de un valor null en la declaracin es para detectar, en su caso, si o o el ujo pudo construirse o no. Adicionalmente, dadas las caracter sticas de las excepciones, donde puede haber cdigo que no se ejecute, en el bloque catch no o se tiene la seguridad de que a las variables se les haya asignado efectivamente un valor en el bloque try, por lo que el compilador no va a permitir que las variables inicien sin valor asignado. En las l neas 156: y 157: del listado 10.5 simplemente agregamos las opciones correspondientes al men que se despliega, mientras que en la l u nea 170: del mismo listado modicamos para que estas opciones sean reconocidas.

Cdigo 10.6 Opciones de leer y escribir a archivo binario o


379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389:

(MenuListaIOReg) 1/3

case LEERREGS : try { s A r c h i v o = pideNombreArch ( cons , LEER ) ; a r c h i v o R e g s I n = new D a t a I n p u t S t r e a m ( new F i l e I n p u t S t r e a m ( s A r c h i v o ) ) ; } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { System . e r r . p r i n t l n ( "el archivo de entrada " + ( s A r c h i v o != n u l l ? s A r c h i v o : "nulo" ) + " no existe " ) ; throw e ; } // end o f t r y c a t c h

10.8 Escritura y lectura de campos que no son cadenas

373 (MenuListaIOReg) 2/3

Cdigo 10.6 Opciones de leer y escribir a archivo binario o


390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436:

try { s h o r t tam = a r c h i v o R e g s I n . r e a d S h o r t ( ) ; tamanhos = new s h o r t [ tam + 1 ] ; tamanhos [ 0 ] = tam ; f o r ( i n t i = 1 ; i <= tam ; i ++) tamanhos [ i ] = a r c h i v o R e g s I n . r e a d S h o r t ( ) ; s h o r t maxt = 0 ; f o r ( i n t i = 0 ; i <= tamanhos [ 0 ] ; i ++) maxt =( s h o r t ) ( Math . max ( maxt , tamanhos [ i ] ) ) ; bCadena = new byte [ maxt ] ; w h i l e ( a r c h i v o R e g s I n . r e a d ( bCadena , 0 , tamanhos [ 1 ] ) > 0 ) { nombre = new S t r i n g ( bCadena , 0 , tamanhos [ 1 ] ) ; f o r ( i n t i = 2 ; i <= tamanhos [ 0 ] ; i ++) { a r c h i v o R e g s I n . r e a d ( bCadena , 0 , tamanhos [ i ] ) ; switch ( i ) { case 2 : c u e n t a = new S t r i n g ( bCadena , 0 , tamanhos [ i ] ) ; break ; case 3 : c a r r e r a = new S t r i n g ( bCadena , 0 , tamanhos [ i ] ) ; break ; case 4 : c l a v e = new S t r i n g ( bCadena , 0 , tamanhos [ i ] ) ; break ; default : break ; } // end o f s w i t c h ( i ) } // end o f f o r ( i n t i = 1 ; i <= tamanhos [ 0 ] ; i ++) i f ( miCurso == n u l l ) { System . o u t . p r i n t l n ( "No existe miCurso " ) ; throw new N u l l P o i n t e r E x c e p t i o n ( " Uuups " ) ; } // end o f i f ( miCurso == n u l l ) miCurso . a g r e g a E s t F i n a l ( new E s t u d i a n t e ( nombre , c u e n t a , c a r r e r a , c l a v e ) ) ; } // end o f w h i l e ( a r c h i v o R e g s I n . r e a d ( bCadena } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( " Error de I/O" ) ; throw e ; } // end o f t r y c a t c h finally { try { archivoRegsIn . close (); } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No se pido cerrar el archivo " ) ; } // end o f t r y c a t c h } // end o f f i n a l l y r e t u r n LEERREGS ;

374

Entrada y salida

Cdigo 10.6 Opciones de leer y escribir a archivo binario o


437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473:

(MenuListaIOReg) 3/3

case GUARDARREGS : try { s A r c h i v o = pideNombreArch ( cons ,GUARDAR ) ; a r c h i v o R e g s O u t = new DataOutputStream ( new F i l e O u t p u t S t r e a m ( s A r c h i v o ) ) ; E s t u d i a n t e l i s t a = ( ( E s t u d i a n t e ) miCurso . d a L i s t a ( ) ) ; i f ( l i s t a != n u l l ) { tamanhos = l i s t a . getTamanhos ( ) ; } // end o f i f ( l i s t a != n u l l ) else { System . o u t . p r i n t l n ( "No hay nadie en la base" + " de datos " ) ; throw new I l l e g a l A r g u m e n t E x c e p t i o n ( " Lista vac a " ) ; } // end o f e l s e a r c h i v o R e g s O u t . w r i t e S h o r t ( tamanhos [ 0 ] ) ; f o r ( i n t i = 1 ; i <= tamanhos [ 0 ] ; i ++) { a r c h i v o R e g s O u t . w r i t e S h o r t ( tamanhos [ i ] ) ; } // end o f f o r ( i n t i = 1 ; . . . // Ahora p r o c e d e m o s a v a c i a r l a b a s e de d a t o s w h i l e ( l i s t a != n u l l ) { f o r ( i n t i = 1 ; i <= tamanhos [ 0 ] ; i ++) { nombre = ( l i s t a . daCampo ( i ) + b l a n c o s ) . s u b s t r i n g ( 0 , tamanhos [ i ] ) ; System . o u t . p r i n t l n ( l i s t a . daCampo ( i )+"\t" +i ) ; a r c h i v o R e g s O u t . w r i t e B y t e s ( nombre ) ; } // end o f f o r ( i n t i = 1 ; i <= tamanhos [ 0 ] ; i ++) l i s t a = l i s t a . daSiguiente (); } // end o f w h i l e ( l i s t a != n u l l ) archivoRegsOut . f l u s h ( ) ; archivoRegsOut . c l o s e ( ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No se qu pas " ) ; e o throw new I O E x c e p t i o n ( "Algo sali mal" ) ; o } // end o f t r y c a t c h r e t u r n GUARDARREGS ;

Como se puede observar, las dos opciones son prcticamente paralelas, excepto a que cuando una escribe la otra lee. Pasemos a revisar primero la opcin de escrio tura, que ser el orden en que programar a amos para probar nuestra aplicacin. o

10.8 Escritura y lectura de campos que no son cadenas

375

10.8.1. Figura 10.13

Escritura en archivos binarios Algoritmo para escribir archivo binario


6 9 9 9Abrir el archivo 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9lista no vac 9 a 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9lista no vac 9 a 9 9 9 9 9 9 9 9 9 9 9 Armar 9 8 5

Pedir nombre de archivo Construirlo


6 9 8 9 7 5

Obtener descriptor de campos

Mensaje de lista vac a Lanzar excepcin de argumento invlido o a


6 9 Escribir nmero de u 9 9 9 9 campos en binario 9 8 6 Escribir tamao 9 n 8 9 9 9 de campo Escribir campo i 9 9 9 9 7 7

archivo binario

9 9 9 descriptor 9 9 9 9 9 de campos 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 Procesar registro 9 9 9 9 9 mientras haya 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 7Cerrar el archivo

Escribir

descriptor[0]

en binario

6 9Escribir nombre ajustado con blancos 9 9 9 9Escribir cuenta ajustada con blancos 9 8 9 9 9Escribir clave ajustada con blancos 9 9 9 7

Escribir carrera ajustada con blancos Pasar al siguiente de la lista

El algoritmo que se us para escribir un archivo binario se encuentra en la gura o 10.13 y corresponde a la discusin de la pgina 367 y mostrado en la gura 10.12 o a en la pgina 368. a En las l neas 440: a 442: del listado 10.6 se implementa la parte correspondiente a abrir el archivo seleccionado mediante el mtodo pideNombreArch. Como este e

376

Entrada y salida

enunciado puede lanzar una excepcin de entrada/salida se enmarca en un bloque o try. Sin embargo es dif que la excepcin sea lanzada y en el caso de que cil o esto suceda quiere decir que hay problemas que no puede resolver el usuario no se conforma un bloque try slo para esta operacin. El constructor de la clase o o DataOutputStream requiere de un ujo de salida (OutputStream) que realice la conexin f o sica. En este caso le pasamos como argumento un objeto de la clase FileOutputStream construido al vuelo, y que se construye utilizando el nombre del archivo deseado. Como FileOutputStream es una subclase de OutputStream no hay ningn problema ni de compilacin ni de ejecucin. u o o En las l neas 443: a 450: se verica que la lista en memoria no est vac De e a. ser as se procede a obtener el descriptor de los campos (el encabezado del archivo binario) en un arreglo de enteros pequeos (short); si la lista est vac se sale del n a a men lanzando una excepcin, que es atrapada desde el mtodo principal (main) u o e de la aplicacin. o Se procede a escribir el encabezado del archivo binario en las l neas 452: a 455: como lo indica el diagrama del algoritmo, utilizando para ello el mtodo writeShort e de la clase DataOutputStream ver documentacin de la clase en las pginas 10.8 o a y 10.8 . Una vez hecho esto se procede a escribir cada uno de los registros de la base de datos, como un arreglo de bytes, con cada campo ocupando el nmero de u bytes que indica el encabezado del archivo en las l neas 459: y 460: se ajusta el campo a su tamao agregando blancos y en la l n nea 463: se escribe utilizando el mtodo writeBytes de la clase DataOutputStream, que convierte una cadena a un e arreglo de bytes . Para escribir toda la lista seguimos nuestro algoritmo usual que recorre listas. Finalmente en las l neas 467: y 468: se procede a cerrar el archivo. En el caso de archivos de disco esto es mucho muy importante, pues si no se hace el archivo no pasa a formar parte del sistema de archivos y por lo tanto no existir ms a a all de la ejecucin de la aplicacin. a o o

10.8.2.

Lectura de archivos binarios

Como ya mencionamos antes, la lectura desde un archivo binario corre prctia camente paralelo a la escritura, pues se tiene que usar la misma mscara para a leer que la que se utiliz para escribir. En la gura 10.14 mostramos en detalle el o algoritmo para esta operacin. o

10.8 Escritura y lectura de campos que no son cadenas

377

Figura 10.14

Algoritmo para leer de archivo binario


6 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9Abrir el archivo 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 Leer de 9Leer descriptor 8 6 9Pedir nombre de archivo 9 9 6 6 9 9 9 9 9 9 8 9 9 9 9 9Se localiz 9 9 o 9 9 8 9 9 9 7 8 Localizarlo y 9 9 9 construirlo 9 9 9 9 5 9 9 9 9 9 9 9 9 9Se localiz Salir con 9 9 o 9 7 7 excepcin o 6 u campos en tamanhos[0] 9Leer nmero de6 9 9 8 Leer tamao 9 n 8 9 de campo Leer de disco tamanhos[i] 9 9 9 7 7

archivo binario

9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9Procesar registro 9 9 9 9 9 mientras haya 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 7

de campos

tamanhos[0]

en binario

6 9Leer a nombre tamanhos[1] bytes 9 9 9 9Leer a cuenta tamanhos[2] bytes 9 9 9 8

Leer a carrera tamanhos[3] bytes 9Leer a clave tamanhos[4] bytes 9 9 9 9Agregar a la lista el registro 9 9 9 7 construido con estos datos

Cerrar el archivo

Comparemos ahora el algoritmo con el cdigo del listado 10.6. La parte que o corresponde a abrir el archivo y localizarlo se encuentra en las l neas 380: a 389:. En esta opcin s procesamos por separado la excepcin que nos pueda dar la o o localizacin del archivo binario del que el usuario solicita leer porque es posible que o no exista dicho archivo. Por eso, en lugar de la tradicional excepcin IOException, o ac tratamos de atrapar una excepcin que nos indica que no se encontr el a o o archivo. Igual que en el caso anterior, sin embargo, salimos con una excepcin del o mtodo, pues tenemos que regresar a solicitar otra opcin. e o De manera similar a como lo hicimos para escribir, construimos un ujo de la

378

Entrada y salida

clase DataInputStream y le pasamos como argumento un objeto de la clase FileInputStream, que es subclase de InputStream, esto ultimo lo que pide el constructor. Una vez abierto el ujo si llegamos a la l nea 390: procedemos a adquirir la descripcin de los campos en el archivo binario. o En la l nea 391: leemos el nmero de campos de cada registro almacenado en u el archivo en los primeros dos bytes del mismo, usando para ello formato de short; y en la l nea 392: construimos un arreglo para almacenar ah los tamaos de cada n campo. En las l neas 393: a 395: leemos con mscara de short cada uno de los a tamaos y los almacenamos en el arreglo de shorts tamanhos. n Una vez que tenemos esto, procedemos a leer del ujo arreglos de k bytes, donde k est dado p la posicin correspondiente de tamanhos. Para poder hacer a e o esto en una iteracin construimos un arreglo de bytes del mximo tamao de los o a n campos esto se obtiene en las l neas 396: a 400: utilizando para ello el mtodo e read(byte[] b, int o, int len) que me permite ir leyendo pedazos de len bytes. Como en el caso de la lectura de cadenas, intentamos primero leer el primer campo, y si esta lectura nos indica que no pudo leer damos por terminado el ujo l nea 401: . En cambio, si pudo leer al primer campo, procedemos a leer tantos campos como nos indique tamanhos[0], en automtico, esto es, sin la garant de que se a a encuentren en el ujo l neas 403: a 417: dejando en manos del mecanismo de excepciones si hay algn problema. Para no tener que llamar a cada campo por u su nombre, simplemente usamos un switch l neas 404: a 417: que convierte al arreglo de bytes en una cadena, que es lo que espera la clase Estudiante. A continuacin, en las l o neas 422: y 423: procedemos a agregar a la base de datos el registro completo recin le e do, construyendo el registro al vuelo. Si hay algn error de entrada/salida, la excepcin correspondiente se atrapa u o y se sale del mtodo, mandando un mensaje alusivo y repitiendo la excepcin. e o En esta opcin presentamos una clusula nally, ya que queremos que, aunque o a haya un error a la mitad, el ujo se cierre para liberar recursos. Esto se hace en las l neas 429: a 435:; sin embargo, tenemos que poner el enunciado que cierra el archivo en un bloque try pues puede lanzar una excepcin. Si sucede esto ultimo, o simplemente absorbemos la excepcin, ya que no importa que haya pasado se va o a salir del mtodo. e

Avance del ujo sin leer


Como en el archivo que estamos construyendo tenemos tamao jo de los n registros, sabemos en qu byte empieza, digamos, el i-simo registro. Todo lo que e e tenemos que hacer es leer el nmero de campos y el tamao de cada campo para u n calcular el tamao del registro, y desde ese punto saltar i 1 registros de tamao n n k, donde k es el tamao de registro calculado. El unico problema que tenemos n

10.8 Escritura y lectura de campos que no son cadenas

379

ac es que el acceso al ujo sigue siendo secuencial, as que los bytes que saltemos a 5 en la lectura ya no los podemos regresar . n el listado 10.7 agregamos una opcin al o men para poder acceder al i-simo registro, siempre y cuando se haga al principio u e del proceso. Esto pudiramos usarlo para descartar un cierto nmero de registros e u y leer unicamente a partir de cierto punto.

Cdigo 10.7 Salto de bytes en lectura secuencial o


481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517:

(MenuListaIOReg) 1/3

case LEEREGISTROK : // P e d i r e l nombre d e l f l u j o try { s A r c h i v o = pideNombreArch ( cons , LEER ) ; a r c h i v o R e g s I n = new D a t a I n p u t S t r e a m ( new F i l e I n p u t S t r e a m ( s A r c h i v o ) ) ; } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { System . e r r . p r i n t l n ( "el archivo de entrada " + ( s A r c h i v o != n u l l ? s A r c h i v o : "nulo" ) + " no existe " ) ; throw e ; } // end o f t r y c a t c h // C a l c u l a r e l tamanho d e l r e g i s t r o i n t tamR = 0 ; try { s h o r t tam = a r c h i v o R e g s I n . r e a d S h o r t ( ) ; tamanhos = new s h o r t [ tam + 1 ] ; tamanhos [ 0 ] = tam ; tamR = 0 ; f o r ( i n t i = 1 ; i <= tam ; i ++) { tamanhos [ i ] = a r c h i v o R e g s I n . r e a d S h o r t ( ) ; tamR += tamanhos [ i ] ; } // end o f f o r ( i n t i = 1 ; i <= tam ; i ++) } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude leer par metros " ) ; a r e t u r n LEEREGISTROK ; } // end o f c a t c h // C a l c u l a r e l n mero t o t a l de r e g i s t r o s en e l f l u j o u int f i l e S i z e = 0; try { f i l e S i z e = a r c h i v o R e g s I n . a v a i l a b l e ( ) / tamR ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( " Error al calcular N m bytes " ) ; u } // end o f t r y c a t c h // P e d i r e l r e g i s t r o s o l i c i t a d o que e s t e en r a n g o s i n t numR = 0 ;

380

Entrada y salida

Cdigo 10.7 Salto de bytes en lectura secuencial o


518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563:

(MenuListaIOReg)2/3

try { System . o u t . p r i n t ( " Ahora dime el numero de registro (0.." + ( f i l e S i z e 1 ) + ") -->" ) ; subcad = cons . r e a d L i n e ( ) ; numR = 0 ; f o r ( i n t i = 0 ; i < s u b c a d . l e n g t h ( ) ; i ++) { numR = numR10 + s u b c a d . c h a r A t ( i ) 0 ; } // end o f f o r ( i n t i = 0 ; i < s u b c a d . l e n g t h ( ) ; i ++) i f ( numR < 0 | | numR >= f i l e S i z e ) { System . o u t . p r i n t l n ( "Hay menos de " + numR + ". Del 0 al " + ( fileSize 1)); r e t u r n LEEREGISTROK ; } } catch ( I O E x c e p t i o n e ) { System . o u t . p r i n t l n ( " Error al leer numero de registro " ) ; } // end o f t r y c a t c h // S a l t a r e l numero de b y t e s r e q u e r i d o s p a r a u b i c a r s e // en e l r e g i s t r o s o l i c i t a d o . i n t a S a l t a r = (numR 1 ) tamR ; i n t pude = 0 ; try { // S a l t a r b y t e s pude = a r c h i v o R e g s I n . s k i p B y t e s ( a S a l t a r ) ; // S i e s que hubo l o s s u f i c i e n t e s i f ( pude >= a S a l t a r ) { bCadena = new byte [ tamR ] ; // Leemos e l r e g i s t r o s o l i c i t a d o . a r c h i v o R e g s I n . r e a d ( bCadena , 0 , tamanhos [ 1 ] ) ; nombre = new S t r i n g ( bCadena , 0 , tamanhos [ 1 ] ) ; f o r ( i n t i = 2 ; i <= tamanhos [ 0 ] ; i ++) { a r c h i v o R e g s I n . r e a d ( bCadena , 0 , tamanhos [ i ] ) ; switch ( i ) { case 2 : c u e n t a = new S t r i n g ( bCadena , 0 , tamanhos [ i ] ) ; break ; case 3 : c a r r e r a = new S t r i n g ( bCadena , 0 , tamanhos [ i ] ) ; break ; case 4 : c l a v e = new S t r i n g ( bCadena , 0 , tamanhos [ i ] ) ; break ; default : break ; } // end o f s w i t c h ( i ) } // end o f f o r ( i n t i = 1 ; i <= tamanhos [ 0 ] ; i ++)

10.8 Escritura y lectura de campos que no son cadenas

381 (MenuListaIOReg) 3/3

Cdigo 10.7 Salto de bytes en lectura secuencial o


554: 555: 556: 557: 558: 559: 560: 561: 562:

// Se arma un o b j e t o de l a c l a s e E s t u d i a n t e E s t u d i a n t e nuevo = new E s t u d i a n t e ( nombre , c u e n t a , carrera , clave ); System . o u t . p r i n t l n ( nuevo . d a R e g i s t r o ( ) ) ; } // end o f i f ( pude >= a S a l t a r ) } catch ( I O E x c e p t i o n e ) { System . o u t . p r i n t l n ( "Hubo error " ) ; } // end o f t r y c a t c h r e t u r n LEEREGISTROK ;

10.8.3.

Acceso semi directo a archivos binarios


Un archivo binario tiene un tamao jo de registros; en muchas ocasiones los n registros, o la informacin propiamente dicha, viene precedida de un descriptor o de los registros lo que antes llamamos el encabezado del archivo pero esta informacin aparece una unica vez y siempre al principio del archivo. Los ujos o que hemos visto hasta ahora, aun los binarios, son secuenciales, esto es, para leer el registro i hay que leer previamente los i 1 registros que lo preceden, o en todo caso saltar los bytes correspondientes a i 1 registros. Debemos notar que incluso con los saltos (skip), stos son siempre hacia el nal del archivo: lo que e no podemos hacer es querer leer el registro i y posteriormente el registro j con j i.6 Sin embargo, es muy comn querer un acceso directo7 . Por ejemplo, supongau mos que tenemos una base de datos ordenada alfabticamente y queremos listar e en orden inverso. Con los ujos esto equivaldr a cerrar y abrir el archivo por a cada registro. Lo ideal es que sin costo adicional, pudiramos recorrer el archivo e de atrs hacia adelante. Tambin se presenta esta necesidad en las bases de daa e tos, donde se guarda en disco una tabla con alguna llave (key) que identica a un registro, y a continuacin la posicin de ese registro en el archivo. Se podr o o a hacer una bsqueda inteligente sobre la tabla (por ejemplo, bsqueda binaria) que u u requiere la posibilidad de ir hacia adelante o hacia atrs en la lectura del archivo. a

Para poder hacer esto en archivos secuenciales tenemos que cerrar y volver a abrir el archivo para que se vuelva a colocar al principio del mismo y poder saltar hacia el nal del archivo. Otra opcin es que el archivo tenga implementado los mtodos mark y reset. o e

382

Entrada y salida

En Java tenemos una clase que nos da esta facilidad y que es la clase RandomAccessFile. Hay que tener presente que aunque esta clase maneja archivos en disco no hereda de ningn ujo (stream) o de lector/escritor (Reader/Writer), sino u que hereda directamente de Object, aunque s se encuentra en el paquete java.io. Los ejemplares de esta clase proveen tanto lectura como escritura en el mismo objeto. en general podemos pensar en un archivo de acceso directo como un arreglo en disco, donde cada elemento del arreglo es un registro y al cual queremos tener acceso directamente a cualquiera de los registros sin seguir un orden predeterminado. Con un archivo de este tipo tenemos siempre asociado un apuntador de archivo (en adelante simplemente apuntador), que se encarga de indicar en cada momento a partir de donde se va a realizar la siguiente lectura/escritura. Si el apuntador est al nal del archivo y viene una orden de escritura, el archivo simplemente a se extiende (el arreglo crece); si estando al nal del archivo viene una orden de lectura, la mquina virtual lanzar una excepcin EOFException. Si se intenta a a o realizar alguna operacin despus de que el archivo fue cerrado, se lanzar una o e a IOException. Se tiene un mtodo que se encarga de mover al apuntador, lo que e consigue que la siguen te lectura/escritura se lleve a cabo a partir de la posicin a o la que se movi el apuntador. Estas posiciones son absolutas en trminos de bytes. o e Este tipo de archivos se pueden usar para lectura, escritura o lectura/escritura, dependiendo de qu se indique al construir los ejemplares. e Si bien no hereda de ninguna de las clases Stream, como ya dijimos, implementa a las interfaces DataOutput, DataInput y Closeable. Como se pueden imaginar, las dos primeras tambin son implementadas por DataOutputStream y DataInputSe tream, por lo que tendremos prcticamente los mismos mtodos que ya conocemos a e de estas dos ultimas clases, todos en una unica clase. Revisaremos unicamente aquellos mtodos que no conocemos todav dando por sentado que contamos e a, con los mtodos para leer y escribir de DataInputStream y DataOutputStream. e

public class RandomAccessFile implements DataOutput, DataInput, Closeable


Constructores:

public RandomAccesFile(File le , String mode) throws FileNotFoundException


Llamamos directo a lo que en ingls se conoce tambin como random aleatorio , porque e e consideramos que aqul es un trmino ms adecuado. Aleatorio tiene un sentido de no determinise e a mo y lo que se hace con este tipo de acceso es poder llegar directamente (no secuencialmente) a un cierto registro.
7

10.8 Escritura y lectura de campos que no son cadenas

383 (contina. . . ) u

class RandomAccessFile

Nuevamente tenemos que File representa a un archivo f sico, ya sea que ya ha sido construido a travs de un ujo o que se le d la identicacin e e o completa de un archivo en disco. La cadena mode representa el tipo de uso que se le va a dar al archivo y puede ser: r rw rws Abre el archivo slo para lectura. Si con un archivo abierto en o este modo se intenta escribir, se lanzar una IOException1. a El archivo se abre para lectura/escritura. Si el archivo no existe, se intenta crear nuevo. Igual que rw, excepto que exige que todo cambio al archivo o al descriptor (meta-datos) del archivo se reeje inmediatamente en el dispositivo, que se haga sincronizado con el enunciado.

rwd Igual que rws, excepto que los meta-datos no tienen que actualizarse de manera sincronizada. Estos dos ultimos modos son muy utiles para garantizar que si hay algn u problema con el sistema, todo lo que se escribi desde la aplicacin en o o efecto se vea reejado en el dispositivo local. Si el dispositivo no es local, no hay garant de que esto suceda. a Las excepciones que pueden ser lanzadas (aunque no es necesario anunciarlas a todas) son: IllegalArgumentException Si el modo del constructor no es uno de los especicados. FileNotFoundException Si no se encontr el archivo solicitado y se ino tent abrir slo para lectura. o o SecurityException Si se intent abrir un archivo para lectura o para leco tura/escritura que el manejador de la seguridad no permita leer, o para lectura/escritura que el manejador de seguridad no permita escribir.

public RandomAccesFile(String name, String mode) throws FileNotFoundException


En este caso la primera cadena da una identicacin en disco del vuelve o a ser el modo en que vamos a usar el archivo. Se comporta exactamente igual que el primer constructor que mostramos. Mtodos adicionales a los de DataInput y DataOutput e

384

Entrada y salida

class RandomAccessFile

(contina. . . ) u

En el caso de los archivos de acceso directo siempre existe el problema de que se trate de leer ms all del n de archivo. Por ello, prcticamente a a a todos los mtodos que intentan leer ms de un carcter incondicionalmente e a a llegando al nal del archivo sin haber le todos los bytes y que son do redenidos lanzan la excepcin EOFException. o Como sta es una subclase de IOException no hay necesidad de modicar el e encabezado que por otro lado no se puede para que avise que tambin e puede lanzar una EOFException, que se reere a tratar de leer ms all del a a n de archivo.

public long length ()


Regresa el tamao del archivo en bytes. n

throws IOException throws IOException

void setLength(long newLength)

Establece un nuevo tamao para el archivo. Si el tamao anterior era n n mayor, se trunca el archivo al nuevo tamao, perdindose lo que hab n e a ms all del nuevo tamao. Si el tamao anterior era menos, se reserva a a n n espacio para agrandar el archivo; sin embargo hay que tener en cuenta que el contenido del espacio agregado no estar denido. En el caso de que el a archivo sea truncado y que el lePointer est ms all del nuevo nal del e a a archivo, el apuntador se colocar al nal del archivo. a

public void seek(long pos)

throws IOException

Coloca el apuntador del archivo (lePointer) en la posicin dada por pos, o contado en bytes a partir del principio del archivo. Esta posicin puede o estar ms all del n de archivo sin que se lance una excepcin de n de a a o archivo (EOFException), pero sin que cambie tampoco el tamao del arn chivo. Sin embargo, si una vez colocado el apuntador ms all del nal, se a a lleva a cabo una escritura, esta accin si modicar el tamao del archivo. o a n La excepcin se lanza si pos 0 u ocurre un error de entrada/salida. o

public long getFilePointer ()

throws IOException

Regresa la distancia al principio del archivo, en bytes, de la posicin actual o del archivo (el prximo byte que va a ser le o escrito). o do

Con esto ya tenemos las herramientas necesarias para acceder al disco con acceso directo.

10.8 Escritura y lectura de campos que no son cadenas

385

10.8.4.

Lectura directa de registros


Como ya mencionamos, cualquier archivo en disco (o algn otro dispositivo de u almacenamiento en bytes) puede ser le o escrito bajo cualquier tipo de ujo; do el tipo de ujo nos indica unicamente cmo interpretar los bytes: no describe el o contenido del archivo.

Figura 10.15

Algoritmo para agregar registros desde archivo de acceso directo


6 9Pedir nombre de archivo 9 9 9 9Abrir archivo para lectura 9 9 5 9 9 9Leer nmero de u tamanhos[0] Primer short 9 9 9 9 9 campos del archivo 9 9 9 9 9 9 9 6 6 9 9 9 9Leer siguiente 9 9 8 8 9Calcular tamao Sumar tamanhos[i] n 9 9 9 short 9 9 de registro 9 i=1,. . . , tamanhos[0] 9 9 7 7 9 9 Sumarlo 9 9 9 9 9 9 6 9 6 9 9 9 9 9Obtener del usuario el 9Leer cadena de 9 9 9 9 9 9 9 8 9 9 consola 9 9 9 nmero del registro 9 9 u 9 9 9 9 9 9 Procesar d gito 9 9 9 9 9 9 solicitado 9 9 7 9 9 9 9 Agregar registros 8 mientras haya 9 9 9 9 9 desde archivo de 9 6 9 9 9 9 9 9 acceso directo 9 9pos 9 8 9Calcular posicin en 9 9 9 o 9 9 9 9 tamR numR 9 9 9 9 9 9 9 bytes 7 9 9 9 9 encabezado 9 9 9 9 9 8 9 Procesar registro 9Colocar el apuntador en esa posicin 9 o 9 9 9 9(mientras se den) 9 9 9 9 9 9 6 9 9 9 9 9 9 9Leer nombre 9 9 9 9 9 9 9 9 8 9Leer registro desde 9 9 Leer cuenta 9 9 9 9 9 9 9 9 9 9 disco 9 9 9Leer carrera 9 9 9 9 9 7 9 9 9 9 Leer clave 9 9 9 9 9 9 9 9 9 9 9 9 9 9 6 9 9 9 9 9 9 9Construir registro 9 9 8 9Agregar a lista 9 9 9 9 9 9 9 nuevo 9 9 9 registro le 9 9 do 7 7 7

Agregar a lista

386

Entrada y salida

El unico problema que enfrentamos es si suponemos un ujo en el que queramos leer cadenas y no tenemos carcteres de n de l a nea en el archivo; en este caso una unica lectura nos dar todo el contenido del archivo, terminando la cadena a con un n de archivo. En este sentido, s hay que distinguir (desde la aplicacin) o cuando estamos trabajando con un archivo de cadenas o de registros de tamao n jo. La lectura directa nos permite brincar dentro del archivo, hacia adelante y hacia atrs, buscando un byte en una posicin determinada. Lo que quisiramos a o e hacer con nuestra base de datos, guardada en un archivo en disco, es poder leer y agregar en desorden a los registros que se encuentran en el disco. Tambin debe e ser posible agregar unicamente a un subconjunto de estos registros. El algoritmo para este caso se encuentra en la gura 10.15 en la pgina anterior. a Podemos revisar cada uno de estos subprocesos en trminos del diagrama. Por e ejemplo, pedir el nombre del archivo consiste de exactamente el mismo proceso que en el caso del acceso al k-simo registro. La implementacin se puede ver en e o el listado 10.8. La parte correspondiente a la lectura y apertura del archivo tienen que estar en un bloque try, ya que ambas operaciones pueden lanzar excepciones l neas 605: a 612:. Lo primero que hacemos es solicitar del usuario el nombre del archivo en disco que vamos a utilizar l nea 606: y a continuacin lo tratamos o de abrir exclusivamente para lectura, lo que que hacemos poniendo como segundo argumento del constructor "r" en la l nea 607:. Si se lanza la excepcin de archivo no encontrado, simplemente emitimos un o mensaje acorde y salimos a mostrar nuevamente el men l u neas 608: a 612: , mientras que si lo que tenemos es un error de entrada/salida realmente ya no podemos hacer nada y exportamos la excepcin l o neas 613: y 614:. Lo siguiente que queremos hacer es leer el encabezado del archivo para calcular el tamao del registro. Tambin esto se lleva a cabo de la misma manera que lo n e hicimos en la lectura de archivos binarios, por lo que ya no es necesaria una explicacin. El cdigo se encuentra en el listado 10.9. o o

Cdigo 10.8 Lectura del nombre del archivo y apertura del mismo o
603: 604: 605: 606: 607: 608:

(case LEERDIRECTO) 1/2

case LEERDIRECTO : // P e d i r e l nombre d e l f l u j o try { s A r c h i v o = pideNombreArch ( cons , o p c i o n ) ; archivoRndm = new R a n d o m A c c e s s F i l e ( s A r c h i v o , "r" ) ; } catch ( F i l e N o t F o u n d E x c e p t i o n e ) {

10.8 Escritura y lectura de campos que no son cadenas

387 (case LEERDIRECTO) 2/2

Cdigo 10.8 Lectura del nombre del archivo y apertura del mismo o
609: 610: 611: 612: 613: 614: 615:

System . e r r . p r i n t l n ( "el archivo de entrada " + ( s A r c h i v o != n u l l ? s A r c h i v o : "nulo" ) + " no existe " ) ; r e t u r n LEERDIRECTO ; } catch ( I O E x c e p t i o n e ) { throw e ; } // end o f t r y c a t c h

Cdigo 10.9 Clculo del tamao del registro o a n


616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641:

(case LEERDIRECTO)

// C a l c u l a r e l tama o d e l r e g i s t r o n tamR = 0 ; numBytes = 0 ; try { s h o r t tam = 0 ; tam = archivoRndm . r e a d S h o r t ( ) ; numBytes = 2 ; tamanhos = new s h o r t [ tam + 1 ] ; tamanhos [ 0 ] = tam ; tamR = 0 ; f o r ( i n t i = 1 ; i <= tam ; i ++) { tamanhos [ i ] = archivoRndm . r e a d S h o r t ( ) ; numBytes+=2; tamR += tamanhos [ i ] ; } // end o f f o r ( i n t i = 1 ; i <= tam ; i ++) } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude leer par metros " ) ; a return opcion ; } // end o f c a t c h // C a l c u l a r e l n mero t o t a l de r e g i s t r o s en e l f l u j o u // p a r a d e t e c t a r p o s i c i o n e s e r r n e a s de r e g i s t r o s o f i l e S i z e = 0; try { f i l e S i z e = archivoRndm . l e n g t h ( ) / tamR ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( " Error al calcular N m bytes " ) ; u } // end o f t r y c a t c h

Una vez que calculamos el tamao del encabezado (numBytes), el tamao del n n registro (tamR) y el nmero de registros lgicos en el archivo (leSize) procedemos u o a iterar, pidindole al usuario el nmero de registro, tantos como desee, del registro e u que desea leer y agregar a la lista en memoria. Esto se lleva a cabo en las l neas 642: a 667: en el listado 10.10 en la siguiente pgina. a

388

Entrada y salida

Cdigo 10.10 Peticin del nmero de registro al usuario o o u


642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667:

(case LEERDIRECTO)

w h i l e (numR >= 0 ) { // P e d i r e l r e g i s t r o s o l i c i t a d o que e s t e en r a n g o s numR = 0 ; try { System . o u t . p r i n t ( " Ahora dime el n mero de registro " u + "(0.." + ( f i l e S i z e 1 ) + ", -1 para terminar )" + " a agregar -->" ) ; subcad = cons . r e a d L i n e ( ) ; i f ( s u b c a d . c h a r A t ( 0 ) < 0 | | s u b c a d . c h a r A t ( 0 ) >= 9 ) { numR = 1; continue ; } // end o f i f numR = 0 ; f o r ( i n t i = 0 ; i < s u b c a d . l e n g t h ( ) ; i ++) { numR = numR10 + s u b c a d . c h a r A t ( i ) 0 ; } // end o f f o r i f ( numR < 0 | | numR >= f i l e S i z e ) { System . o u t . p r i n t l n ( "Hay menos de " + numR + ". Del 0 al " + ( fileSize 1)); return opcion ; } } catch ( I O E x c e p t i o n e ) { System . o u t . p r i n t l n ( " Error al leer n mero de registro " ) ; u } // end o f t r y c a t c h

Elegimos leer del usuario una cadena, para evitar errores de lectura en caso de que el usuario no proporcione un entero l nea 650:. Calculamos el entero correspondiente usando la regla de Horner, que proporciona una manera sencilla, de izquierda a derecha, de calcular un polinomio. Supongamos que tenemos un polinomio cn xn cn1 xn1 . . . c0 Podemos pensar en un nmero en base b como un polinomio donde x  b y u tenemos la restriccin de que 0 ci b. En este caso para saber el valor en base o 10 evaluamos el polinomio. La manera fcil y costosa de hacerlo es calculando a cada una de las potencias de b para proceder despus a multiplicar ci por bi . e P pxq

n i 0

c i xi

Pero como acabamos de mencionar, esta es una manera costosa y poco elegante

10.8 Escritura y lectura de campos que no son cadenas

389

de hacerlo. La regla de Horner nos dice: P pxq

 c0 xpc1 xpc2 xp. . .qq . . .q  8000 700 20 5  8725

lo que nos permite evaluar el polinomio sin calcular previamente las potencias de b. Por ejemplo, el nmero base 10 8725 lo podemos expresar como el polinomio u 8 103 7 102 2 101 5 100

Si usamos la regla de Horner lo calculamos de la siguiente manera: 5 10p2 10p7 10p8qqq Pero como tenemos que calcular de adentro hacia afuera, el orden de las operaciones es el siguiente:

ppp8 10q 7q 10 2q 10 5
que resulta en la siguiente sucesin de operaciones: o 8 10 80 7 87 10 870 2 872 10 8720 5  8725 lo que permite leer los d gitos de izquierda a derecha e ir realizando las multiplicaciones y sumas necesarias. La ventaja de esta regla es que cuando leemos el 8, por ejemplo, no tenemos que saber la posicin que ocupa, sino simplemente que o es el que est ms a la izquierda. Dependiendo de cuntos d a a a gitos se encuentren a su derecha va a ser el nmero de veces que multipliquemos por 10, y por lo tanto u la potencia de 10 que le corresponde. Este algoritmo se encuentra codicado en las l neas 655: a 658:. En las l neas 651: a 654: vericamos que el usuario no est proporcionando un e nmero negativo (que empieza con -). Si es as damos por terminada la sucesin u , o de enteros para elegir registros. Quisiramos insistir en que no importa si el ujo es secuencial o de acceso e directo, una lectura se hace siempre a partir de la posicin en la que se encuentra o el ujo. Si se acaba de abrir esta posicin es la primera la cero (0) . Conforme se o hacen lecturas o escrituras el ujo o archivo va avanzando; en los ujos secuenciales de entrada, mediante el mtodo skip se puede avanzar sin usar los bytes saltados, e pero siempre hacia adelante. En cambio, en los archivos de acceso directo se cuenta

390

Entrada y salida

con el comando seek que es capaz de ubicar la siguiente lectura o escritura a partir de la posicin dada como argumento, colocando el apuntador de archivo en esa o posicin. o En el listado 10.11 se encuentra el cdigo que corresponde a la ubicacin del o o apuntador del archivo, frente al primer byte del registro solicitado por el usuario en la iteracin actual. o

Cdigo 10.11 Posicionamiento del apuntador del archivo y lectura o


687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718: 719: 720: 721: 722: 723: 724:

(case LEERDIRECTO)

try { // S a l t a r e l numero de b y t e s r e q u e r i d o s p a r a u b i c a r s e // en e l r e g i s t r o s o l i c i t a d o . a S a l t a r = numRtamR + numBytes ; archivoRndm . s e e k ( a S a l t a r ) ; bCadena = new byte [ tamR ] ; // Leemos e l r e g i s t r o s o l i c i t a d o . archivoRndm . r e a d ( bCadena , 0 , tamanhos [ 1 ] ) ; nombre = new S t r i n g ( bCadena , 0 , tamanhos [ 1 ] ) ; f o r ( i n t i = 2 ; i <= tamanhos [ 0 ] ; i ++) { archivoRndm . r e a d ( bCadena , 0 , tamanhos [ i ] ) ; switch ( i ) { case 2 : c u e n t a = new S t r i n g ( bCadena , 0 , tamanhos [ i ] ) ; break ; case 3 : c a r r e r a = new S t r i n g ( bCadena , 0 , tamanhos [ i ] ) ; break ; case 4 : c l a v e = new S t r i n g ( bCadena , 0 , tamanhos [ i ] ) ; break ; default : break ; } // end o f s w i t c h ( i ) } // end o f f o r ( i n t i = 1 ; // Se arma un o b j e t o de l a c l a s e E s t u d i a n t e E s t u d i a n t e nuevo = new E s t u d i a n t e ( nombre , c u e n t a , c a r r e r a , c l a v e ) ; i f ( miCurso == n u l l ) { System . o u t . p r i n t l n ( "No existe Curso " ) ; throw new N u l l P o i n t e r E x c e p t i o n ( " Uuups " ) ; } // end o f i f ( miCurso == n u l l ) miCurso . a g r e g a E s t F i n a l ( new E s t u d i a n t e ( nombre , c u e n t a , c a r r e r a , c l a v e ) ) ; } catch ( I O E x c e p t i o n e ) { System . o u t . p r i n t l n ( "Hubo error en LEERDIRECTO " ) ; } // end o f t r y c a t c h } // end w h i l e s e p i d a n r e g i s t r o s

10.8 Escritura y lectura de campos que no son cadenas

391

En la l nea 690: calculamos la posicin que corresponde al entero dado en esta o iteracin y en la l o nea 691: colocamos al apuntador del archivo frente al siguiente registro a leer. Como los registros estn numerados del 0 en adelante, el nmero a u de registros que se tienen que saltar son precisamente el entero proporcionado por el usuario. Una vez colocados frente al registro deseado procedemos a leer el registro desde disco l neas 692: a 711: . Una vez le el registro procedemos a do agregarlo a la lista que se est construyendo l a neas 713: a 720: . En este bloque hay varias operaciones que pueden lanzar una excepcin, por lo que se colocan en o un bloque try. En caso de que haya algn problema, simplemente descarta esta u escritura y procede a pedir el siguiente nmero de registro. u

10.8.5.

Escritura directa de registros


En un archivo de acceso directo podemos tener varias situaciones: 1. Queremos guardar en un archivo de acceso directo a los registros de la lista en memoria. 2. Queremos agregar registros al nal del archivo. 3. Deseamos sustituir un registro en el archivo por otro proporcionado por el usuario. Las primeras dos situaciones no guardan ninguna diferencia con la de estar trabajando con archivos binarios; insistimos en que las opciones que vamos a tener con un archivo en disco depende del color del cristal con el que lo veamos. En general, si tenemos el encabezado del archivo y registros de tamao uniforme, n podemos verlo como un archivo binario o como uno de acceso directo. Por esta razn nos concentraremos en la tercera situacin, aclarando que la posicin que se o o o d para escribir el registro puede estar ms all del n de archivo, lo que resultar, e a a a en este tipo de archivos, en que se extienda el archivo, dejando como indenidos los bytes que se encuentren entre el nal anterior y el actual. El algoritmo para esta opcin se encuentra en la gura 10.16. o Por supuesto que en otras aplicaciones puede ser la misma aplicacin la que o modique un registro y lo reescriba. En el contexto de nuestra aplicacin pensao remos que el usuario nos da la posicin del registro y el registro a escribir en esa o posicin esto para poder seguir trabajando en este contexto. El algoritmo para o esta opcin corre paralelo al que dimos para lectura de archivo de acceso directo, o excepto que adems de pedir nmero de registro, la aplicacin tiene que pedir el a u o registro a escribir. Por lo anterior, esta opcin es una combinacin de la opcin o o o que agrega un registro y de la que lee de un archivo de acceso directo. El cdigo o correspondiente se encuentra en al listado 10.12.

392

Entrada y salida

Figura 10.16

Algoritmo para sobrescribir registros en archivo de acceso directo

6 9Pedir nombre de archivo 9 9 9 9Abrir archivo para lectura y escritura 9 9 5 9 9 9Leer nmero 9 u tamanhos[0] Primer short 9 9 9 9 de campos 9 del archivo 9 9 9 9 9 9 6 9 6 9 9 9 9 8 9 9 Sumar tamanhos[i] 9Leer siguiente 9 9 9 9Calcular tamao 8 n short 9 9 9 i=1,. . . , tamanhos[0] 9 7 9 9 de registro 9 Sumarlo 9 9 9 9 9 7 9 9 9 6 9 6 9 9 9 9 9Leer de consola 9 9 9 9 9 9 9 9 Obtener la posicin 9 9 9 9 o 8 cadena 9 9 9 9 9 9 9del registro a escribir 9 Procesar d 9 9 9 gito 9 9 9 9 9 9 7 9 9 9 9 mientras haya 9 9 9 9 9 Modicar registros 9 9 8 9 9 9 en archivo de 6 9 9 9 9 9Pedir nombre 9 9 acceso directo 9 9 9 9 9 8 9Obtener del usuario 9Pedir cuenta 9 9 9 9 9 9 9 9 9 9el registro a escribir 9Pedir carrera 9 9 9 9 9 9 9 9 9 7 9 9 9 9 9 9 Pedir clave 9 Procesar registro 8 9 9 9 9 9(mientras se den) 9 6 9 9 9 9 9 9pos 9 9 8 9Calcular posicin 9 9 9 o 9 9 9 9 tamR numR 9 9 9 9 9 9 en bytes 9 7 9 9 9 9 encabezado 9 9 9 9 9 9 9Colocar el apuntador 9 9 9 9 9 9 9 9 9 9 9 9 9 en esa posicin o 9 9 9 9 6 9 9 9 9 9 9 9Escribir nombre 9 9 9 9 9 9 9 9 8 9Escribir campos 9 Escribir cuenta 9 9 9 9 9 9 9 9 9 9 9Escribir carrera en el disco 9 9 9 9 9 9 9 9 7 7 7 Escribir clave

10.8 Escritura y lectura de campos que no son cadenas

393 (case GUARDARDIRECTO)1/2

Cdigo 10.12 Opcin de modicar registros o o


742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781: 782: 783: 784: 785:

case GUARDARDIRECTO : E s t u d i a n t e nuevo = n u l l ; // P e d i r e l nombre d e l f l u j o try { s A r c h i v o = pideNombreArch ( cons , o p c i o n ) ; archivoRndm = new R a n d o m A c c e s s F i l e ( s A r c h i v o , "rw" ) ; } catch ( F i l e N o t F o u n d E x c e p t i o n e ) { System . e r r . p r i n t l n ( "el archivo de entrada " + ( s A r c h i v o != n u l l ? s A r c h i v o : "nulo" ) + " no existe " ) ; return opcion ; } catch ( I O E x c e p t i o n e ) { throw e ; } // end o f t r y c a t c h // C a l c u l a r e l tama o d e l r e g i s t r o n tamR = 0 ; numBytes = 0 ; try { s h o r t tam = 0 ; tam = archivoRndm . r e a d S h o r t ( ) ; numBytes = 2 ; tamanhos = new s h o r t [ tam + 1 ] ; tamanhos [ 0 ] = tam ; tamR = 0 ; f o r ( i n t i = 1 ; i <= tam ; i ++) { tamanhos [ i ] = archivoRndm . r e a d S h o r t ( ) ; numBytes+=2; tamR += tamanhos [ i ] ; } // end o f f o r ( i n t i = 1 ; i <= tam ; i ++) } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude leer par metros " ) ; a return opcion ; } // end o f c a t c h // C a l c u l a r e l n mero t o t a l de r e g i s t r o s en e l f l u j o u f i l e S i z e = 0; try { f i l e S i z e = archivoRndm . l e n g t h ( ) / tamR ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( " Error al calcular Num bytes " ) ; } // end o f t r y c a t c h ( i n t i = 1 ; i <= tamanhos [ 0 ] ; i ++) w h i l e (numR >= 0 ) { // P e d i r e l r e g i s t r o s o l i c i t a d o que e s t e en r a n g o s try {

394

Entrada y salida

Cdigo 10.12 Opcin de modicar registros o o


786: 787: 788: 789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: 827: 828: 829: 830: 831: 832:

(case GUARDARDISCO)2/2

System . o u t . p r i n t ( " Ahora dime el numero de registro (0.." + ", -1 para terminar )" + " a escribir -->" ) ; subcad = cons . r e a d L i n e ( ) ; char c u a l = s u b c a d . c h a r A t ( 0 ) ; i f ( c u a l < 0 | | c u a l >= 9 ) { numR = 1; continue ; } // end o f i f numR = 0 ; f o r ( i n t i = 0 ; i < s u b c a d . l e n g t h ( ) ; i ++) numR = numR10 + s u b c a d . c h a r A t ( i ) 0 ; } catch ( I O E x c e p t i o n e ) { System . o u t . p r i n t l n ( " Error al leer n mero de registro " ) ; u } // end o f t r y c a t c h // P e d i r l o s campos a e s c r i b i r try { nombre = pideNombre ( c o n s ) ; cuenta = pideCuenta ( cons ) ; c a r r e r a = p i d e C a r r e r a ( cons ) ; c l a v e = pideClave ( cons ) ; nuevo = new E s t u d i a n t e ( nombre , c u e n t a , c a r r e r a , c l a v e ) ; } catch ( I O E x c e p t i o n e ) { System . o u t . p r i n t l n ( " Error al leer datos del " + " estudiante .\ nNo se pudo escribir " ) ; } // end o f t r y c a t c h try { // S a l t a r e l numero de b y t e s r e q u e r i d o s p a r a u b i c a r s e // en e l r e g i s t r o s o l i c i t a d o . a S a l t a r = numRtamR + numBytes ; archivoRndm . s e e k ( a S a l t a r ) ; // E s c r i b o e l r e g i s t r o c o n s t r u i d o en nuevo f o r ( i n t i = 1 ; i <= tamanhos [ 0 ] ; i ++) { nombre = ( nuevo . daCampo ( i ) + b l a n c o s ) . s u b s t r i n g ( 0 , tamanhos [ i ] ) ; archivoRndm . w r i t e B y t e s ( nombre ) ; } // end o f f o r ( i n t i = 1 ; i <= tamanhos [ 0 ] ; i ++) } catch ( I O E x c e p t i o n e ) { System . o u t . p r i n t l n ( "Hubo error en GUARDARDIRECTO " ) ; } // end o f t r y c a t c h } // end w h i l e s e p i d a n r e g i s t r o s try { archivoRndm . c l o s e ( ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude cerrar archivo directo " ) ; } // end o f t r y c a t c h r e t u r n GUARDARDIRECTO ;

10.9 Lectura y escritura de objetos

395

Realmente ya no hay mucho que explicar en esta opcin, pues prcticamente o a todo lo interesante ya se vio y los comentarios del cdigo explican el orden. Sin o embargo es necesario insistir en un punto. Supongamos que leemos un registro (en modo directo), lo modicamos y lo reescribimos; esto quiere decir que leemos y escribimos de la misma posicin. Por lo tanto, tendremos que colocar el apuntador o del archivo en la posicin correspondiente; leemos el registro; lo modicamos; o volvemos a colocar el apuntador de archivo en la posicin anterior; y escribimos. o Si no regresamos al apuntador a la posicin donde empieza el registro, vamos o a sobreescribir en el siguiente registro, no en el que le mos. Esto se debe a que cada lectura (o escritura) avanza al apuntador tantos bytes como se hayan le do (o escrito); al nal de la lectura el apuntador va a estar posicionado un byte ms a all del nal del registro le a do, por lo que es necesario regresarlo a que apunte al principio del registro le do.

10.9 Lectura y escritura de objetos


Siendo Java un lenguaje orientado a objetos, uno se hace la pregunta de por qu se usan mecanismos anticuados como la transformacin de y a bytes e o para lectura y escritura del ingrediente fundamental del lenguaje. Una respuesta, aunque no del todo precisa, es que el almacenamiento en dispositivos externos sigue siendo todav en trminos de bytes. Pero no es del todo precisa esta respuesta a e pues lo que intentamos con el uso de un lenguaje orientado a objetos es elevar el nivel de abstraccin de nuestras aplicaciones; esto es, independientemente de o cul sea la representacin subyacente de la informacin el lenguaje de mquina a o o a que corresponde as la representacin de la informacin deseamos, al nivel de la o o aplicacin, pensar en que tenemos almacenados objetos, independientemente de o cmo estn codicados estos objetos. o e La caracter stica que buscamos escribiendo y leyendo objetos es el autoconocimiento que tienen de s mismos los objetos: buscamos que tanto en el dispositivo externo como en la memoria durante la ejecucin el objeto pueda describirse a o s mismo; que la lectura, escritura, y por lo tanto la interpretacin, no dependa de o la aplicacin, sino que dependa de lo que est almacenado (o se est almacenano a e do). En resumen, deseamos que los objetos se almacenen con una descripcin de o s mismos; y que esta descripcin sea manejada automticamente por el lenguaje, o a descargando de esta tarea a la aplicacin. o Para ello cuenta Java con dos ujos, uno de entrada y otro de salida, que contempla la lectura y escritura de objetos, ObjectInputStream y ObjectOutputS-

396

Entrada y salida

tream respectivamente. Las lecturas y escrituras se llevan a cabo como acabamos de mencionar, por lo que los archivos presentan un tamao mayor al que espen rar amos. Tambin hay que tomar en cuenta que este modo de entrada/salida no e es muy eciente, sobre todo si los objetos son grandes o tenemos una cantidad grande de los mismos. En este ultimo caso se recomienda vaciar el objeto como lo hicimos antes y dejar que sea la aplicacin la que lo interprete. Se conoce como o serializacin a la conversin que se realiza de un objeto a un ujo de bytes y o o deserializacin la accin inversa: construir el estado de un objeto a partir de su o o imagen en bytes. Un aspecto muy importante e interesante es que al serializar un objeto estamos guardando su estado, que es lo que lo distingue de otros objetos de la misma clase. Al deserializarlo recuperamos precisamente el estado del objeto en el momento en que fue serializado. De esta manera se puede recuperar fcilmente el estado de a una aplicacin utilizando estos mecanismos. El contexto principal en el que estos o mecanismos se requieren es cuando se ejecuta una aplicacin distribuida en varias o plataformas o de manera remota. Regresando a los mecanismos de Java para leer/escribir objetos, llama la atencin que ObjectInputStream y ObjectOutputStream no heredan de FilexxxputStream o sino que heredan directamente de xxxputStream, donde xxx corresponde a in o out. Pero veamos parte de la denicin de estas clases y los mtodos que nos o e aportan. No es trivial guardar el estado de un objeto, pues algunos de los campos pueden depender de la mquina virtual en la que se est ejecutando o del contexto. Por a a ejemplo, si tenemos una lista ligada con objetos, la referencia al siguiente elemento de la lista depende de la secuencia de ejecucin y de la localidad asignada por o la mquina virtual: no es probable que en otra ejecucin, posiblemente hasta a o en otra mquina o con distinto sistema operativo, la lista quede armada en las a mismas posiciones que las dadas por el estado de los objetos. Otro ejemplo es el de un objeto que guarda la posicin de un cierto ujo donde se encuentra el o apuntador del archivo en el momento en que qued denido su estado: en una o ejecucin posterior o distinta puede suceder que el archivo ya haya cambiado y o esa posicin no corresponda; o que al momento de leer el objeto el archivo ni o siquiera est disponible. Por lo tanto, hay campos en un objeto serializado que e no tiene sentido guardar o leer porque se encuentran fuera de contexto. A este tipo de campos los marcamos con el calicativo de transient para indicarle a la aplicacin que no los incluya en la serializacin del objeto. o o Otros campos que no vale la pena tampoco guardar son los campos estticos a (static) de la clase. Tambin en este caso, dado que esos campos pueden ser moe dicados por cualquiera de los objetos de esa clase, el valor que tienen cuando se serializa al objeto no tiene por que ser el mismo que cuando se le deserializa. Por

10.9 Lectura y escritura de objetos

397

lo tanto, tampoco los campos estticos van a ser incluidos en la serializacin de a o un objeto. Si tenemos una clase que contiene campos que corresponden a otra(s) clase(s), los objetos de la primera van a poder ser serializados si y slo si cada una de o los campos que corresponden a clases contenidos en el objeto son tambin seriae lizables. Asimismo, si una superclase no es serializable, ninguna subclase de ella puede ser serializable, ya que si serializamos a la subclase le damos la vuelta a la seguridad de la superclase que no desea ser serializada. Cada objeto en el ujo consiste de un bloque de datos, en el que primero se encuentra la descripcin de los datos y despus los datos propiamente dichos. o e Como se puede ver de estos breves comentarios, la serializacin y deserializao cin de objetos tiene much o simos puntos nos que hay que observar. Haremos una lista ms precisa de estos puntos hacia el nal de esta seccin. Pasemos a revisar a o los ujos de objetos.

public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants


Un ujo de entrada de objetos que deserializa datos primitivas que fueron previamente escritos usando un ujo de salida de objetos (ObjectOutputStream). Slo los objetos que implementan la interfaz Seo rializable son sujetos a ser serializados. La interfaz ObjectInput hereda de la interfaz DataInput todos los mtodos e que interpretan valores primitivas y agrega mtodos para leer objetos y e bytes en crudo, sin interpretacin. o La interfaz ObjectStreamConstants simplemente proporciona constantes simblicas para evitar el uso de nmeros mgicos. Adems de implementar o u a a los mtodos de DataInput (que no listaremos) provee mtodos espec e e cos para trabajar con objetos. Clase anidada:

public abstract static class ObjectInputStream.GetField


Provee acceso para los campos persistentes le dos del ujo de entrada. Campos: Los heredados de ObjectStreamConstants. Constructores:

protected ObjectInputStream() throws IOException, SecurityException

398

Entrada y salida

La excepcin de I/O es la usual. La excepcin relativa a la seguridad tiene o o que ver con el administrador de seguridad, si es que se violan los permisos dados para la serializacin. o

public ObjectInputStream(InputStream in) throws IOException


Crea un ObjectInputStream que se asocia al ujo dado por in. Mtodos: e (adems de los de DataInput) a

public void defaultReadObject() throws IOException, ClassNotFoundException


Lee los datos no-estticos y no-transentes de la clase asociada a este a u ujo. Slo puede ser invocada desde el mtodo readObject. Si el ujo no o e est activo, lanza la excepcin NotActiveException. Si no encuentra en a o su entorno la clase a la que pertenecen los objetos serializados, lanza la excepcin ClassNotFoundException. o

protected boolean enableResolveObject( boolean enable)


Habilita al ujo para permitir que los objetos que son le dos del ujo puedan ser reemplazados. Regresa el valor anterior antes de esta invocacin. o Lanza la excepcin si el manejador de seguridad no permite que se reeso criba en este ujo.

public Object readObject() throws IOException, ClassNotFoundException


Permite leer un objeto de un ujo serializado. Adems de las excepciones mencionadas en el encabezado, puede lanzar a una de las siguientes excepciones: InvalidClassException Lanza esta excepcin cuando la clase descrita en o la serializacin no coincide con la que se encuentra declarada, no o reconoce el tipo de alguno de los campos o bien no cuenta con el constructor requerido. StreamCorruptedException La descripcin del objeto viola la consisteno cia del sistema y no lo puede leer. OptionalDataException En el ujo no hay objetos, sino datos primitivos; no est la descripcin del objeto. a o

protected ObjectStreamClass readClassDescriptor ()

10.9 Lectura y escritura de objetos

399

throws IOException, ClassNotFoundException


Lee un descriptor de clase del ujo serializado. Se utiliza cuando la aplicacin espera un descriptor de clase como siguiente elemento en el ujo. o

protected Object resolveObject(Object obj) throws IOException


Permite a subclases en las que se conf sustituir a un objeto por otro a durante la deserializacin. Este mtodo se llama despus de invocar a o e e readObject pero antes de regresar de esta ultima invocacin. o La lista que acabamos de dar dista mucho de ser exhaustiva, pero contiene la suciente informacin para poder cumplir con nuestro objetivo, que consiste en o serializar y deserializar la base de datos. Terminemos de reunir las herramientas que requerimos mostrando la denicin de la clase ObjectOutputStream. o

public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants


Escribe al objeto serializado en un ujo de bytes. Unicamente los objetos de clases que declaran ser Serializable pueden ser serializadas. Tanto la superclase como todos los campos de la clase que a su vez sean objetos deben ser serializables. Clase anidada:

public abstract static class ObjectOutputStream.PutField


Aporta acceso de programacin a los campos persistentes a escribirse e la o salida de objetos (ObjectOutput). Campos: Los heredados de ObjectStreamConstants. Constructores:

public ObjectOutputStream(OutputStream out) throws IOException


Crea un ujo ObjectOutputStream que escribe sobre el ujo de salida especicado (out). Escribe el encabezado de la serializacin en el ujo de o salida. Adems de la excepcin IOException puede lanzar una excepcin a o o de seguridad (SecurityException) si hubiese clases que pasen por encima de las medidas de seguridad de acceso; y nalmente tambin puede lanzar e una excepcin NullPointerException si out es nulo. o

400

Entrada y salida

protected ObjectOutputStream() throws IOException, SecurityException


Sirve para construir subclases que redenan el ujo para no tener que asignar espacio para datos privados que se usan unicamente en esta imple mentacin de ObjectOutputStream. Realiza las vericaciones de seguridad o necesarias.

protected void annotateClass(Class<?> cl) throws IOException


Permite escribir en el ujo datos que corresponden a la clase. Corresponde al mtodo resolveClass de ObjectInputStream. Lo escrito por este mtodo e e debe ser le por resolveClass. do

public void defaultWriteObject () throws IOException


Escribe los campos no estticos y no transentes al ujo. Slo puede ser a u o invocado desde writeObject. So no se hace as lanza una excepcin del tipo o NotActiveException.

protected void drain() throws IOException


Vac cualquier informacin acumulada en el ujo que no ha sido escrita a o en el dispositivo. Similar a ush pero no se encadena con el dispositivo.

protected boolean enableReplaceObject (boolean enable) throws SecurityException


Cuando es verdadera, permite el reemplazo de objetos en el ujo. Si est habilitada, se invoca al mtodoreplaceObject cada vez que se serializa a e un objeto.

public ObjectOutputStream.PutField putFields () throws IOException


Obtiene el objeto que se usa para almacenar campos persistentes que van a ser escritos en el ujo. Los campos se escriben cuando se invoca al mtodo e writeFields.

protected Object replaceObject(Object obj) throws IOException


Permite que clases conables sustituyan un objeto por otro durante la serializacin. El objeto que reemplaza (obj) puede no ser serializable. El o objeto debe ser de la clase que est descrita en el ujo, o de una subclase a de sta. De otra manera lanza una excepcin. e o

public void reset () throws IOException


Ignora el estado de los objetos que ya fueron escritos en el ujo. El estado del ujo se establece igual al de haberlo creado (new ObjectInputStream).

10.9 Lectura y escritura de objetos

401

public void write ( args ) throws IOException


La especicacin de args puede ser cualquiera de las especicadas en o las interfaces DataOutput. Los argumentos son, como en otros ujos, un arreglo de bytes (con, opcionalmente, la primera posicin y el tamao) o o n un valor entero. Adems de aquellos mtodos que escriben enteros, bytes, otantes, etc., tenemos a e mtodos que provienen de la interfaz ObjectOutput. Unicamente mencionaremos e los que provienen de la segunda interfaz, ya que el resto funciona exactamente de la misma manera que en todos los ujos que implementa DataOutput y que ya revisamos.

public void writeFields () throws IOException


Escribe los campos que se encuentren en el buer al ujo de objetos.

protected void writeStreamHeader() throws IOException


Se proporciona para que las subclases puedan pegar su propio encabezado al ujo.

protected void writeClassDescriptor ( ObjectStreamClass desc) throws IOException


Se utiliza para que las subclases puedan determinar la manera como van a codicar la descripcin de la clase. o

public nal void writeObject(Object obj) throws IOException


Escribe el objeto indicado en el ujo, incluyendo a la clase y la rma de la clase a la que pertenece el objeto, los valores de las variables de estado que no sean transentes y todos los supertipos de la clase. Se usa para u brincar la serializacin y hacerla ms ad-hoc para el objeto dado. Si se o a reimplementa este mtodo se tiene que hacer lo mismo con readObject de e ObjectInputStream. Las excepciones que puede lanzar son: InvalidClassException Algo est mal con la clase utilizada por la serializaa cin. o NotSerializableException Alguno de los objetos que forman parte del objeto que se desea escribir no implementa la interfaz Serializable. IOException Excepciones lanzadas por el ujo subyacente.

protected void writeObject(Object obj) throws IOException


Lo usan las subclases para redenir el mtodo writeObject. e

public void writeUnshared(Object obj) throws IOException


Escribe el objeto como unico, sin referencias hacia objetos anteriores en el mismo ujo.

402

Entrada y salida

Como se puede ver de la descripcin de los mtodos (y nos faltaron algunos) o e la escritura y lectura en ujos de objetos es muy sosticada, ya que permite transferir objetos de una aplicacin a otra, conservando cada uno su estado, de o manera transparente y muy efectiva. Cuando se estn ejecutando aplicaciones a donde los datos (en este caso objetos) tiene que transitar por la red, tal vez el tiempo adicional de escribir/leer en un ujo de objetos no es muy signicativo. Sin embargo, hay que tener presente que la lectura/escritura de objetos es un proceso lento y que no debiera utilizarse en aplicaciones locales que manejen un nmero u grande de datos. Regresemos a nuestro objetivo de esta seccin que es la de escribir y leer a o ujos con formato de objetos. Nuevamente permitiremos en cualquier momento de las ejecucin de nuestra aplicacin cargar o descargar objetos a y desde la base o o de datos. Pero lo primero que tenemos que hacer es permitir que cada uno de los objetos que pretendemos escribir implementen a la interfaz Serializable. Lo ms a sencillo ser simplemente agregar este aspecto a nuestra clase Estudiante, y como a sta extiende a InfoEstudiante, ambas tenemos que corregirlas para que implemene ten a la interfaz8 . Como InfoEstudiante originalmente implementa interfaces, a las que no hay que ponerles el calicativo de ser serializables, con estas dos clases ya cubrimos el requisito de que todas las superclases sean serializables. Tambin hay e que notar que el registro consiste de cadenas y 7un referencia; como las cadenas son serializables y la referencia tambin lo es, se cubre ampliamente el requisito e de ser serializable. Los cambios hechos a InfoEstudiante y Estudiante se pueden ver en los listados 10.13 y 10.14 en la pgina opuesta respectivamente. a

Cdigo 10.13 Cambios a InfoEstudiante o

(InfoEstudianteSerial) 1/2

1: import j a v a . i o . ; 2: / 3: Base de d a t o s , a b a s e de l i s t a s de r e g i s t r o s , que emula 4: l a l i s t a de un c u r s o de l i c e n c i a t u r a . T i e n e l a s o p c i o n e s 5: n o r m a l e s de una b a s e de d a t o s y f u n c i o n a m e d i a n t e un Men u 6: / 7: c l a s s I n f o E s t u d i a n t e S e r i a l implements D a E s t u d i a n t e , S e r i a l i z a b l e

......
16: 17: 18: / Constructor s i n parmetros a /

Reescribimos estas dos clases agregando al nombre Serial para mantener intacto el trabajo que realizamos con anterioridad.

10.9 Lectura y escritura de objetos

403 (InfoEstudianteSerial) 2/2

Cdigo 10.13 Cambios a InfoEstudiante o


19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:

public I n f o E s t u d i a n t e S e r i a l () { nombre = c a r r e r a = c u e n t a = n u l l ; } / C o n s t r u c t o r a p a r t i r de d a t o s de un e s t u d i a n t e . l o s campos v i e n e n s e p a r a d o s e n t r e s p o r comas , m i e n t r a s que l o s r e g i s t r o s v i e n e n s e p a r a d o s e n t r e s p o r punto y coma . @param S t r i n g , S t r i n g , S t r i n g , S t r i n g l o s v a l o r e s p a r a cada uno de l o s campos que s e van a l l e n a r . @ r e t u r n I n f o E s t u d i a n t e una r e f e r e n c i a a una l i s t a / p u b l i c I n f o E s t u d i a n t e S e r i a l ( S t r i n g nmbre , S t r i n g c nta , String crrera ) { nombre = nmbre . t r i m ( ) ; cuenta = cnta . trim ( ) ; carrera = c r r e r a . trim ( ) ; }

...... Como ya hab amos mencionado, en la l nea 7: le cambiamos el nombre a la clase y agregamos el que implementa la interfaz Serializable. Por este cambio de nombre, tambin tuvimos que cambiar el nombre a los constructores l e neas 19: y 30: . De similar manera tenemos que modicar a la clase Estudiante.

Cdigo 10.14 Modicaciones a la clase Estudiante o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 13: 14: 15: 16: 17: 18: 19: import j a v a . i o . ;

(EstudianteSerial) 1/2

// i m p o r t i c c 1 . i n t e r f a z . C o n s o l a ; / Base de d a t o s , a b a s e de l i s t a s de r e g i s t r o s , que emula l a l i s t a de un c u r s o de l i c e n c i a t u r a . T i e n e l a s o p c i o n e s n o r m a l e s de una b a s e de d a t o s y f u n c i o n a m e d i a n t e un Men u / c l a s s E s t u d i a n t e S e r i a l extends I n f o E s t u d i a n t e S e r i a l implements S e r i a l i z a b l e {

......
/ C o n s t r u c t o r s i n p a r m e t r o s / a public E s t u d i an t e S e ri al () { super ( ) ; clave = null ; siguiente = null ; s h o r t [ ] tam = { ( s h o r t ) ( tamanhos [ 0 ] + 1 ) , tamanhos [ 1 ] , tamanhos [ 2 ] , tamanhos [ 3 ] , 2 0 } ;

404

Entrada y salida

Cdigo 10.14 Modicaciones a la clase Estudiante o


20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 50: 51: 52: 53: 54: 55: 56: 57: 58:

(EstudianteSerial) 2/2

tamanhos = tam ; } / C o n s t r u c t o r a p a r t i r de d a t o s de un e s t u d i a n t e . l o s campos v i e n e n s e p a r a d o s e n t r e s p o r comas , m i e n t r a s que l o s r e g i s t r o s v i e n e n s e p a r a d o s e n t r e s p o r punto y coma . @param S t r i n g , S t r i n g , S t r i n g , S t r i n g l o s v a l o r e s p a r a cada uno de l o s campos que s e van a l l e n a r . @ r e t u r n E s t u d i a n t e una r e f e r e n c i a a una l i s t a / p u b l i c E s t u d i a n t e S e r i a l ( S t r i n g nmbre , S t r i n g c nt a , String crrera , String clve ) { public E s t udi a n t e S e r i a l daSiguiente () { return s i g u i e n t e ; } / A c t u a l i z a e l campo s i g u i e n t e . @params E s t u d i a n t e l a r e f e r e n c i a que s e va a a s i g n a r . / public void p o n S i g u i e n t e ( E s t u d i a n t e S e r i a l s i g ) { siguiente = sig ; }

......

En las l neas 9:, 13:, 30:, 50: y 56: se encuentran las consecuencias de haber cambiado el nombre a ambas clases de las que hablamos. En la l nea 10: se encuentra la declaracin de que esta clase implementa a la interfaz Serializable, lo o que la hace susceptible de ser escrita o le de un ujo de objetos. da Dado que la base de datos est compuesta por objetos de la clase Estudiante a y eso no lo queremos modicar, tenemos que dar mtodos que conviertan de e EstudianteSerial a Estudiante y viceversa en la clase EstudianteSerial. Son mtodos e sencillos que unicamente copian los campos. El cdigo se puede ver en el listado o 10.15.

Cdigo 10.15 Conversin de Estudiante a EstudianteSerial y viceversa (EstudianteSerial) 1/2 o o


99: 100: 101: 102: 103: 104: / C o n v i e r t e a un e s t u d i a n t e en un e s t u d i a n t e s e r i a l , s i m p l e m e n t e c o p i a n d o l o s campos c o r r e s p o n d i e n t e s . @param nuevo e l E s t u d i a n t e . @ r e t u r n s Un E s t u d i a n t e S e r i a l con e l mismo c o n t e n i d o que nuevo . /

10.9 Lectura y escritura de objetos

405

Cdigo 10.15 Conversin de Estudiante a EstudianteSerial y viceversa (EstudianteSerial) 2/2 o o


105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: p u b l i c v o i d d a E s t u d i a n t e S e r i a l ( E s t u d i a n t e nuevo ) { nombre = nuevo . daNombre ( ) ; c u e n t a = nuevo . daCuenta ( ) ; c a r r e r a = nuevo . d a C a r r e r a ( ) ; c l a v e = nuevo . d a C l a v e ( ) ; } / C o n v i e r t e a un E s t u d i a n t e S e r i a l en un E s t u d i a n t e c o n s t r u y e n d o uno de e s t o s . @param e l e s t u d i a n t e s e r i a l . @ r e t u r n s un E s t u d i a n t e . / public Estudiante daEstudiante () { S t r i n g nmbre = t h i s . daNombre ( ) ; S t r i n g c n t a = t h i s . daCuenta ( ) ; String c rrera = this . daCarrera ( ) ; S t r i n g c l v e = this . daClave ( ) ; r e t u r n new E s t u d i a n t e ( nmbre , c nt a , c r r e r a , c l v e ) ; }

Habiendo hecho estos cambios, nos avocamos a la clase ListaCursoIO a la que identicaremos como ListaCursoIOObj para ver los cambios necesarios en el manejo de nuestra base de datos, que se localizan en la clase MenuListaIOObj. Lo primero que tenemos que hacer es agregarle al mtodo que pide el nombre e del archivo la opcin para que pida un archivo del que se van a leer (o escribir) o objetos, lo que se encuentra en el listado 10.16.

Cdigo 10.16 Solicitud de nombre de archivo para ujo de objetos o


182: 183: 184: 185:

(MenuListaIOObj)

p u b l i c S t r i n g pideNombreArch ( B u f f e r e d R e a d e r cons , i n t c a s o ) throws I O E x c e p t i o n { S t r i n g m e n s a j e = "Por favor dame el nombre del archivo \n" ; switch ( caso ) {

......
219: 220: 221: 222: 223: 224: case LEEROBJETOS : m e n s a j e += "de d nde vas a leer objetos " ; o break ; case GUARDAROBJETOS : m e n s a j e += "en d nde vas a escribir objetos " ; o break ;

......

406

Entrada y salida

Necesitamos tambin declarar un ujo de objetos, al principio del mtodo e e daMenu, lo que se encuentra en el listado 10.17.

Cdigo 10.17 Declaracin de ujo de objetos o o


267: 268: ObjectInputStream archvoObjetosIn = null ; ObjectOutputStream archvoObjetosOut = n u l l ;

(MenuListaIOObj)

Con esto ya estamos listos para llenar los casos que nos ocupan. El algoritmo para la lectura de los registros se muestra en la gura 10.17 y como se puede observar es prcticamente idntico al de leer registros de un archivo directo, excepto a e que por el hecho de leer objetos la mquina virtual se encarga de interpretarlos. a

Figura 10.17

Algoritmo para la lectura de objetos


6 9Abrir el archivo 9 9 6 9 9 9 9Leer el registro actual 8 8

Lectura de Objetos

Cargar registros 9 (mientras haya) 9 9 7 9


9 9 9 7

Ingresarlo a la base de datos

Cerrar el archivo

En este diagrama no pusimos el manejo de excepciones porque no forman parte de la lgica general del algoritmo. En la gura 10.18 mostramos el diagrama para o la escritura a ujos de objetos, que es sumamente similar al de la lectura.

Figura 10.18

Escritura a un ujo de objetos


6 9 9 9Inicio 9 9 9 9 9 9 9 9 9 9 8 5

Abrir el archivo Colocarse al principio de la lista


6 9Convertir al estudiante actual 9 9 8

Escritura de Objetos

9 Tomar registro 9 9 9 9(mientras haya) 9 9 9 9 9 9 9 9 7

en estudiante serial 9Escribirlo al ujo de objetos 9 9 7 Tomar como actual al siguiente

Cerrar el archivo

10.9 Lectura y escritura de objetos

407

Antes de proceder con la codicacin de estos esquemas debemos nuevamente o declarar los ujos correspondientes y una variable para almacenamiento temporal de objetos de la clase EstudianteSerial, as como modicar el men y el manejo de u las opciones. El cdigo correspondiente a esto se encuentra en el listado 10.18. o

Cdigo 10.18 Caso de lectura de objetos (declaraciones) o


......
267: 268: 272: 293: 294: 308: ObjectInputStream archvoObjetosIn = null ; ObjectOutputStream archvoObjetosOut = n u l l ;

(MenuListaIOObj)

......
EstudianteSerial nuevoSrl = null ;

......
+ "(F)\ tLeer objetos de flujo \n" + "(G)\ tEscribir objetos en flujo \n"

......
o p c i o n = " 0123456789 ABCDEFGZ " . i n d e x O f ( s o p c i o n ) ;

En el listado 10.19 se encuentra el cdigo que corresponde al manejo de estos o dos casos en el men. u

Cdigo 10.19 Caso de lectura de objetos o


......
966: 967: 968: 969: 970: 971: 972: 973: 974: 975: 976: 977: 978: 979: 980: 981: 982: 983:

(MenuListaIOObj) 1/3

case LEEROBJETOS : try { archvoObjetosIn = null ; s A r c h i v o = pideNombreArch ( cons , LEEROBJETOS ) ; a r c h v o O b j e t o s I n = new O b j e c t I n p u t S t r e a m ( new F i l e I n p u t S t r e a m ( s A r c h i v o ) ) ; i f ( a r c h v o O b j e t o s I n == n u l l ) { System . o u t . p r i n t l n ( "el archivo NO qued abierto " ) ; o } // end o f i f ( a r c h v o O b j e t o s I n == n u l l ) boolean yaNoHay = f a l s e ; w h i l e ( ! yaNoHay ) { nuevoSrl = null ; / Cuando s e ac ab a un a r c h i v o de o b j e t o s y s e t r a t a de l e e r s i m p l e m e n t e l a n z a una e x c e p c i n o / try { nuevoSrl = ( EstudianteSerial ) archvoObjetosIn . readObject ( ) ;

408

Entrada y salida

Cdigo 10.19 Caso de lectura de objetos o


984: 985: 986: 987: 988: 989: 990: 991: 992: 993: 994: 995: 996: 997: 998: 999: 1000: 1001: 1002: 1003: 1004: 1005: 1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013: 1014: 1015: 1016: 1017: 1018: 1019: 1020: 1021: 1022: 1023: 1024: 1025: 1026: 1027: 1028: 1029:

(MenuListaIOObj) 2/3

} catch ( I O E x c e p t i o n e ) { yaNoHay = t r u e ; } // end o f t r y c a t c h i f ( yaNoHay ) { continue ; } // end o f i f ( a r c h v o O b j e t o s I n . a v a i l a b l e > 0 ) nuevo = n u e v o S r l . d a E s t u d i a n t e ( ) ; miCurso . a g r e g a E s t O r d e n ( nuevo ) ; } // end o f w h i l e ( ( nombre = a r c h i v o I n . r e a d L i n e ( ) . . . } // end o f t r y catch ( C l a s s N o t F o u n d E x c e p t i o n e ) { System . o u t . p r i n t l n ( "Se est leyendo el archivo equivocado " ) ; a } // end o f c a t c h catch ( I n v a l i d C l a s s E x c e p t i o n e ) { System . o u t . p r i n t l n ( "La clase no permite ser serializada " ) ; } // end o f c a t c h catch ( S t r e a m C o r r u p t e d E x c e p t i o n e ) { System . o u t . p r i n t l n ( "La descripci n del objeto " o + "es inconsistente " ) ; } // end o f c a t c h catch ( O p t i o n a l D a t a E x c e p t i o n e ) { System . o u t . p r i n t l n ( "No se encontraron objetos sino " + " datos directos " ) ; } // end o f c a t c h catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude abrir archivo " ) ; } // end o f t r y c a t c h catch ( E x c e p t i o n e ) { System . o u t . p r i n t l n ( "No alcanzaron los datos " ) ; } // end o f c a t c h finally { i f ( a r c h v o O b j e t o s I n != n u l l ) { try { archvoObjetosIn . close ( ) ; } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude cerrar el" + " archivo de lectura " ) ; } // end o f t r y c a t c h } // end o f i f ( a r c h i v o I n != n u l l ) } // end o f f i n a l l y r e t u r n LEEROBJETOS ; case GUARDAROBJETOS : try { s A r c h i v o = pideNombreArch ( cons , GUARDAROBJETOS ) ; archvoObjetosOut = null ;

10.9 Lectura y escritura de objetos

409 (MenuListaIOObj) 3/3

Cdigo 10.20 Caso de lectura de objetos o


1030: 1031: 1032: 1033: 1034: 1035: 1036: 1037: 1038: 1039: 1040: 1041: 1042: 1043: 1044: 1045: 1046: 1047: 1048: 1049: 1050: 1051: 1052: 1053: 1054: 1055: 1056: 1057: 1058: 1059:

a r c h v o O b j e t o s O u t = new O b j e c t O u t p u t S t r e a m ( new F i l e O u t p u t S t r e a m ( s A r c h i v o ) ) ; System . o u t . p r i n t l n ( "Abr el archivo " ) ; i f ( a r c h v o O b j e t o s O u t == n u l l ) { System . o u t . p r i n t l n ( "el archivo NO est abierto !" ) ; a } // end o f i f ( a r c h v o O b j e t o s O u t == n u l l ) l i s t a = ( ( E s t u d i a n t e ) miCurso . d a L i s t a ( ) ) ; int contador = 0; w h i l e ( l i s t a != n u l l ) { n u e v o S r l = new E s t u d i a n t e S e r i a l ( ) ; nuevoSrl . daEstudianteSerial ( l i s t a ) ; archvoObjetosOut . writeObject ( nuevoSrl ) ; l i s t a = l i s t a . daSiguiente (); } // end o f w h i l e ( l i s t a != n u l l ) } catch ( S e c u r i t y E x c e p t i o n e ) { System . o u t . p r i n t l n ( "Se violaron condiciones de seguridad " ) ; } // end o f c a t c h catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude abrir archivo " ) ; } // end o f t r y c a t c h finally { try { i f ( a r c h v o O b j e t o s O u t != n u l l ) { archvoObjetosOut . c l o s e ( ) ; } // end o f i f ( a r c h i v o O u t != n u l l ) } catch ( I O E x c e p t i o n e ) { System . e r r . p r i n t l n ( "No pude cerrar el archivo " ) ; } // end o f t r y c a t c h } // end o f f i n a l l y r e t u r n GUARDAROBJETOS ;

Como hasta ahora, la lectura es un poco ms compleja que la escritura, ya a que sabemos lo que estamos escribiendo, pero al tratar de leer pueden venir mal los datos, no existir el archivo, leer de ms, etc.. Pasamos a explicar unicamente a aquellas l neas de cdigo que no sean del todo claras. o Para abrir el archivo de entrada o de salida se construye un ujo de objetos sobre un ujo de bytes l neas 970:971: y 1030: 1031: ya que esa es la denicin del constructor de un ujo de objetos. o Se lee el registro a un EstudianteSerial l nea 982: 983: , pero como la lista que tenemos es de objetos de la clase Estudiante, lo tendremos que convertir antes de agregarlo a la lista. En el caso de ujos de objetos no tenemos un indicador

410

Entrada y salida

adecuado de cundo se acab el archivo y lo unico que podemos usar es la excepa o cin de entrada y salida que se va a alcanzar cuando ya no pueda leer del ujo. o Por ello, colocamos estas l neas en un bloque trycatch 981: a 986: que atrape la excepcin y simplemente prenda una variable para avisar que ya no hay datos. o En las l neas 987: a 989: se detecta que ya se alcanz el n de archivo (o que no o se pudo leer) y se regresa al encabezado del while, para evitar tratar de procesar datos errneos. o Finalmente, en las l neas 990: y 991: se obtiene un objeto Estudiante a partir del que se ley y se agrega a la lista. o Las clusulas catch externas a la lectura y que corresponden unicamente al caso a de lectura l neas 994: a 1013: simplemente proporcionan un mensaje de error de qu es lo que sucedi, lo mismo que las clusulas catch del caso de escritura a e o a ujo de objetos l neas 1044: a 1049: . En ambos casos que nos ocupan tenemos una clusula nally que se encarga de a cerrar el archivo, preguntando antes si es que el archivo fue abierto adecuadamente l neas 1015: a 1022: en lectura y 1050: a 1058: en escritura que adems tiene a que estar, nuevamente, en un bloque try-catch para el caso en que se lance una excepcin. o Es interesante ver con un visor de texto en ASCII un archivo creado como ujo de objetos ya que se ve, de alguna manera, la gran cantidad de informacin o que se encuentra adicional a los datos mismos. Esto se debe a que con cada objeto se tiene que describir al mismo, lo que sucede ya que en un mismo ujo de objetos podemos escribir objetos de distintas clases y al leerlos, automticamente a se carga la descripcin que trae consigo el objeto. Es por eso que la lectura siempre o es de un Object y se tiene que hacer un cast para obtener el objeto que creemos estamos leyendo. Para decodicar un objeto de un ujo del que no sabemos a qu clase pertenece, podemos utilizar la clase Class que proporciona toda clase de e herramientas para ello.

10.10 Colofn o
Revisamos varias clases de ujos de entrada y salida, pero de ninguna manera fuimos exhaustivos. Sin embargo, los conceptos vertidos en este cap tulo deber servir de plataforma para poder revisar (y utilizar adecuadamente) ujos de otras clases que no hayan sido tratados. Una revisin completa de toda la entrada y o salida que ofrece Java est ms all del alcance de este material. a a a

Hilos de ejecucin o

11

11.1 Qu es un hilo de ejecucin? e o

Hasta ahora, cada vez que ejecutamos un programa estamos trabajando de solamente sobre el programa. Sin embargo, cualquier sistema operativo moderno tiene ms de un programa trabajando al mismo tiempo: el servidor de impresin, a o el shell, los demonios para correo electrnico, etc. Cada uno de estos programas o constituyen un hilo de ejecucin thread, proceso, tarea que trabaja indepeno diente de otros procesos y que usan un juego comn de datos. u Un ejemplo ms sencillo y ms usual es el manejo de las cuentas de cheques a a de un banco. El banco cuenta con su base de datos y tenemos a miles de cajeros teniendo acceso a la base de datos, cada cajero en su propio hilo de ejecucin. No o ser pensable que fuera un unico proceso el que se encargara de todos los accesos. a Decimos entonces que tenemos procesos o hilos de ejecucin concurrentes, ya que o concurren a un mismo conjunto de datos o recursos.

412

Hilos de ejecucin o

11.2 La clase Thread


En Java tenemos la posibilidad de que un proceso lance, a su vez, a otros procesos. En principio, los procesos lanzados pueden o no mantener comunicacin o con el proceso padre que los lanz. Asimismo, los procesos lanzados, a diferencia o de los mtodos invocados, no regresan al lugar desde el que se les lanz. Pero e o tienen acceso a todos los recursos y atributos con que cuenta el mtodo que los e haya lanzado. La clase Thread es la encargada de iniciar hilos de ejecucin independientes. o Es una clase muy extensa que tiene much simos mtodos y atributos. En este e momento nos interesan dos mtodos: el que dene al hilo de ejecucin, run(), y el e o que lo inicia, start(). Si deseamos que de una clase se puedan crear hilos de ejecucin independientes o hacemos que extienda a Thread y que tenga un mtodo run(). Veamos el ejemplo e de una clase que lanza dos hilos de ejecucin independientes en el listado 11.1. o

Cdigo 11.1 Objeto que lanza dos hilos de ejecucin o o

1/2

1: p u b l i c c l a s s PingPong extends Thread { 2: p r i v a t e S t r i n g word ; 3: private int delay ; 4: 5: p u b l i c PingPong ( S t r i n g whatToSay , i n t d e l a y T i m e ) { 6: word = whatToSay ; 7: d e l a y = delayTime ; 8: } 9: 10: public void run ( ) { 11: System . e r r . p r i n t l n ( " Empez :"+Thread . c u r r e n t T h r e a d ( ) . getName ( ) ) ; o 12: try { 13: while ( true ) { 14: System . o u t . p r i n t ( word + " " ) ; 15: Thread . s l e e p ( d e l a y ) ; 16: } 17: } catch ( I n t e r r u p t e d E x c e p t i o n e ) { 18: return ; // Termina e s t e h i l o de e j e c u c i n o 19: } 20: }

11.2 La clase Thread

413
2/2

Cdigo 11.1 Objeto que lanza dos hilos de ejecucin o o


21: 22: 23: 24: 25: 26: }

p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { System . o u t . p r i n t l n ( " Nombre : " + c u r r e n t T h r e a d ( ) . getName ( ) ) ; new PingPong ( "ping" , 3 3 ) . s t a r t ( ) ; // 1/30 de s e g u n d o new PingPong ( "pong" , 1 0 0 ) . s t a r t ( ) ; // 1/10 de s e g u n d o }

En el listado 11.1 en la pgina opuesta la clase PingPong extiende a la clase a Thread, por lo que tendr que redenir su propio mtodo run() el de Thread no a e hace nada lo que hace en las l neas 10: a 20:. Esto es lo que va a hacer el hilo de ejecucin, cuando sea lanzado. Se inicia un nuevo hilo de ejecucin cuando se o o invoca al mtodo start() de un objeto, cuya clase extiende a Thread l e neas 23:2 y 24: del listado 11.1 en la pgina opuesta. a En la l nea 11: aparecen dos invocaciones a dos mtodos de Thread. El mtodo e e currentThread() es un mtodo esttico de la clase Thread y regresa un objeto de e a tipo Thread pod haber sido llamada sin precederlo del nombre de la clase. El a mtodo getName() regresa una cadena, que es el nombre del hilo de ejecucin que e o se lanz. El nombre del hilo de ejecucin es un atributo del mismo. Tambin en la o o e l nea 15: se encuentra un mtodo esttico de la clase Thread, el mtodo sleep(delay). e a e Este mtodo suspende la ejecucin del hilo durante el tiempo estipulado. e o

Figura 11.1

Salida de PingPong.
java PingPong Nombre: main Empez:Thread-0 o ping Empez:Thread-1 o pong ping ping pong ping pong ping ping pong ping pong ping ping pong ping ping ping ping pong ping ping ping pong ping ping ping ping pong ping ping ping ping pong ping ping ping ping pong ping ping ping ping pong ping ping

ping ping ping ping ping pong pong pong pong

pong pong pong pong pong ping ping ping ping

ping ping ping ping ping ping ping ping

ping ping ping ping ping ping pong pong

ping pong pong pong pong pong ping ping

pong ping ping ping ping ping ping ping

ping ping ping ping ping ping ping pong

ping ping pong pong pong pong pong ping

La clase PingPong podr no tener mtodo main. Lo ponemos para hacer una a e demostracin de esta clase. En la l o nea 22: nuevamente invocamos a los mtodos e

414

Hilos de ejecucin o

currentThread y getName para que nos indiquen el nombre del hilo donde aparece esa solicitud. A continuacin creamos dos objetos annimos y les solicitamos que o o cada uno de ellos inicie su hilo de ejecucin con el mtodo start(). o e La ejecucin del mtodo main de esta clase produce la salida que se observa o e en la gura 11.1 en la pgina anterior. a Como se puede observar en la gura 11.1 en la pgina anterior, mientras que a el nombre del proceso que est ejecutndose inmediatamente antes de lanzar los a a hilos de ejecucin se llama main, que es el que tiene, los hilos de ejecucin lanzados o o tienen nombres asignados por el sistema. Pod amos haberles asignado nosotros un nombre al construir a los objetos. Para eso debemos modicar al constructor de la clase PingPong e invocar a un constructor de Thread que acepte como argumento a una cadena, para que sta sea el nombre del hilo de ejecucin. Los cambios a e o realizar se pueden observar en el listado 11.2. La salida que produce se puede ver en la gura 11.2 en la pgina opuesta. a

Cdigo 11.2 Asignacin de nombres a los hilos de ejecucin o o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: p u b l i c c l a s s NombrePingPong extends Thread p r i v a t e S t r i n g word ; private int delay ; {

p u b l i c NombrePingPong ( S t r i n g whatToSay , i n t de l a y Ti m e , S t r i n g name ) { super ( name ) ; word = whatToSay ; d e l a y = delayTime ; }

. . . // El mtodo run() queda exactamente igual e


20: 21: 22: 23: 24: 25: 26: 27: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { System . o u t . p r i n t l n ( " Nombre : "+ c u r r e n t T h r e a d ( ) . getName ( ) ) ; / 1/30 de s e g u n d o : / new NombrePingPong ( "ping" , 3 3 , "Ping" ) . s t a r t ( ) ; / 1/10 de s e g u n d o : / new NombrePingPong ( "pong" , 1 0 0 , "Pong" ) . s t a r t ( ) ; } }

Ejecucin del mtodo run() o e


Una vez iniciado el hilo de ejecucin, el mtodo run() va a proseguir su ejecucin o e o hasta que una de tres cosas sucedan:
I.

El mtodo se encuentra un return, en cuyo caso simplemente termina su e

11.3 La interfaz Runnable

415

ejecucin. o
II. III.

El mtodo llega al nal de su denicin. e o Se presenta una excepcin en el mtodo. o e

Debe quedar claro que en el caso de hilos de ejecucin, cuando uno termina su o ejecucin no es que regrese al lugar desde el que se le lanz, sino que simplemente o o se acaba. El hilo de ejecucin, una vez lanzado, adquiere una vida independiente, o con acceso a los recursos del objeto desde el cual fue lanzado.

Figura 11.2

Salida con asignacin de nombres. o


java NombrePingPong Nombre: main Empez:Ping o ping Empez:Pong o pong ping ping pong ping ping pong ping pong ping ping ping ping ping pong ping ping pong ping ping ping ping ping pong ping pong ping ping pong ping ping pong ping ping pong ping pong ping ping pong

ping ping pong ping pong ping pong ping ping ping

ping pong ping pong ping ping ping ping pong

pong ping ping ping ping pong ping pong ping

ping ping pong ping pong ping ping ping ping

ping pong ping ping ping ping pong ping pong

ping ping ping pong ping pong ping pong ping

pong ping pong ping pong ping ping ping ping

11.3 La interfaz Runnable


Java proporciona otra manera de lanzar hilos de ejecucin, que es mediano te clases que implementan a la interfaz Runnable. Esta interfaz slo declara un o mtodo, run(), por lo que una clase que implemente a esta interfaz, sus objee tos tambin pueden lanzar hilos de ejecucin independientes. Veamos la clase del e o PingPong programada implementando a la interfaz Runnable en el listado 11.3 en la siguiente pgina. a

416

Hilos de ejecucin o

Cdigo 11.3 Hilos de ejecucin con la interfaz Runnable o o


1: c l a s s RunPingPong implements R u n n a b l e { 2: p r i v a t e S t r i n g word ; 3: private int delay ; 4: 5: RunPingPong ( S t r i n g whatToSay , i n t d e l a y T i m e ) { 6: word = whatToSay ; 7: d e l a y = delayTime ; 8: } 9: 10: public void run ( ) { 11: System . o u t . p r i n t l n ( "El nombre del hilo de ejecuci n es: " + o 12: Thread . c u r r e n t T h r e a d ( ) . getName ( ) ) ; 13: try { 14: for ( ; ; ) { 15: System . o u t . p r i n t ( word + " " ) ; 16: Thread . s l e e p ( d e l a y ) ; 17: } 18: } catch ( I n t e r r u p t e d E x c e p t i o n e ) { 19: return ; 20: } 21: } 22: 23: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 24: R u n n a b l e p i n g = new RunPingPong ( "ping" , 3 3 ) ; 25: R u n n a b l e pong = new RunPingPong ( "pong" , 1 0 0 ) ; 26: new Thread ( p i n g , "Ping" ) . s t a r t ( ) ; 27: new Thread ( pong , "Pong" ) . s t a r t ( ) ; 28: } 29: }

La clase RunPingPong se declara implementando Runnable. Al igual que cuando se extiende a la clase Thread, RunPingPong debe denir su propio mtodo run(), e que es lo que va a hacer el hilo de ejecucin una vez lanzado. Bsicamente hace o a lo mismo que hac la clase PingPong, que era una extensin de Thread. Donde a o cambia un poco el asunto es al crear los hilos de ejecucin. Si vemos las l o neas que corresponden a main, primero se crean objetos de tipo Runnable se pueden crear objetos de este tipo, ya que Runnable est implementada en RunPingPong y se a asignan en esas referencias a objetos tipo RunPingPong. Una vez hecho esto, se construyen hilos de ejecucin l o neas 24: y 25: para cada uno de los objetos y se lanzan mediante el mtodo start. En esta ocasin, al construir el hilo de ejecucin e o o le estamos asignando un nombre, mediante el segundo argumento al constructor de la clase Thread.

11.3 La interfaz Runnable

417

Una ejecucin de este programa produce lo que se ve en la gura 11.3. Es o importante aclarar que dos ejecuciones de RunPingPong pueden o no dar la misma salida. La mquina virtual de Java (JVM) no puede garantizar el orden en que a llegan los procesos a la pantalla. Ntese tambin que no se mezclan las palabras o e ping y pong en la misma. Esto es porque una vez que System.out empieza a escribir desde uno de los hilos de ejecucin, no suelta la pantalla hasta que o termina.

Figura 11.3

Salida de RunPingPong.
java RunPingPong El nombre del hilo de ejecucin es: Ping o ping El nombre del hilo de ejecucin es: Pong o pong ping ping pong ping ping pong ping ping pong pong ping ping pong ping ping pong ping ping pong ping ping ping pong ping ping pong ping ping pong ping ping pong ping ping ping pong ping ping pong ping ping pong ping ping pong ping ping ping pong ping ping pong ping ping pong ping ping pong ping ping ping pong ping ping pong ping ping pong ping ping ping pong ping ping pong ping ping pong ping ping pong ping ping ping pong ping ping pong ping ping pong ping ping pong ping ping ping pong ping ping pong ping ping pong ping

ping ping ping ping ping ping ping ping ping ping

ping ping ping ping ping ping pong pong pong pong

ping pong pong pong pong pong ping ping ping ping

11.3.1.

Constructores de la clase Thread


Hasta ahora hemos visto cuatro constructores para objetos de la clase Thread:
public Thread()

Es el constructor por omisin. Construye un hilo de ejecucin annimo, al o o o que el sistema le asignar nombre. Cuando una clase que extiende a Thread a no invoca a ningn constructor de la superclase, ste es el constructor que u e se ejecuta.
public Thread(String name)

Este construye un hilo de ejecucin y la asigna el nombre dado como arguo mento.

418
public Thread(Runnable target)

Hilos de ejecucin o

Usa el mtodo run() de la clase que implementa a Runnable para ejecutarse. e


public Thread(Runnable target, String name)

Construye un Thread con el nombre especicado y usa el mtodo run() de e la clase que implementa a Runnable. El problema con implementar a travs de Runnable es que el mtodo run() es e e pblico y entonces lo podr ejecutar cualquiera que tuviera acceso a la clase. Una u a manera de evitar esto es declarando una clase interna annima para que no se o tenga acceso al mtodo run(). e Cuando hay ms de un hilo de ejecucin en un programa se corre el riesgo de a o que uno de ellos corrompa la informacin del otro. A los puntos en los que esto o puede suceder se les llama regiones crticas y se evita la interferencia sincronizando las actividades de esas regiones cr ticas. Se sincroniza el acceso a datos comunes a travs de candados (lock s). El proceso e que quiere trabajar sobre algn dato comn, adquiere el candado de los objetos u u sobre los que va a trabajar. Todo objeto tiene un candado. Para trabajar con ese objeto, cualquier hilo de ejecucin debe encontrar el candado disponible. Se usa o el trmino cdigo sincronizado para aquel cdigo que se encuentra en un mtodo e o o e o enunciado sincronizado.

11.4 Sincronizacin de hilos de ejecucin o o


11.4.1. Mtodos sincronizados e
Un objeto que va a ser usado simultneamente por ms de un hilo de ejecucin a a o declara a los mtodos peligrosos como sincronizados (synchronized). Cuando e se solicita la ejecucin de un mtodo sincronizado, el hilo de ejecucin primero o e o adquiere el candado sobre el objeto. Realiza la ejecucin solicitada, y al terminar o el mtodo libera el candado del objeto. Si otro hilo de ejecucin intenta ejecutar e o cualquiera de los mtodos sincronizados del objeto, se bloquear y esperar hasta e a a que el candado del objeto est disponible. Si los mtodos no son sincronizados e e podrn trabajar sobre el objeto independientemente del estado del candado. a Para evitar que algn hilo de ejecucin se bloquee a s mismo, el dueo del u o n candado es el hilo de ejecucin mismo. Por lo tanto, si se encuentra ejecutando o algn mtodo sincronizado sobre cierto objeto, la ejecucin de ms mtodos, sinu e o a e

11.4 Sincronizacin de hilos de ejecucin o o

419

cronizados o no, sobre el mismo objeto proceder sin problemas, ya que el hilo a de ejecucin posee el candado respectivo. El candado se libera cuando el hilo de o ejecucin termina de ejecutar el primer mtodo sincronizado que invoc, que es el o e o que le proporcion el candado. Si esto no se hiciera as cualquier mtodo recursivo o , e o de herencia, por ejemplo, bloquear la ejecucin llegando a una situacin en la a o o que el hilo de ejecucin no puede continuar porque el candado est comprometio a do (aunque sea consigo mismo), y no puede liberar el candado porque no puede continuar. El candado se libera automticamente en cualquiera de estos tres casos: a El mtodo termina normalmente porque se encuentra un enunciado return. e El mtodo termina normalmente porque lleg al nal de su cuerpo. e o El mtodo termina anormalmente lanzando una excepcin. e o El hecho de que la liberacin del candado sea automtica libera al programador o a de la responsabilidad de adquirir y liberar el candado, evitando as que algn u objeto quede congelado porque se olvidaron de liberar su candado. Para manejar una cuenta de cheques desde varias posibles terminales, el cdigo o se har como se muestra en el listado 11.4. a

Cdigo 11.4 Manejo sincronizado de una cuenta de cheques o


1: c l a s s S o b r e g i r a d o E x c e p t i o n extends E x c e p t i o n { 2: p u b l i c S o b r e g i r a d o E x c e p t i o n ( S t r i n g msg ) { 3: super ( msg ) ; 4: } 5: } 6: 7: c l a s s Cuenta { 8: p r i v a t e double b a l a n c e ; 9: 10: p u b l i c Cuenta ( double d e p o s i t o I n i c i a l ) { 11: balance = d e p o s i t o I n i c i a l ; 12: } 13: 14: p u b l i c s y n c h r o n i z e d double g e t B a l a n c e ( ) { 15: return balance ; 16: } 17: 18: p u b l i c s y n c h r o n i z e d v o i d d e p o s i t o ( double c a n t i d a d ) 19: b a l a n c e += c a n t i d a d ; 20: }

1/2

420

Hilos de ejecucin o 2/2

Cdigo 11.4 Manejo sincronizado de una cuenta de cheques o


21: 22: 23: 24: 25: 26: 27: 28: 29: 30: } p u b l i c s y n c h r o n i z e d v o i d c h e q u e ( double c a n t i d a d ) throws S o b r e g i r a d o E x c e p t i o n { i f ( b a l a n c e >= c a n t i d a d ) { b a l a n c e = c a n t i d a d ; } else { throw new S o b r e g i r a d o E x c e p t i o n ( " Cuenta sobregirada " ) ; } }

Revisemos con cuidado la sincronizacin que pedimos en la clase Cuenta. El o constructor no est sincronizado, porque la creacin de una nueva cuenta puede a o hacerse unicamente desde un solo hilo. Adems, los constructores no pueden estar a sincronizados. Pero debemos proteger al atributo balance, ya que no puede suceder al mismo tiempo que sea modicado y se solicite su valor. Tampoco puede suceder que sea modicado en direcciones opuestas. En estos momentos debe quedar claro por qu se ha elegido declarar los campos e de datos como privados (o en el mejor de los casos, protegidos) y denir mtodos e de acceso y modicacin. Si los campos fueran declarados pblicos o de paquete, o u no habr manera de protegerlos de accesos invlidos desde mltiples hilos de a a u ejecucin. o Con la declaracin de synchronized se garantiza que dos o ms hilos de ejeo a cucin no van a interferirse entre s Lo que no se puede garantizar es el orden o . en que se van a ejecutar los distintos hilos de ejecucin. Si dos hilos presentan o solicitudes simultneas, es impredecible cul de ellas va a ser atendida primero. a a Para garantizar el orden de ejecucin frente a procesos simultneos, los hilos de o a ejecucin tienen que coordinarse entre s de alguna manera que va a depender de o la aplicacin. o

11.4.2.

Sincronizacin de mtodos y la herencia o e


Cuando el mtodo sincronizado de una superclase se extiende mediante hee rencia, el mtodo de la subclase puede o no estar sincronizado. El programador e podr programar la subclase de tal manera que ya no hubiera la posibilidad de a interferir o ser interferido por algn otro hilo de ejecucin. Sin embargo, si el u o mtodo no sincronizado de la subclase invoca al mtodo sincronizado de la sue e

11.4 Sincronizacin de hilos de ejecucin o o

421

perclase, esta invocacin deber estar sincronizada. A la inversa tambin puede o a e suceder: un mtodo no sincronizado en la superclase puede extenderse a un mtodo e e sincronizado en la subclase.

11.4.3.

Mtodos estticos sincronizados e a


Tambin los datos estticos de las clases se pueden sincronizar, utilizando e a para ello mtodos estticos. Cuando algn hilo de control ejecuta alguno de estos e a u mtodos, ningn otro hilo de control podr invocarlos, exactamente de la misma e u a manera que con los mtodos que pertenecen a objetos. Sin embargo, los objetos e de la clase si podrn ser usados por otros hilos de control, ya que el candado se a referir unicamente al objeto que representa a la clase, no a los objetos de ese a tipo.

11.4.4.

Enunciados sincronizados
Puede suceder que queramos sincronizar el acceso no unicamente al objeto vigente (this), sino de cualquier otro objeto involucrado en nuestro cdigo, y no o queremos que la sincronizacin dure todo el mtodo, sino unicamente mientras se o e ejecutan ciertos enunciados. Podemos entonces sincronizar un pedazo de cdigo o con la siguiente sintaxis: Sintaxis: xenunciado de sincronizaciny::= synchronized( xexpry) { o xenunciadosy } Semantica: La xexpry tiene que regresar una referencia a un objeto. Mientras se ejecutan los xenunciadosy, el objeto referido en la xexpry queda sincronizado. Si se desea sincronizar ms de un objeto, se recurrir a enunciados de estos a a anidados. El candado quedar liberado cuando se alcance el nal de los a enunciados, o si se lanza alguna excepcin dentro del grupo de enunciados. o Veamos un ejemplo de sincronizacin de un grupo de enunciados en el listao do 11.5 en la siguiente pgina. a

422

Hilos de ejecucin o

Cdigo 11.5 Sincronizacin selectiva sobre objetos o o


1: 2: 3: 4: 5: 6: 7: 8: 9: // Cambiar a l o s e l e m e n t o s de un a r r e g l o // p o r s u v a l o r a b s o l u t o public s t a t i c void abs ( i n t [ ] v a l o r e s ) { synchronized ( v a l o r e s ) { f o r ( i n t i = 0 ; i < v a l o r e s . l e n g t h ; i ++) i f ( v a l o r e s [ i ] < 0) v a l o r e s [ i ] = v a l o r e s [ i ] ; } }

A este tipo de sincronizacin se le conoce como sincronizacin del cliente, o o porque son los usuarios del objeto los que se ponen de acuerdo para sincronizar su uso. Por ejemplo, en el caso de los arreglos, no hay otra manera de sincronizarlos, ya que no tenemos acceso a los mtodos para sincronizarlos desde el servidor. e Los enunciados de sincronizacin permiten: o Mantener con candado a un objeto el menor tiempo posible (nicamente el u indispensable). Adquirir el candado de otros objetos diferentes de this. Veamos un ejemplo de sincronizacin con objetos distintos de this en el listao do 11.6.

Cdigo 11.6 Sincronizacin de variables primitivas en enunciados o o


1: c l a s s G r u p o s S e p a r a d o s { 2: p r i v a t e double a V a l = 0 . 0 ; 3: p r i v a t e double bVal = 1 . 1 ; 4: p r o t e c t e d O b j e c t l o c k A = new O b j e c t ( ) ; 5: p r o t e c t e d O b j e c t l o c k B = new O b j e c t ( ) ; 6: 7: p u b l i c double getA ( ) { 8: synchronized ( lockA ) { 9: return aVal ; 10: } 11: } 12: 13: p u b l i c v o i d s e t A ( double v a l ) { 14: synchronized ( lockA ) { 15: aVal = v a l ; 16: } 17: }

1/2

11.4 Sincronizacin de hilos de ejecucin o o

423
2/2

Cdigo 11.6 Sincronizacin de variables primitivas en enunciados o o


18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: } p u b l i c double getB ( ) { synchronized ( lockB ) { r e t u r n bVal ; } } p u b l i c v o i d s e t B ( double v a l ) synchronized ( lockB ) { bVal = v a l ; } } public void r e s e t ( ) { synchronized ( lockA ) { synchronized ( lockB ) { a V a l = bVal = 0 . 0 ; } } } {

Este tipo de sincronizacin la hace el servidor. Elige sincronizar unicamente a o los datos primitivos del objeto, y cada uno de estos datos independiente del otro. Lo consigue restringiendo el acceso a los mismos unicamente a travs de mtodos e e de la clase, donde asocia a un objeto arbitrario para que maneje el candado de cada uno de los datos. De esta manera, como el hilo de ejecucin que llega al proceso o adquiere el candado del objeto, ningn otro hilo de ejecucin puede entrar a la u o regin cr o tica hasta que el primero que lleg libere el candado. En el mtodo o e reset() se adquieren ambos candados antes de entrar a la regin cr o tica, que es la que modica el valor de los datos primitivos. Como ya hab amos mencionado, si queremos sincronizar con ms de un objeto simplemente anidamos los enunciados a de sincronizacin. Cualquier hilo de ejecucin que intente ejecutar la regin cr o o o tica tendr que librar ambos obstculos. a a Lo que se pretende con la sincronizacin es que un mtodo (o grupo de enunciao e dos) se ejecuten como si fueran atmicos, esto es, que nada los interrumpa desde o que empiezan a ejecutarse hasta que llegan al nal. Por supuesto que estamos hablando de la ejecucin del bytecode en la JVM, donde cada enunciado escrito en o Java se traduce a varios enunciados del lenguaje de mquina. Por lo tanto, algn a u otro proceso concurrente podr ejecutarse antes de que se termine de ejecutar el a enunciado peligroso.

424

Hilos de ejecucin o

11.4.5.

Modelos de sincronizacin o
Regresemos al esquema de cliente-servidor en el cual el objeto que da el servicio es el servidor mientras que el que lo solicita es el cliente. La pregunta importante en la sincronizacin es quin deber ser el responsable de la misma? Trataremos o e a de dar algunas ideas al respecto. En general en la orientacin a objetos, como cada objeto se conoce a s mismo o debe encargarse de su propia sincronizacin, esto es, de protegerse en regiones o cr ticas de manera adecuada. Esto responder a que sea el servidor, el proveedor a de servicios, el encargado de manejar la sincron Pero pudiera suceder que el a. diseador de la clase que va a dar los servicios no pens en que sus objetos fuen o ran usados concurrentemente. En ese caso, ser la responsabilidad del cliente el a manejar la sincronizacin entre los distintos procesos que quieran trabajar sobre o algn objeto. u El problema de sincronizacin es complejo y en algunos casos puede llevar a lo o que se conoce como un abrazo mortal deadlock en el que un proceso est espea rando adquirir un candado que no es soltado por otro procesa que espera adquirir el candado que tiene el primer proceso. De esta manera, ambos procesos estn a esperando a que el otro proceso libere el candado que necesitan para proseguir.

11.5 Comunicacin entre hilos de ejecucin o o


Si bien el mecanismo de sincronizacin evita que los procesos intereran unos o con otros, no tenemos hasta el momento ninguna manera de que los hilos de ejecucin se comuniquen entre s Por ejemplo, en el caso del servidor de impresora o . debe suceder que cuando la cola de impresin est vac el servidor de impreo e a, sora libere el recurso y espere hasta que algn trabajo de impresin ingrese a la u o misma. Si no es as el servidor est trabajando intilmente, revisando la cola , a u continuamente hasta que haya algo, pero manteniendo en su poder el candado, lo que no permitir que ningn objeto ingresara a la cola. Asimismo, para que esto a u funcione adecuadamente, se requiere que el proceso que coloca elementos en la cola avise a los procesos que estn esperando que ya hay algo que sacar. Tenemos, a entonces, dos aspectos en este tipo de comunicacin: por un lado, el proceso que o est dispuesto a esperar a que algo suceda, y por el otro el proceso que logra que a algo suceda y lo notica.

11.5 Comunicacin entre hilos de ejecucin o o

425

La forma general para que un proceso espere a que una condicin se cumpla es o
s y n c h r o n i z e d v o i d doWhenCondition ( ) { while ( ! c o n d i c i o n ) wait ( ) ; // . . . Se h a c e l o que s e t i e n e que h a c e r cuando l a // condicin es verdadera o }

Cuando se usa el enunciado wait se deben considerar los siguientes puntos: Como hay varios hilos de ejecucin, la condicin puede cambiar de estado o o an cuando este hilo de ejecucin no est haciendo nada. Sin embargo, es u o e importante que estos enunciados estn sincronizados. Si estos enunciados no e estn sincronizados, pudiera suceder que el estado de la condicin se hiciera a o verdadero, pero entre que este cdigo sigue adelante, algn otro proceso hace o u que vuelva a cambiar el estado de la condicin. Esto har que este proceso o a trabajara sin que la condicin sea verdadera. o La denicin de wait pide que la suspensin del hilo de ejecucin y la liberao o o cin del candado sea una operacin atmica, esto es, que nada pueda suceder o o o ni ejecutarse entre estas dos operaciones. Si no fuera as podr suceder que a hubiera un cambio de estado entre que se libera el candado y se suspende el proceso, pero como el proceso ya no tiene el candado, la noticacin se o podr perder. a La condicin que se est probando debe estar siempre en una iteracin, para o a o no correr el riesgo de que entre que se prueba la condicin una unica vez y o se decide qu hacer, la condicin podr cambiar otra vez. e o a Por otro lado, la noticacin de que se cambi el estado para una condicin o o o generalmente toma la siguiente forma:
synchronized v o i f changeCondition ( ) { // . . . Cambia a l g u n v a l o r que s e u s a en l a p r u e b a // de l a c o n d i c i n o notifyAll (); // o b i e n n o t i f y ( ) }

Si se usa notifyAll() todos los procesos que estn en estado de espera que ejea cutaron un wait() se van a despertar, mientras que notify() unicamente despierta a un hilo de ejecucin. Si los procesos estn esperando distintas condiciones se o a debe usar notifyAll(), ya que si se usa notify() se corre el riesgo de que despierte un proceso que est esperando otra condicin. En cambio, si todos los procesos estn a o a esperando la misma condicin y slo uno de ellos puede reiniciar su ejecucin, no o o o es importante cul de ellos se despierta. a

426

Hilos de ejecucin o

Volvamos al ejemplo del servidor de impresora, pero incluyendo ahora un hilo de ejecucin que produce trabajos de impresin. o o

11.5.1.

Ejemplo: un servidor de impresora

Como sabemos a estas alturas, en los sistemas Unix las impresoras estn coneca tadas a los usuarios a travs de un servidor de impresin. Cada vez que un usuario e o solicita la impresin de algn trabajo, lo que en realidad sucede es que se arma la o u solicitud del usuario y se forma en una cola de impresin, que es atendida uno a la o vez. En una estructura de datos tipo cola, el primero que llega es el primero que es atendido. Por supuesto que el manejo de la cola de impresin se tiene que hacer o en al menos dos hilos de ejecucin: uno que forma a los trabajos que solicitan ser o impresos y otra que los toma de la cola y los imprime. En el listado 11.7 vemos la implementacin de una cola genrica o abstracta (sus elementos son del tipo o e Object) usando para ello listas ligadas. Como los elementos son objetos, se puede meter a la cola cualquier tipo de objeto, ya que todos heredan de Object. Se usa para la implementacin listas ligadas, ya que siendo genrica no hay una idea de o e cuantos elementos podrn introducirse a la cola. Cuando se introduce algn elea u mento a la cola en el mtodo add, se notica que la cola ya no est vac Cuando e a a. se intenta sacar a un elemento de la cola, si la cola est vac el mtodo take entra a a, e a una espera, que rompe hasta que es noticado de que la cola ya no est vac a a.

Cdigo 11.7 Cola genrica con operaciones sincronizadas o e


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: c l a s s Queue { / P r i m e r o y u l t i m o e l e m e n t o s de l a c o l a / p r i v a t e C e l l head , t a i l ; p u b l i c s y n c h r o n i z e d v o i d add ( O b j e c t o ) { C e l l p = new C e l l ( o ) ; // E n v o l v e r a o en una C e l l i f ( t a i l == n u l l ) { head = p ; } else { t a i l . setNext (p ) ; } p . setNext ( null ) ;

1/2

11.5 Comunicacin entre hilos de ejecucin o o

427
2/2

Cdigo 11.7 Cola genrica con operaciones sincronizadas o e


15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: tail = p; notifyAll (); // A v s a l e a l o s que e s p e r a n , // e l o b j e t o f u e a n a d i d o

} public synchronized Object take ( ) throws I n t e r r u p t e d E x c e p t i o n { w h i l e ( head == n u l l ) wait ( ) ; // E s p e r a un o b j e t o en l a c o l a C e l l p = head ; // R e c u e r d a a l p r i m e r o b j e t o head = head . g e t N e x t ( ) ; // Q ut a l o de l a c o l a i f ( head == n u l l ) t a i l = null ; return p . getElement ( ) ; } }

La denicin de cada uno de los elementos de la cola se encuentra en el listao do 11.8.

Cdigo 11.8 Denicin de elementos de la cola o o


1: c l a s s C e l l { 2: private C e l l next ; 3: private Object element ; 4: public C e l l ( Object element ) { 5: this . element = element ; 6: } 7: public C e l l ( Object element , C e l l next ) 8: this . element = element ; 9: this . next = next ; 10: } 11: public Object getElement () { 12: return element ; 13: } 14: p u b l i c v o i d s e t E l e m e n t ( O b j e c t E l e m e nt ) 15: this . element = element ; 16: } 17: public C e l l getNext () { 18: return next ; 19: } 20: public void setNext ( C e l l next ) { 21: this . next = next ; 22: } 23: }

428

Hilos de ejecucin o

En el listado 11.9 tenemos la denicin concreta de lo que es un trabajo de o impresin. Se redene el mtodo toString() de Object para poder escribir el trabajo o e directamente en la impresora.

Cdigo 11.9 PrintJob: trabajo de impresin o o


1: p u b l i c c l a s s P r i n t J o b { 2: p r i v a t e i n t numLines ; 3: p r i v a t e S t r i n g name ; 4: p u b l i c P r i n t J o b ( S t r i n g name , i n t numLines ) { 5: t h i s . name = name ; 6: t h i s . numLines = numLines ; 7: } 8: public String toString () { 9: r e t u r n name . t r i m ( ) + " " + numLines + " lines printed " ; 10: } 11: }

Con todas las clases que usa el servidor de impresin ya denidas, en el listao do 11.10 mostramos el servidor de impresin propiamente dicho. o

Cdigo 11.10 Servidor de impresin que corre en un hilo propio de ejecucin o o o


1: import j a v a . u t i l . ; 2: 3: c l a s s P r i n t S e r v e r implements R u n n a b l e { 4: p r i v a t e Queue r e q u e s t s = new Queue ( ) ; 5: public PrintServer () { 6: new Thread ( t h i s ) . s t a r t ( ) ; 7: } 8: public void p r i n t ( PrintJob job ) { 9: r e q u e s t s . add ( j o b ) ; 10: } 11: public void run ( ) { 12: try { 13: for ( ; ; ) 14: r e a l P r i n t (( PrintJob ) requests . take ( ) ) ; 15: } catch ( I n t e r r u p t e d E x c e p t i o n e ) { 16: System . o u t . p r i n t l n ( "La impresora qued fuera de l nea " ) ; o 17: System . e x i t ( 0 ) ; 18: } 19: } 20: private void r e a l P r i n t ( PrintJob job ) { 21: System . o u t . p r i n t l n ( " Imprimiendo " + j o b ) ; 22: } 23: }

11.5 Comunicacin entre hilos de ejecucin o o

429

Finalmente, para simular un servidor de impresin que recibe y procesa trabao jos de impresin construimos la clase SistOperativo, que proporciona un entorno o en el cual el servidor de impresin se arranca para que empiece a funcionar. Esta o clase se muestra en el listado 11.11.

Cdigo 11.11 Entorno en el que funciona un servidor de impresin o o


1: c l a s s S i s t O p e r a t i v o extends Thread { 2: p r i v a t e P r i n t S e r v e r p r = new P r i n t S e r v e r ( ) ; 3: p r i v a t e i n t numJob =0; 4: public void run ( ) { 5: try { 6: while ( true ) { 7: numJob++; 8: // Se g e n e r a n t r a b a j o s con un n mero a r b i t r a r i o u 9: // de l n e a s p o r i m p r i m i r 10: p r . p r i n t ( new P r i n t J o b ( "Job \#" + numJob + ", " , 11: ( ( i n t ) Math . f l o o r ( Math . random ( ) 12: 10000)) + 1 ) ) ; 13: sleep (300); 14: } 15: } catch ( I n t e r r u p t e d E x c e p t i o n e ) { 16: System . o u t . p r i n t l n ( " Termin el trabajo " ) ; o 17: System . e x i t ( 0 ) ; 18: } 19: } 20: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 21: new S i s t O p e r a t i v o ( ) . s t a r t ( ) ; 22: } 23: }

Una ejecucin breve de este programa produce se puede ver en la gura 11.4 o en la siguiente pgina. a

11.5.2.

Ms detalles sobre la espera y la noticacin a o


Los mtodos wait que tienen tres variantes y los mtodos que corresponden e e a la noticacin notify() y notifyAll son mtodos de la clase Object y por lo o e tanto todas las clases los pueden invocar. Todos estos mtodos estn declarados e a nal por lo que no pueden ser modicados. Veamos las variantes que presentan: Mtodos de espera: e
p u b l i c f i n a l v o i d w a i t ( long t i m e o u t ) throws I n t e r r u p t e d E x c e p t i o n

El hilo de control en el que aparece el wait se duerme hasta que sucede una de cuatro cosas:

430

Hilos de ejecucin o

Figura 11.4

Salida que produce el servidor de impresin. o


% java SistOperativo Imprimiendo Job#1, 920 lines printed Imprimiendo Job#2, 8714 lines printed Imprimiendo Job#3, 2297 lines printed Imprimiendo Job#4, 799 lines printed Imprimiendo Job#5, 1966 lines printed Imprimiendo Job#6, 2439 lines printed Imprimiendo Job#7, 6808 lines printed Imprimiendo Job#8, 8270 lines printed Imprimiendo Job#9, 7093 lines printed Imprimiendo Job#10, 63 lines printed Imprimiendo Job#11, 2904 lines printed Imprimiendo Job#12, 5396 lines printed Imprimiendo Job#13, 8980 lines printed Imprimiendo Job#14, 6519 lines printed Imprimiendo Job#15, 4359 lines printed Imprimiendo Job#16, 2179 lines printed Imprimiendo Job#17, 9404 lines printed Imprimiendo Job#18, 8575 lines printed Imprimiendo Job#19, 2095 lines printed Imprimiendo Job#20, 6245 lines printed Imprimiendo Job#21, 4016 lines printed Imprimiendo Job#22, 9531 lines printed

Mtodos de espera: e
I.

(contina. . . ) u

Se invoca notify sobre este objeto y se selecciona al hilo de ejecucin o como ejecutable. II. Se invoca notifyAll sobre este objeto. III. El tiempo especicado para esperar expira. IV. Se invoca al mtodo interrupt despus veremos con ms detalle este e e a mtodo del hilo de ejecucin. e o Si timeout es cero, el proceso esperar indenidamente hasta que sea noticaa do que puede continuar. Durante la espera, se libera el candado del objeto, y ste es readquirido antes de que termine wait, no importa cmo o por e o qu termine. Si wait termina porque el hilo de ejecucin fue interrumpido, e o

11.5 Comunicacin entre hilos de ejecucin o o

431

el mtodo lanza una excepcin de InterruptedException. e o


p u b l i c f i n a l v o i d w a i t ( long t i m e o u t , i n t n a n o s )

Da la posibilidad de esperar tiempo en nanosegundos tambin. El proceso e dormir el nmero de timeout de milisegundos ms nanos nanosegundos. a u a
p u b l i c f i n a l v o i d w a i t ( ) throws I n t e r r u p t e d E x c e p t i o n

Equivalente a wait(0). Mtodos de noticacin: e o


public f i n a l void n o t i f y A l l ( )

Notica a todos los procesos que estn en estado de espera que alguna cona dicin cambi. Despertarn los procesos que puedan readquirir el candado o o a que estn esperando. a
public f i n a l void n o t i f y ( )

Notica a lo ms a un hilo esperando noticacin, pero pudiera suceder que a o el hilo noticado no estuviera esperando por la condicin que cambi de o o estado. Esta forma de noticacin slo se debe usar cuando hay seguridad o o de quin est esperando noticacin y por qu. e a o e Los mtodos de noticacin lo que hacen, de hecho, es ofrecer el candado e o que poseen a los mtodos que estn esperando. Si ningn mtodo est esperando, e a u e a o el que recibi la noticacin no est esperando ese candado en particular, la o o a noticacin se pierde. o Tanto los mtodos de espera como los de noticacin tienen que invocarse desde e o cdigo sincronizado, usando el candado del objeto sobre el cual son invocados. La o invocacin se puede hacer directamente desde el cdigo sincronizado, o bien desde o o mtodos invocados desde el cdigo sincronizado. Si se intenta invocar a cualquiera e o de estos dos mtodos cuando no se posee el candado del objeto, se presentar una e a excepcin IllegalMonitorStateException. o No hay manera de saber el porqu un objeto despierta, si es porque transe curri el tiempo especicado, o porque fue noticado de un cambio de estado. o Esto se puede averiguar si se obtiene el tiempo transcurrido durante la espera. Esto ultimo se puede lograr con el mtodo esttico currentTimeMillis() de la clase e a System, como mostramos en el listado 11.12 en la siguiente pgina. a

432

Hilos de ejecucin o

Cdigo 11.12 Para vericar tiempo transcurrido. o


......
1: p u b l i c s y n c h r o n i z e d O b j e c t t a k e ( ) 2: throws I n t e r r u p t e d E x c e p t i o n { 3: w h i l e ( head == n u l l ) { 4: long t i e m p o = System . c u r r e n t T i m e M i l l i s ( ) ; 5: wait (300); // E s p e r a un o b j e t o en l a c o l a 6: long t r a n s c u r r i d o = System . c u r r e n t T i m e M i l l i s ( ) t i e m p o ; 7: i f ( t r a n s c u r r i d o > 300) 8: System . e r r . p r i n t l n ( "Se despert sin que hubiera cambios " ) ; o 9: }

......

11.6 Alternativas para la programacin de procesos o


Hasta ahora, si hay varios procesos esperando para reanudar su ejecucin, es o impredecible el orden en que van a reanudar su ejecucin, ya que es la JVM la o que decide a cul proceso atender. Pero dentro de nuestros programas, algunas a tareas van a ser ms importantes que otras, o vamos a desear inuir, aunque sea a un poco, en que esas tareas entren a ejecucin antes que otras. o Se puede pensar en varios procesos ejecutndose simultneamente an cuando a a u la mquina tenga un solo procesador. Para ello, el sistema operativo (o en nuesa tro caso, la JVM) tiene pol ticas de distribucin del tiempo de procesador. Cada o proceso tiene una prioridad asociada, que da una indicacin de la importancia del o mismo. La pol tica del sistema puede ser que va a llevar a ejecucin a los procesos o con ms altas prioridad de entre los que estn en posibilidades de ejecutarse. El a a proceso seleccionado seguir ejecutndose hasta que desee ejecutar una operacin a a o que bloque la ejecucin (un wait, sleep o alguna operacin de entrada y salida). En o o ese momento se le quita el uso del procesador y se le da a otro proceso, ponindolo e a esperar. El sistema tambin pudiera tener la pol e tica de repartir el tiempo de procesador de manera equitativa, permitiendo a cada proceso ejecutarse un cierto lapso. En este caso, los procesos son desalojados del procesador an cuando estn u a en estado ejecutable, y simplemente esperarn hasta que les vuelva a tocar otro a lapso de ejecucin. o El que el sistema desaloje a un proceso depende ms de las pol a ticas del sis-

11.6 Alternativas para la programacin de procesos o

433

tema que del tipo de proceso. En general es aceptable asumir que el sistema le dar preferencia a los procesos con ms alta prioridad y que estn en estado de a a e poder ser ejecutados, pero no hay ninguna garant al respecto. La unica manera a de modicar estas pol ticas de desalojo es mediante la comunicacin de procesos. o La prioridad de un hilo de ejecucin es, en principio, la misma que la del o proceso que lo cre. Esta prioridad se puede cambiar con el mtodo public nal o e void setPriority(int newPriority) que asigna una nueva prioridad al hilo de ejecucin. o Esta prioridad es un valor entero tal que cumple MIN PRIORITY

newPriority MAX PRIORITY

La prioridad de un hilo que se est ejecutando puede cambiarse en cualquier a momento. En general, aquellas partes que se ejecutan continuamente en un sistema deben correr con prioridad menor que los que detectan situaciones ms raras, a como la alimentacin de datos. Por ejemplo, cuando un usuario oprime el botn o o de cancelar para un proceso, quiere que ste termine lo antes posible. Sin embargo, e si se estn ejecutando con la misma prioridad, puede pasar mucho tiempo antes a de que el proceso que se encarga de cancelar tome el control. En general es preferible usar prioridades cercanas a NORM PRIORITY para evitar comportamientos extremos en un sistema. Si un proceso tiene la prioridad ms alta posible, puede suceder que evite que cualquier otro proceso se ejecute, a una situacin que se conoce como hambruna (starvation). o

11.6.1.

Desalojo voluntario
Hay varios mtodos mediante los cuales un programa puede desalojarse a e s mismo del procesador. Todos estos mtodos de la clase Thread son estticos. e a Veamos cules son. a public static void sleep(long millis) throws InterruptedException Pone a dormir al hilo de ejecucin en cuestin al menos por el tiempo especio o cado. Sin embargo, no hay garant de que sea exactamente este tiempo, ya a que las pol ticas de programacin pudieran no permitir que el proceso regreo se al procesador en cuando despierta. Si el proceso es interrumpido mientras est durmiendo, se termina y lanza una excepcin InterruptedException. a o public static void sleep(long millis, int nanos) throws InterruptedException Lo mismo que el anterior, pero se pueden agregar nanosegundos, que estarn a en el rango 0-999999.

434

Hilos de ejecucin o

public static void yield() Le indica al programador de procesos que est dispuesto a ser desalojado, a ya que la tarea que est realizando no tiene que continuar en este momento. a El programador de procesos decide si lo desaloja o no. Ya hemos utilizado el mtodo sleep. En la clase del listado 11.13 mostramos e un ejemplo de yield. Esta clase se ejecuta dndole en la l a nea de comandos varios argumentos.

Cdigo 11.13 Desalojo voluntario o


1: c l a s s B a b b l e extends Thread { 2: s t a t i c boolean d e c l i n a ; // D e c l i n a r a o t r o s p r o c e s o s ? 3: s t a t i c i n t howOften ; // c u a n t a s v e c e s i m p r i m i r 4: p r i v a t e S t r i n g word ; 5: 6: B a b b l e ( S t r i n g whatToSay ) { 7: word = whatToSay ; 8: } 9: 10: public void run ( ) { 11: f o r ( i n t i = 0 ; i < howOften ; i ++) { 12: System . o u t . p r i n t l n ( word ) ; 13: if ( declina ) 14: Thread . y i e l d ( ) ; // Deja que o t r o s p r o c e s o s 15: // s e e j e c u t e n 16: } 17: } 18: 19: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 20: d e c l i n a = new B o o l e a n ( a r g s [ 0 ] ) . b o o l e a n V a l u e ( ) ; 21: howOften = I n t e g e r . p a r s e I n t ( a r g s [ 1 ] ) ; 22: 23: // Crea un h i l o de e j e c u c i n p a r a cada p a l a b r a o 24: f o r ( i n t i = 2 ; i < a r g s . l e n g t h ; i ++) 25: new B a b b l e ( a r g s [ i ] ) . s t a r t ( ) ; 26: } 27: }

Podemos ver dos ejecuciones con l neas de comandos distintas, una que provoca el desalojo y la otra no. Sin embargo, en el sistema en que probamos este pequeo n programa, el resultado es el mismo. Deber amos ejecutarlo en varios sistemas para ver si el resultado cambia de alguna manera. Veamos los resultados en la gura 11.5 en la pgina opuesta. a

11.7 Abrazo mortal (deadlock )

435

Figura 11.5

Ejecucin con desalojo voluntario. o


% java Babble false 2 Did DidNot Did Did DidNot DidNot % java Babble true 2 Did DidNot Did Did DidNot DidNot

11.7 Abrazo mortal (deadlock)


Siempre que se tienen dos hilos de ejecucin y dos objetos con candados se o puede llegar a una situacin conocida como abrazo mortal, en la cual cada proceso o posee el candado de uno de los objetos y est esperando adquirir el candado del a otro proceso. Si el objeto X tiene un mtodo sincronizado que invoca a un mtodo e e sincronizado del objeto Y, quien a su vez tiene a un mtodo sincronizado invocando e a un mtodo sincronizado de X, cada proceso se encontrar esperando a que el e a otro termine para poder continuar, y ninguno de los dos va a poder hacerlo. En el listado 11.14 mostramos una clase Apapachosa en la cual un amigo, al ser apapachado, insiste en apapachar de regreso a su compaero. n

Cdigo 11.14 Posibilidad de abrazo mortal o


1: c l a s s Apapachosa { 2: p r i v a t e Apapachosa amigo ; 3: p r i v a t e S t r i n g nombre ; 4: p u b l i c Apapachosa ( S t r i n g nombre ) { 5: t h i s . nombre = nombre ; 6: } 7: p u b l i c s y n c h r o n i z e d v o i d apapacha ( ) { 8: System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + 9: " en " + nombre + ". apapacha () tratando de " + 10: " invocar a " + amigo . nombre + ". reApapacha ()" ) ; 11: amigo . r e A p a p a c h a ( ) ; 12: }

1/2

436

Hilos de ejecucin o 2/2

Cdigo 11.14 Posibilidad de abrazo mortal o


13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: } p r i v a t e synchronized void reApapacha ( ) { System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ()+ " en " + nombre + ". reApapacha ()" ) ; } p u b l i c v o i d seAmigo ( Apapachosa amiga ) t h i s . amigo = amiga ; } {

p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { f i n a l Apapachosa l u p e = new Apapachosa ( "Lupe" ) ; f i n a l Apapachosa j u a n a = new Apapachosa ( " Juana " ) ; l u p e . seAmigo ( j u a n a ) ; j u a n a . seAmigo ( l u p e ) ; new Thread ( new R u n n a b l e ( ) { p u b l i c v o i d r u n ( ) { l u p e . apapacha ( ) ; } } , " Hilo1 " ) . s t a r t ( ) ; new Thread ( new R u n n a b l e ( ) { public void run ( ) { j u a n a . apapacha ( ) ; } , " Hilo2 " ) . s t a r t ( ) ; }

Dependiendo del encargado de la programacin, pudiera suceder que lupe tero mine antes que empiece juana. Pero esto no lo podemos garantizar. Una ejecucin o en nuestra mquina logr lo que acabamos de mencionar, y produjo la salida que a o se muestra en la gura 11.6.

Figura 11.6

Ejecucin con la posibilidad de abrazo mortal. o


% java Apapachosa Hilo1 en Lupe.apapacha() tratando de invocar a Juana.reApapacha() Hilo1 en Juana.reApapacha() Hilo2 en Juana.apapacha() tratando de invocar a Lupe.reApapacha() Hilo2 en Lupe.reApapacha()

En las corridas que hicimos en nuestra mquina siempre pudo Lupe terminar a su intercambio antes de que Juana intentara el suyo. Sin embargo, pudiera suceder

11.7 Abrazo mortal (deadlock )

437

que en otro sistema, o si la JVM tuviera que atender a ms hilos de ejecucin, ese a o programita bloqueara el sistema cayendo en abrazo mortal. Introduciendo un enunciado wait dentro del mtodo reaApapacha conseguimos e que el proceso caiga en un abrazo mortal. Se lanza el hilo de ejecucin con lupe, o pero como tiene que esperar cuando llega a reApapacha(), esto permite que tambin e el hilo de ejecucin con juana se inicie. Esto hace que ambos procesos se queden o esperando a que el otro libere el candado del objeto, pero para liberarlo tienen que terminar la ejecucin de reApapacha, lo ninguno de los dos puede hacer cada o uno est esperando a que termine el otro. La aplicacin modicada se muestra a o en el listado 11.15 y en la gura 11.7 en la siguiente pgina se muestra cmo se a o queda pasmado el programa, sin avanzar ni terminar, hasta que se teclea un C.

Cdigo 11.15 Abrazo mortal entre hilos de ejecucin o o

1/2

1: c l a s s Apapachosa2 { 2: p r i v a t e Apapachosa2 amigo ; 3: p r i v a t e S t r i n g nombre ; 4: p r i v a t e O b j e c t candado = new O b j e c t ( ) ; 5: 6: p u b l i c Apapachosa2 ( S t r i n g nombre ) { 7: t h i s . nombre = nombre ; 8: } 9: 10: p u b l i c s y n c h r o n i z e d v o i d apapacha ( ) { 11: System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + 12: + " en " + nombre 13: + ". apapacha () tratando " 14: + " de invocar " + amigo . nombre 15: + ". reApapacha ()" ) ; 16: amigo . r e A p a p a c h a ( ) ; 17: } 18: 19: p r i v a t e synchronized void reApapacha ( ) { 20: try { 21: wait(3); 22: System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) 23: + " en " + nombre + ". reApapacha ()" ) ; 24: } catch(InterruptedException e) { } 25: } 26: 27: p u b l i c v o i d hazteAmigo ( Apapachosa2 amigo ) { 28: t h i s . amigo = amigo ; 29: }

438

Hilos de ejecucin o 2/2

Cdigo 11.15 Abrazo mortal entre hilos de ejecucin o o


30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: } p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { f i n a l Apapachosa2 l u p e = new Apapachosa2 ( "Lupe" ) ; f i n a l Apapachosa2 j u a n a = new Apapachosa2 ( " Juana " ) ; l u p e . hazteAmigo ( j u a n a ) ; j u a n a . hazteAmigo ( l u p e ) ; new Thread ( new R u n n a b l e ( ) { public void run ( ) { l u p e . apapacha ( ) ; } } , " Hilo1 " ) . s t a r t ( ) ; new Thread ( new R u n n a b l e ( ) { p u b l i c v o i d r u n ( ) { j u a n a . apapacha ( ) ; } } , " Hilo2 " ) . s t a r t ( ) ; }

Como se puede ver en las l neas 22, 23 y 26, lo unico que se le agreg a esta clase o es que una vez dentro del mtodo reApapacha espere 3 milisegundos, liberando el e candado. Este es un tiempo suciente para que el otro hilo de ejecucin se apodere o del candado. Ac es cuando se da el abrazo mortal, porque el primer hilo no puede a terminar ya que est esperando a que se libere el candado, pero el segundo hilo a tampoco puede continuar porque el primero no ha terminado.

Figura 11.7

Ejecucin de Apapachosa2 con abrazo mortal o


elisa@lambda ...ICC1/progs/threads % java Apapachosa2 Hilo1 en Lupe.apapacha() tratando de invocar Juana.reApapacha() Hilo2 en Juana.apapacha() tratando de invocar Lupe.reApapacha() elisa@lambda ...ICC1/progs/threads % Si bien nos cost trabajo lograr el abrazo mortal, en un entorno donde se estn o a ejecutando mltiples aplicaciones a la vez no podr u amos predecir que el segundo hilo de ejecucin no se apoderara del candado del objeto antes de que el primer o hilo lograra llegar a ejecutar el mtodo reApapacha, por lo que tendr e amos que hacer algo al respecto. Es responsabilidad del cliente evitar que haya abrazos mortales, asegurndose a del orden en que se ejecutan los distintos mtodos y procesos. Esto lo conseguir el e a programador usando enunciados wait, yield, notify, notifyAll y sincronizando alrededor de uno o ms objetos, para conseguir que un proceso espere siempre a otro. a

11.8 Cmo se termina la ejecucin de un proceso o o

439

El tema de abrazos mortales es de gran importancia e inters, y proporciona e un campo de estudio e investigacin muy frtil. Siendo ste un curso introductorio o e e no creemos necesario verlo ms a fondo y nos conformamos con haber creado la a conciencia de situaciones anmalas que se pueden presentar. o

11.8 Cmo se termina la ejecucin de un proceso o o


Un proceso en general puede presentar los siguientes estados: vivo: Un mtodo est vivo desde el momento que es iniciado con start() y hasta e a que termina su ejecucin. o activo: Un mtodo est activo si est siendo ejecutado por el procesador. e a a ejecutable: Na hay ningn obstculo para que sea ejecutado, pero el procesador u a no lo ha atendido. esperando: Se encuentra esperando a que alguna condicin se cumpla o se le o notique de algn evento, por lo que el procesador no lo puede ejecutar. u Un mtodo deja de estar vivo, como ya dijimos, por cualquiera de las causas e que siguen: El mtodo run ejecuta un return o llega al nal de su denicin. e o El mtodo run termina abruptamente. e Se invoca al mtodo destroy sobre ese proceso. e Es posible que se detecte que algo mal est sucediendo en cierto proceso y se a desee terminar su ejecucin. Sin embargo, destroy deber ser un ultimo recurso, ya o a que si bien se consigue que el proceso termine, pudiera suceder que no se liberen los candados que posee el proceso en cuestin y se queden en el sistema otros o procesos bloqueados para siempre esperando candados que ya nunca van a poder ser liberados. Es tan drstico y peligroso este mtodo para el sistema en general a e que muchas mquinas virtuales han decidido no implementarlo y simplemente a lanzan una excepcin NoSuchMethodError, que termina la ejecucin del hilo. Esto o o sucede si modicamos el mtodo apapacha como se muestra en el listado 11.16 en e la siguiente pgina. a

440

Hilos de ejecucin o

Cdigo 11.16 Uso de destroy para terminar un hilo de ejecucin o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: p u b l i c s y n c h r o n i z e d v o i d apapacha ( ) { System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) + " en " + nombre + ". apapacha () tratando " + " de invocar " + amigo . nombre + ". reApapacha ()" ) ; i f ( Thread . c u r r e n t T h r e a d ( ) . getName ( ) . e q u a l s ( " Hilo2 " ) ) { System . o u t . p r i n t l n ( " Entr a destroy " ) ; e Thread . c u r r e n t T h r e a d ( ) . d e s t r o y ( ) ; } amigo . r e A p a p a c h a ( ) ; }

Figura 11.8

Implementacin de destroy en la mquina virtual de Java. o a


elisa@lambda ...ICC1/progs/threads % java Apapachosa2 Hilo1 en Lupe.apapacha() tratando de invocar Juana.reApapacha() Hilo2 en Juana.apapacha() tratando de invocar Lupe.reApapacha() Entr a destroy e java.lang.NoSuchMethodErrorHilo1 en Juana.reApapacha() at java.lang.Thread.destroy(Thread.java:709) at Apapachosa2.apapacha(Apapachosa2.java:20) at Apapachosa2$2.run(Apapachosa2.java:49) at java.lang.Thread.run(Thread.java:484) La ejecucin de Apapachosa2 con la redenicin de este mtodo produce la o o e salida que se observa en la gura 11.8. Cuando un programa termina por cualquier razn, en ese momento terminan o todos los procesos lanzados por este programa.

11.8.1.

Cancelacin de un proceso o
En ocasiones es deseable poder cancelar un proceso que est trabajando. Por a ejemplo, si estamos trabajando con interfaces grcas y el usuario oprime el botn a o de cancelar deseamos poder terminar el proceso de una manera adecuada. Para poder pedir la cancelacin de un proceso debemos poder interrumpirlo para que o escuche la orden de terminacin. o La clase Thread tiene dos mtodos asociados a la interrupcin de un proceso, la e o solicitud de interrupcin y la consulta de si el proceso ha sido o no interrumpido. o

11.8 Cmo se termina la ejecucin de un proceso o o

441

La solicitud de interrupcin se hace mediante el enunciado o


// En e l h i l o 1 thread2 . i n t e r r u p t ()

desde un mtodo distinto que el que se desea interrumpir. En el mtodo por ine e terrumpir deberemos tener una iteracin que en cada vuelta est preguntando si o e ha sido o no interrumpido:
// En e l h i l o 2 while ( ! i n t e r r u p t e d ( ) ) { // haz e l t r a b a j o p l a n e a d o }

Si tenemos ms de un proceso ejecutndose y se le env una seal de interrupa a a n cin a uno de los procesos, el otro proceso proseguir, aduendose del procesador. o a na Al interrumpir a un proceso, los candados que posea el objeto se liberan, por lo que ste puede ser un mecanismo necesario para terminar con un abrazo mortal, e an a costa de uno de los procesos. u Veamos una aplicacin de dos hilos de ejecucin encargados de escribir un o o punto en la pantalla cada determinado tiempo. En un momento dado el programa principal decide interrumpir a uno de las procesos, y en ese momento el otro contina su ejecucin ya sin ceder el procesador a nadie ms. Podemos ver este u o a programa en el listado 11.17.

Cdigo 11.17 Interrupcin de procesos o o


1: c l a s s T i c k extends Thread { 2: i n t count ; 3: long p a u s e ; 4: 5: p u b l i c T i c k ( i n t count , long pauseTime , S t r i n g name ) 6: super ( name ) ; 7: t h i s . count = count ; 8: p a u s e = pauseTime ; 9: } 10: 11: public void run ( ) { 12: t i c k ( count , p a u s e ) ; 13: }

1/2

442

Hilos de ejecucin o 2/2

Cdigo 11.17 Interrupcin de procesos o o


14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: } s y n c h r o n i z e d v o i d t i c k ( i n t count , long pauseTime ) { System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) ) ; try { f o r ( i n t i = 0 ; i < c o u n t ; i ++) { System . o u t . p r i n t l n ( c u r r e n t T h r e a d ( ) . getName ( ) + " >." ) ; System . o u t . f l u s h ( ) ; Thread . s l e e p ( pauseTime ) ; } } catch ( I n t e r r u p t e d E x c e p t i o n e ) { System . o u t . p r i n t l n ( "Se interrumpi " + o c u r r e n t T h r e a d ( ) . getName ( ) ) ; } } p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { T i c k h i l o 1 = new T i c k ( 2 0 , 1 0 0 , "Hilo 1" ) ; T i c k h i l o 2 = new T i c k ( 1 0 , 1 0 0 , " Hilo2 " ) ; hilo1 . start (); hilo2 . start (); System . o u t . p r i n t l n ( " Desde el programa principal " ) ; try { s l e e p ( 3 0 0 ) ; // Deja que t r a b a j e n un r a t o } catch ( I n t e r r u p t e d E x c e p t i o n e ) { hilo1 . interrupt (); } }

La diferencia principal entre este mtodo de interrupcin o interrumpir con un e o C, por ejemplo, es que en este ultimo caso estamos interrumpiendo al programa principal, por lo que todos los procesos que fueron lanzados desde l se dan por e aludidos, mientras que si mandamos un mensaje de interrupcin a un solo proceso, o ste es el unico que cachar la excepcin y suspender su ejecucin. La salida que e a o a o produce esta aplicacin se puede ver en la gura 11.9 en la pgina opuesta. o a La ejecucin del proceso hilo 1 se interrumpe porque al estar dormido (ejecuo tando un sleep) el proceso sale de este estado al recibir la seal de interrupcin. n o Entonces, el proceso terminar. a Un enunciado interrupt no va a tener ningn efecto si el proceso correspondiente u no est en un estado de espera (wait o sleep). Tiene efecto en estos estados porque a estos mtodos lanzan la excepcin InterruptedException, pero no porque de hecho e o se suspenda inmediatamente el mismo.

11.8 Cmo se termina la ejecucin de un proceso o o

443

Figura 11.9

Interrupcin de un hilo de ejecucin desde el programa principal. o o


elisa@lambda ...ICC1/progs/threads % java Tick Thread[Hilo 1,5,main] Hilo 1>. Desde el programa principal Thread[Hilo2,5,main] Hilo2>. Hilo 1>. Hilo2>. Hilo 1>. Hilo2>. Se interrumpi Hilo 1 o Hilo2>. Hilo2>. Hilo2>. Hilo2>. Hilo2>. Hilo2>. Hilo2>. elisa@lambda ...ICC1/progs/threads % Si el programa solicita la interrupcin de un proceso es muy peligroso hacer o caso omiso de esta solicitud, cachando la excepcin y no haciendo nada al reso pecto. En algunas ocasiones se usa interrupt() para desbloquear a un proceso que est esperando algo, y al no responder en ninguna forma a la excepcin de hecho a o el proceso no se interrumpe.

Cdigo 11.18 Signicado de la interrupcin en un proceso o o


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: s y n c h r o n i z e d v o i d t i c k ( i n t count , long pauseTime ) { System . o u t . p r i n t l n ( c u r r e n t T h r e a d ( ) ) ; f o r ( i n t i = 0 ; i c o u n t ; i ++) { try { System . o u t . p r i n t l n ( c u r r e n t T h r e a d ( ) . getName ( ) +" >." ) ; System . o u t . f l u s h ( ) ; s l e e p ( pauseTime ) ; } catch ( I n t e r r u p t e d E x c e p t i o n e ) { System . o u t . p r i n t l n ( "Se interrumpi " o + c u r r e n t T h r e a d ( ) . getName ( ) ) ; } } }

444

Hilos de ejecucin o

Si en el mtodo tick del ejemplo que estamos manejando intercambiamos el e try con la iteracin, como se muestra en el listado 11.18 en la pgina anterior, o a el proceso recibe la seal de interrupcin en una de las iteraciones, pero como n o no hace nada al momento de cacharla respecto al hilo de ejecucin, al regresar o una vez ms a la iteracin resulta que el hilo supuestamente suspendido no fue a o suspendido y sigue compartiendo el procesador con el otro hilo.

11.8.2.

Terminacin de la ejecucin de un proceso o o


Una de las razones que puede tener un programa para lanzar un proceso paralelo es la necesidad de hacer clculos complejos, que pudieran hacerse simultneaa a mente. Sin embargo, muchas veces no se sabe cul de los dos clculos se va a a a tardar ms, por lo que el proceso principal tendr que esperar a que termine, en a a su caso, el proceso que lanz antes de poder utilizar los resultados del mismo. o Java tiene el mtodo join() de la clase Thread que espera a que el proceso con e el que se invoca el mtodo termine antes de que se proceda a ejecutar la siguiente e l nea del programa. Veamos en los listados 11.19 y 11.20 en la pgina opuesta un a ejemplo en el quesupuestamente se desean hacer dos clculos que pueden llevarse a a cabo de manera simultnea. El mtodo principal (main) crea un proceso paralelo a e para que se efecte uno de los clculos mientras l ejecuta el otro. Una vez que u a e termina de ejecutar su propio clculo, se sienta a esperar hasta que el proceso a que lanz termine. o

Cdigo 11.19 Espera para la terminacin de un coproceso o o


1: c l a s s C a l c T h r e a d extends Thread { 2: p r i v a t e double r e s u l t ; 3: public void run ( ) { 4: result = calculate (); 5: } 6: p u b l i c double g e t R e s u l t ( ) { 7: return r e s u l t ; 8: } 9: p u b l i c double c a l c u l a t e ( ) { 10: i n t t o p e =(( i n t ) Math . f l o o r ( Math . random ( ) 1 0 0 0 0 0 ) ) ; 11: f o r ( i n t i = 0 ; i < t o p e ; i ++); 12: r e t u r n ( ( double ) System . c u r r e n t T i m e M i l l i s ( ) ) ; 13: } 14: }

En el programa principal, desde el que se lanza el coproceso del clculo, una a

11.8 Cmo se termina la ejecucin de un proceso o o

445

vez hecha la tarea que se pod realizar de manera simultnea, se espera para a a garantizar que el coproceso termin. Si esto no se hace as pudiera suceder que o el valor que se desea calcule el coproceso no estuviera listo al terminar el proceso principal con su propio trabajo.

Cdigo 11.20 Programa principal para ejemplicar la espera o


1: c l a s s S h o w J o i n s { 2: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 3: long a n t e s , d e s p u e s ; 4: C a l c T h r e a d c a l c = new C a l c T h r e a d ( ) ; 5: calc . start (); 6: i n t t o p e =(( i n t ) Math . f l o o r ( Math . random ( ) 1 0 0 0 0 ) ) ; 7: f o r ( i n t i =0; i < t o p e ; i ++); 8: try { 9: a n t e s = System . c u r r e n t T i m e M i l l i s ( ) ; 10: calc . join (); 11: d e s p u e s = System . c u r r e n t T i m e M i l l i s ( ) ; 12: System . o u t . p r i n t l n ( " Esper "+( d e s p u e s a n t e s ) + e 13: " milisegundos " ) ; 14: System . o u t . p r i n t l n ( " result vale " + 15: calc . getResult ()); 16: } catch ( I n t e r r u p t e d E x c e p t i o n e ) { 17: System . o u t . p r i n t l n ( "No hay respuesta : fue interrumpido " ) ; 18: } 19: } 20: }

Otra manera de esperar a que un proceso termine antes de continuar con un cierto hilo de ejecucin es mediante el mtodo isAlive() de la clase Thread, que nos o e indica si el mtodo todav no ha llegado a su nal, ya sea porque fue interrumpido e a o porque termin su tarea. Si eligiramos usar esta forma de sincronizacin, el o e o programa del listado 11.20 se ver como se muestra en el listado 11.21 en la a siguiente pgina, con la parte que cambi en negritas. a o En la l nea 11 del listado 11.21 en la siguiente pgina todo lo que hace el a programa es estar en un ciclo hasta que la condicin del ciclo se vuelva falsa. Como o la condicin es que el hilo de ejecucin calc est vivo, el programa podr salir del o o e a ciclo cuando el proceso calc deje de estar vivo, o sea cuando termine.

446

Hilos de ejecucin o

Cdigo 11.21 Vericacin de terminacin con isAlive() o o o


1: c l a s s S h o w A l i v e { 2: p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) { 3: long a n t e s , d e s p u e s ; 4: C a l c T h r e a d c a l c = new C a l c T h r e a d ( ) ; 5: calc . start (); 6: i n t t o p e =(( i n t ) Math . f l o o r ( Math . random ( ) 1 0 0 0 0 ) ) ; 7: f o r ( i n t i =0; i < t o p e ; i ++); // e s p e r a 8: a n t e s = System . c u r r e n t T i m e M i l l i s ( ) ; 9: while (calc.isAlive()); 10: d e s p u e s = System . c u r r e n t T i m e M i l l i s ( ) ; 11: System . o u t . p r i n t l n ( " Esper "+( d e s p u e s a n t e s ) e 12: + " milisegundos " ) ; 13: System . o u t . p r i n t l n ( " result vale " + c a l c . g e t R e s u l t ( ) ) ; 14: } 15: }

Que un proceso no est ejecutndose no quiere decir que el proceso no est vivo. e a e Un proceso est vivo si est en espera, ya sea de alguna noticacin, de que a a o transcurra algn tiempo, o de que la JVM contine ejecutndolo. Por lo tanto, an u u a u cuando un proceso sea interrumpido por alguna de las condiciones que acabamos de mencionar, el proceso seguir vivo mientras no termine su ejecucin. a o

11.9 Terminacin de la aplicacin o o


Nos surge una pregunta natural respecto a la relacin que existe entre los o procesos lanzados desde una aplicacin, y el proceso que corresponde a la aplicao cin misma: qu pasa si el proceso principal termina antes de que terminen los o e procesos que fueron lanzados por l? e La respuesta es ms simple de lo que se piensa: el proceso principal no termina a hasta que hayan terminado todos los procesos que fueron lanzados por l. Bajo e terminar nos referimos a dejar de estar vivo, no forzosamente a estar ejecutando algo. Esta situacin, pensndolo bien, es lo natural. El proceso principal es aquel o a cuyo mtodo main se invoc. El resto de los procesos fueron invocados utilizando e o start(). Esa es la unica diferencia que hay entre ellos. Al lanzar procesos se genera una estructura como de cacto, donde cada proceso tiene su stack de ejecucin, que o es una continuacin del stack de ejecucin del proceso que lo lanz. Por lo tanto, o o o

11.9 Terminacin de la aplicacin o o

447

no puede terminar un proceso hasta en tanto todos los procesos que dependen de l (que montaron su stack de ejecucin encima) hayan terminado. e o En Java hay otro tipo de procesos llamados demonios. Un demonio es un proceso lanzado desde otro y cuya tarea es realizar acciones que no forzosamente tienen mucho que ver con el proceso que los lanz. En el caso de los demonios, o cuando termina el proceso que los lanz, en ese momento y abruptamente se o interrumpen todos los demonios lanzados por ese proceso. Es como si aplicramos a un destroy() a todos los demonios lanzados por el proceso que termina. Resumiendo, hay dos tipos de procesos que se pueden lanzar desde otro proceso cualquiera: los hilos de ejecucin normales y los demonios. El proceso lanzador o no puede terminar hasta en tanto los hilos de ejecucin iniciados por l no tero e minen, mientras que los demonios se suspenden abruptamente cuando el proceso que los lanz termina. o Un proceso se puede hacer demonio simplemente aplicndole el mtodo setDaea e mon(true) al hilo de ejecucin antes de que ste inicie su ejecucin. Los procesos o e o iniciados por un demonio son, a su vez, demonios. Veamos un ejemplo de la diferencia entre procesos normales y demonios en el listado 11.22.

Cdigo 11.22 Diferencia entre procesos normales y demonios o


1: import j a v a . i o . ; 2: 3: c l a s s Daemon extends Thread { 4: p r i v a t e s t a t i c f i n a l i n t SIZE = 1 0 ; 5: p r i v a t e Thread [ ] t = new Thread [ SIZE ] ; 6: 7: p u b l i c Daemon ( ) { 8: setDaemon ( t r u e ) ; 9: start (); 10: } 11: 12: public void run ( ) { 13: f o r ( i n t i = 0 ; i < SIZE ; i ++) 14: t [ i ] = new DaemonSpawn ( i ) ; 15: f o r ( i n t i = 0 ; i < SIZE ; i ++) 16: System . o u t . p r i n t l n ( "t[" + i +"]. isDaemon () = " + 17: t [ i ] . isDaemon ( ) ) ; 18: while ( true ) 19: yield (); 20: } 21: }

1/2

448

Hilos de ejecucin o 2/2

Cdigo 11.22 Diferencia entre procesos normales y demonios o


22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: c l a s s DaemonSpawn extends Thread { p u b l i c DaemonSpawn ( i n t i ) { System . o u t . p r i n t l n ( " DaemonSpawn " + i + " lanzado " ) ; start (); } public void run ( ) { while ( true ) y i e l d ( ) ; } }

p u b l i c c l a s s Daemons { p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) } throws I O E x c e p t i o n { Thread d = new Daemon ( ) ; System . o u t . p r i n t l n ( "d. isDaemon () = " + d . isDaemon ( ) ) ; } }

Para ver los distintos efectos que tiene el que los procesos lanzados sean o no demonios, vamos a ejecutar la clase Daemon como est. La teor es que al a a terminar de ejecutarse el mtodo main de esta clase, los demonios suspendern e a tambin su funcionamiento. Y en efecto as es. La ejecucin la podemos ver en la e o gura 11.10 en la pgina opuesta. a Mostramos dos ejecuciones de la misma aplicacin. En la primera ejecucin, o o se alcanz a lanzar 5 demonios antes de que el proceso principal terminara. En la o segunda ejecucin, unicamente se alcanzaron a lanzar 2 demonios. El nmero de o u demonios que se logre lanzar depender de las pol a ticas de atencin de la JVM. o Como el proceso Daemon es, a su vez, un demonio, en cuanto llega el proceso principal al nal, se suspende abruptamente la ejecucin de todos los demonios o que se hayan iniciado desde l, o desde procesos o demonios iniciados por l. e e Si cambiamos la l nea 8: de esta aplicacin para que Daemon sea un proceso o comn y corriente con la l u nea
8: setDaemon ( f a l s e ) ;

obtenemos la salida que se muestra en la gura 11.10 en la pgina opuesta. a

11.10 Depuracin en hilos de ejecucin o o

449

Figura 11.10

Terminacin de procesos que son demonios o


elisa@lambda ...ICC1/progs/threads % java Daemons d.isDaemon() = true DaemonSpawn 0 iniciado DaemonSpawn 1 iniciado DaemonSpawn 2 iniciado DaemonSpawn 3 iniciado DaemonSpawn 4 iniciado elisa@lambda ...ICC1/progs/threads % java Daemons d.isDaemon() = true DaemonSpawn 0 iniciado DaemonSpawn 1 iniciado elisa@lambda ...ICC1/progs/threads % Si adems agregamos la l a nea que sigue
setDaemon ( t r u e ) ;

entre las l neas 37: y 38: de la clase DaemonSpawn, la ejecucin de Daemon tiene o que terminar antes de que la aplicacin pueda hacerlo, por lo que se alcanzan a o lanzar todos los demonios. Como no hay ninguna manera de que termine el proceso Daemon, tambin el proceso principal nunca termina, hasta que tecleamos ctrl-C. e Si en las l neas 18: y 19: de la clase Daemon quitamos el ciclo y unicamente tenemos yield(), el proceso termina normalmente en el momento en que termina el proceso de la clase Daemon. Veamos la salida en la gura 11.11 en la siguiente pgina. a Dependiendo de la programacin particular que d la JVM, podr suceder o e a que alguno de los hilos de ejecucin causara una excepcin, o que al suspenderse o o alguno de los demonios abruptamente lanzara una excepcin. Esto resulta en o que ejecuciones de la misma aplicacin puedan dar distintos resultados cuando o estamos trabajando con hilos de ejecucin, sobre todo si no se ejerce ninguna o sincronizacin, como es el caso del ejemplo con el que estamos trabajando. o

11.10 Depuracin en hilos de ejecucin o o


Java proporciona dos mtodos a utilizar cuando hay problemas con la ejecucin e o de coprocesos. Estos son:

450
public String toString

Hilos de ejecucin o

Regresa la representacin en cadena del hilo de ejecucin. Esta representao o cin tiene el nombre del hilo de ejecucin, su prioridad y el grupo al que o o pertenece. public static void dumpStack() Imprime el stack de ejecucin para el coproceso en cuestin. Manda la salida o o a System.out.

Figura 11.11

Terminacin de procesos o
elisa@lambda ...ICC1/progs/threads % java Daemons d.isDaemon() = false DaemonSpawn 0 iniciado DaemonSpawn 1 iniciado DaemonSpawn 2 iniciado DaemonSpawn 3 iniciado DaemonSpawn 4 iniciado DaemonSpawn 5 iniciado DaemonSpawn 6 iniciado DaemonSpawn 7 iniciado DaemonSpawn 8 iniciado DaemonSpawn 9 iniciado t[0].isDaemon() = true t[1].isDaemon() = true t[2].isDaemon() = true t[3].isDaemon() = true t[4].isDaemon() = true t[5].isDaemon() = true t[6].isDaemon() = true t[7].isDaemon() = true t[8].isDaemon() = true t[9].isDaemon() = true elisa@lambda ...ICC1/progs/threads

11.11 Otros temas relacionados con hilos de ejecucin o

451

11.11 Otros temas relacionados con hilos de ejecucin o


Entre los temas que ya no veremos en este cap tulo por considerarlos ms a apropiados para cursos posteriores, podemos mencionar: Grupos: Cada hilo de ejecucin pertenece a un grupo de hilos de ejecucin con o o sus caracter sticas particulares. Java proporciona casi un mtodo para el e trabajo en grupo por cada mtodo para el trabajo con un hilo de ejecucin. e o Los grupos pueden modicar la prioridad con la que se ejecuta un proceso, a qu otros hilos de ejecucin tiene acceso, etc. e o Excepciones en hilos de ejecucin: Al igual que en una aplicacin comn y o o u corriente, se pueden dar excepciones dentro de un hilo de ejecucin. Si la o excepcin no es manejada a ese nivel, se propagar a niveles superiores. o a La gran diferencia es que una vez que hay una excepcin en un hilo de o ejecucin, ese hilo se aborta y no se va a poder obtener informacin sobre o o l desde fuera, ya que ya no va a existir. Bajo este tema se revisan bien los e mecanismos para poder saber con ms certeza que est pasando y el por a a qu de las excepciones. e variables volatile: Esto se reere a variables que para el compilador pudieran parecer como que no se modican de un enunciado a otro, pero con la existencia de hilos de ejecucin paralelos, pudieran ser modicadas por otros o procesos. Se marcan las variables como volatile para obligar al compilador a consultar el valor de la variable, y no asumir que no ha cambiado desde la ultima vez que la vio; el efecto es que no permite al compilador hacer optimizaciones que pudieran falsear el verdadero valor de una variable en un momento dado. Esperamos haber proporcionado una visin del potencial que tienen los hilos o de ejecucin en Java. Una vez que estn realizando programacin en serio, y en el o e o ambiente actual de procesos en red, tendrn que usarlos. a

Bibliograf a
[1] Ken Arnold and James Gosling. The Java Programming Language Third Edition. Addison-Wesley, 2001. [2] Jos Galaviz Casas. Elogio a la pereza. V e nculos Matemticos, Nm. 8, 2001. a u [3] Sun Corporation. The source for java technology. web page. [4] Elliotte Rusty Harold. Java I/O. OReilly, 1999. [5] Henry F. Ledgard. The Little Book of Object-Oriented Programming. Prentice Hall, 1996. [6] Canek Pelez and Elisa Viso. Prcticas para el curso de Introduccin a Ciena a o cias de la Computacin I. V o nculos Matemticos, por publicarse, 2002. a [7] Danny C. C. Poo and Dereck B. K. Kiong. Object-Oriented Programming and Java. Springer, 1998. [8] Phil Sully. Modeling the world with objects. Prentice hall, 1993.

You might also like