You are on page 1of 544

Introduccin o

En este cap tulo introducimos conceptos relacionados con la computacin; presentamos una breve historia de las computadoras y del proo ceso de abstraccin que ha llevado a conformar la disciplina de las o ciencias de la computacin como la conocemos hoy en d Asimismo o a. revisamos la manera como han evolucionado los equipos de cmputo o hasta alcanzar las formas que tienen actualmente.

1.1 Signicado y alcances de la computacin 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 este texto 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

1.2 Conceptos generales

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 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 e o la administracin de la informacin (sistemas de informacin, bases de datos, o o o etctera). Al igual que la programacin, la informtica la podemos considerar e o a contenida propiamente en la computacin, con aspectos de administracin no o o exclusivos de 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, entre otros. A pesar de que se utiliza muchas o veces en un sentido ms general, ac no lo haremos as a a . Por ultimo, un trmino que ha tomado relevancia en los ultimos aos es el de e n equiparar las ciencias de la computacin con lo que se denomina las tecnolog de o as la informacin, en cierto sentido tomando en cuenta que, como dice Denning, las o ciencias de la computacin tienen que ver con el manejo de informacin codicada o o de cierta manera. Este trmino es mucho ms cercano a informtica que a ciencias e a a de la computacin, pues se preocupa de formas de manejar, transmitir y procesar o la informacin sin asomarse demasiado a las implementaciones de bajo nivel. o

1.2 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:

Introduccin o

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 poda e mos proporcionar un mtodo de solucin que sea un algoritmo aunque en realidad e o uno de las temas ms importantes en computacin, dado que est demostrado que a o a hay ms problemas que soluciones, es el de discernir cuando un problema tiene a solucin, y si la tiene, si es una solucin algor o o tmica. La segunda parte importante de nuestra disciplina es la implementacin de o algoritmos. Con esto queremos decir el poder llevar a cabo de manera automtica a un algoritmo dado (o diseado). n 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.3 Breve historia del concepto de algoritmo


La computacin es una disciplina muy antigua. Tiene dos ra fundamentales: o ces 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

1.3 Historia

4 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 esta direccin con el desarrollo del o o mtodo axiomtico en matemticas y el desarrollo de la geometr como un sise a a a tema lgico deductivo, en el que juegan un papel importante el modus ponens o (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. En el fondo o 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 dado origen a o a una disciplina muy extensa y rigurosa que es la lgica matemtica. No creemos o a necesario remarcar el gran avance que ha tenido esta disciplina en la ultima mi tad del siglo XX. Sin embargo, es importante notar que el desarrollo ha estado siempre presente, no unicamente en este ultimo per odo. Para ello presentamos a continuacin una tabla con aquellos eventos histricos que consideramos ms o o a relevantes en el desarrollo de la computacin, con una pequea anotacin de cul o n o a fue la aportacin en cada uno de los incisos los nmeros romanos corresponden o u a siglos.

Tabla 1.1
2000 AC

Resumen de la historia de Ciencias de la computacin o


Babilonios y egipcios

(1/3)

Tablas para clculos aritmticos, como ra a e ces cuadradas, inters compuesto, area de un e c rculo (8/9*1/2=3.1604...). Lgica formal con Modus Ponens y Modus o Tollens. Libro sobre recetas o mtodos para hacer e aritmtica con los nmeros arbigos. e u a Uso de letras para las incgnitas: surgimiento o del lgebra. a Huesos de Napier para la multiplicacin. El o concepto de logaritmo. Primer antecesor de la regla de clculo. a Formulacin matemtica de la F o a sica.

IV AC 825 1580 1614 1620

Aristteles (384-322 AC) o Abu Jafar Mohammed ibn Msa al-Khowrizm` u a Franois Vi`te (1540-1603) c e John Napier (1550-1617)

Edmund Gunter (1581-1626) XVI y XVII Galileo (1564-1642)

Introduccin o

Tabla 1.1

Resumen de la historia de Ciencias de la computacin o

(2/3)

XVI y XVII Descartes (1596-1650)

Descubre la geometr anal a tica, posibilitando la aplicacin del algebra a problemas o geomtricos y, por lo tanto, a problemas que e ten que ver con movimiento f an sico. Primera calculadora digital: suma y resta automticamente; multiplica y divide en forma a semiautomtica. a Calculadora que sobrevivi. Slo sumaba y o o restaba automticamente. a Coinventor del clculo con Newton. Primer a investigador occidental de la aritmtica bie naria. Inventor de la rueda de Leibniz, que hac las cuatro operaciones aritmticas. a e Fundamentos de la lgica simblica. o o Mquina Diferencial y Mquina Anal a a tica. Primera persona en programar la Mquina a Anal tica de Babbage. Construy un modelo de la Mquina Difereno a cial de Babbage. Estableci los fundamentos para el estudio o moderno de la Lgica Formal. o Uso de equipo tabulador (de registro unitario). Mquina electromecnica basada en la de a a Babbage. Propuso a los matemticos encontrar un sisa tema de axiomas lgicomatemtico unico o a para todas las reas de la matemtica. a a Elaboracin de tablas de posicin de la luna o o utilizando su mquina de registro unitario: a uso cient co de herramientas pensadas para procesamiento de datos.

1623

Wilhelm Schickard

1642-1644 XVII y XVIII

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

XIX 1842 1854 1854 1890 1893 1900

Charles Babbage (1791-1871) Ada Lovelace Pehr George Scheutz George Boole (1815-1864) Herman Hollerith Leonardo Torres y Quevedo (1852-1936) David Hilbert (1862-1943)

1928

Hollerith

1.4 Sistemas numricos e

6
(3/3)

Tabla 1.1
1936

Resumen de la historia de Ciencias de la computacin o


Kurt Gdel (1906-1978) o

1936

Alan Turing (1912-1954)

1939-1945

Wallace J. Eckert con John W. Mauchly

1937-1942

Claude Shannon

1940 1937-1944

John V. Atanaso (1903-) Howard T. Aiken (1900-1937)

Demostr que lo que propuso Hilbert no es o posible, esto es que hay problemas matemtia cos inherentemente insolubles. Atac el problema de cundo se puede deo a cir que se tiene un mtodo de solucin, que e o el problema no tiene solucin, etctera. Dio e se un implemento abstracto de cmputo no o conocido a partir de entonces como la Mquia na de Turing. Extensin de la mquina tabuladora de IBM o a para propsitos cient o cos. Diseo y consn truccin de la ENIAC, primera gran compuo tadora digital totalmente electrnica. o El uso del lgebra booleana para el anlisis a a de circuitos electrnicos. Liga entre la teor o a y el diseo. n Solucin de ecuaciones lineales simultneas. o a Computadora ABC. 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 la o a computadora. Conjunto de instrucciones para la EDVAC. El concepto de programa almacenado la nocin de que los datos y los o programas pueden compartir el almacenaje. Aritmtica binaria como mecanismo en las e computadoras

1943-1945 1944

John W. Mauchly John von Neumann

Introduccin o

1.4 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 rea sultados 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 que e agrupaban s mbolos. As por ejemplo, el sistema de nmeros romanos combina u s mbolos para abreviar: en lugar de escribir 100 rayas verticales utiliza el s mbolo C. Las abreviaturas del sistema romano son: N mero u de marcas 1 5 10 50 100 500 1000 S mbolo romano I V X L C D M

El sistema numrico romano tiene reglas para representar cantidades distintas que e stas y que son: e 1. El valor total de un nmero romano es la suma de los valores de cada uno u de los s mbolos que aparecen en el nmero, menos la suma de los valores de u los s mbolos que estn restando. e 2. Un s mbolo est restando si es que aparece inmediatamente a la izquierda de a un s mbolo con valor mayor que l. Pueden aparecer restando unicamente los e s mbolos I, X y C. No puede aparecer ms de un mismo s a mbolo restando. 3. Se pueden repetir hasta tres veces los s mbolos para el I, X, C y M. 4. Cuando un s mbolo est restando, slo puede hacerlo de los dos s a o mbolos que le siguen en valor: I X C puede restarse de V y X. puede restarse de L y C. puede restarse de D y M.

Veamos algunos ejemplos:

1.4 Sistemas numricos e

8 IV IX IC XCIX XC XL XLIX IL CMXCIX IM representa representa representa representa representa representa representa representa representa representa el el el el el el el el el el valor 4 valor 9 valor 99 incorrecto! valor 99 valor 90 valor 40 valor 49 valor 49 incorrecto! valor 999 valor 999 incorrecto!

5. Para cantidades mayores a 3,000 (MMM) se usan barras horizontales encima de los s mbolos D y M, que indican que el valor se multiplica por 10. 6. Los s mbolos romanos siempre se escriben de izquierda a derecha de mayor valor a menor valor, excepto en los casos en que el s mbolo est restando. e Contamos con un algoritmo muy sencillo para convertir nmeros en notacin u o decimal a nmeros romanos, que lo que hace es, repetitivamente, traducir un d u gito a su representacin en romano y multiplicarlo por 10. Lo dejamos para despus o e de haber visto notacin posicional. o

El cero y la notacin posicional o


Un concepto importante es el del nmero cero. Su origen es muy antiguo. Tamu bin los mayas ten un s e an mbolo asociado al cero. Este concepto es importante para poder utilizar notacin posicional. La notacin posicional es lo que usamos o o hoy en d y consiste en que cada s a mbolo tiene dos valores asociados: el peso y la posicin. Estos valores estn dados en trminos de lo que conocemos como la o a e base, que hoy en d corresponde al 10. a

EJemplo 1.4.2
El nmero 327.15 en el sistema decimal (o con base 10) se puede presentar de u la siguiente manera: 327.15

 3 102 2 101 7 100 1 101 5 102

Decimos que la notacin es posicional porque el valor (o peso) que tiene un o d gito depende de la posicin que ocupa con respecto al resto de los d o gitos en un nmero y no nada ms de cul d u a a gito es en el sistema romano, el valor del s mbolo

Introduccin o

es independiente de la posicin que ocupa; lo unico que cambia es si suma o resta. o 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 d u gito depende del d gito y de su posicin. Cada posicin lleva un peso de alguna potencia de 10 o o (decimal) asignadas, alrededor del punto decimal, de la siguiente forma: ... 4513.6  30100  .075  104 103 102 101 100 10000 1000 100 10 1 4 5 1 3 3 0 1 0 0 101 102 103 . . . .1 .01 .001 6 0 7 5

Hay varias reglas que observamos respecto a la notacin posicional: o i. En cada posicin se coloca un solo d o gito. ii. Los ceros antes del primer d gito distinto de cero, desde la izquierda, no aportan nada al nmero. u iii. 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. iv. Los ceros a la derecha del ultimo d gito distinto de cero despus del punto e decimal no cuentan. v. Los d gitos que podemos utilizar son del 0 al 9. vi. 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. Si en el o caso del sistema decimal la base es el valor 10 y cada posicin representa a una o potencia de 10, para trabajar en otra base cualquiera b, todo lo que tenemos que hacer es que los d gitos vayan del 0 a b 1 y las posiciones sean las correspondientes a potencias de b.

EJemplo 1.4.3
Para trabajar en base manera: . . . 84 83 82 4096 512 64 476.18 4 41378 4 1 8 (mejor conocida como octal ) ser de la siguiente a 81 80 8 1 7 6 3 7 81 82 83 ... .125 .015625 .001953125 1  318.12510  214310

1.4 Sistemas numricos e

10

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 o o un unico d gito, debemos inventar s mbolos (o d gitos) para seis valores que nos faltan, que ser del 10 al 15 inclusive. La tradicin es utilizar las letras A, B, C, an o D, E y F (a veces en minsculas) para los valores consecutivos 10, 11, 12, 13, 14 u y 15 respectivamente. Siguiendo la notacin posicional, pero en base 16, tenemos: o

EJemplo 1.4.4
161 1 162

... 476.116 BA7C16

164

163

162 161 160 16 7 7 1 6 12 4

...

65536 4096 256 11 10

.0625 .00390625

 1142.062510  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 abacos1 , huesos de Napier, calculadoras) usaban base 10. Mientras las calculadoras se constru con partes f an sicas (engranes, cuentas) la base 10 no era muy distinta de cualquier otra base. Pero al intentar construir calculadoras (o computadoras) electrnicas, la base 10 presentaba problemas de implementacin: cmo distino o o guir entre el 4 y el 5, cuando se estaba midiendo en trminos de niveles de luz, e l quidos, analgicamente? Por lo tanto se opt, aunque no desde el principio, por o o usar base 2, que tiene una representacin en la electrnica bastante simple. Para o o base 2 requerimos de dos s mbolos, por lo que utilizamos el 0 y el 1. El 0 se puede interpretar como ausencia y el 1 como presencia; con esto ms all de cierto nivel a a se supone presencia. Esto es mucho ms sencillo que ver si tenemos uno de 10 (u 8) a niveles distintos. En la tabla que sigue damos dos ejemplos en notacin posicional o en base 2:
Los bacos, en realidad, usaban una especie de base 5. Observa que cada hilo o columna en a un baco tiene, en la parte inferior, cuatro cuentas el japons y cinco el chino, aunque como la a e parte superior tiene una cuenta el japons y dos el chino, se contaba dos veces la parte inferior, e pudindose considerar como base 10. e
1

11

Introduccin o

EJemplo 1.4.5
27 26 25 24 23 22 21 20 128 64 32 16 8 4 2 1 1 1 0 0 1 1 0 1 1 0 1 0 0 0 21 22 23 . . . .5 .25 .125 1 1  205.7510  4010

... 11001101.112 1010002

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

n i 0

d i 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, mientras que bi se reere a la base elevada a la potencia de la posicin correspondiente. La n representa a la posicin distinta de cero con o o la potencia ms grande (la primera posicin signicativa a la izquierda). a o

1010002

5 i 0

0 2 0 0 2 1 0 2 2 1 2 3 0 2 4 1 2 5  0 0 0 8 0 32  4010
Para pasar de base 10 a cualquier otra base, se utiliza el algoritmo 1.1. Como estamos trabajando en notacin posicional y debemos ocupar una posicin por o o cada d gito en la base h, trabajaremos con s mbolos, por lo que buscamos construir una cadena de s mbolos y no precisamente un nmero. u El algoritmo construye el nmero base h pegando d u gitos de la base h por la izquierda, trabajando con una cadena. Hay que notar que en la l nea 11 se est concatenando un s a mbolo a nmero, por lo que si la base es mayor a 10, se u tendrn que utilizar s a mbolos para los d gitos mayores que 9.

d i 2i

1.4 Sistemas numricos e

12

Algoritmo 1.1 Algoritmo para pasar de base 10 a base h


Entrada: El nmero entero en base 10: num10 u Salida: El nmero en base h: nmero u u Mtodo: Vamos obteniendo los d e gitos de derecha a izquierda, empezando por el menos signicativo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 / I n i c i a l i z a c i n / o d i v i d e n d o = num10 ; divisor = h; residuo = 0; n mero = "" ; // Una c a d e n a v a ca , que no t i e n e u // n i n g u n sm b o l o / Se o b t i e n e d g i t o p o r d g i t o de l a nueva b a s e / repite r e s i d u o = d i v i d e n d o m dulo d i v i s o r o // ( r e s i d u o e n t e r o de d i v i d e n d o d i v i s o r ) ; n mero = p e g a r e l sm b o l o c o r r e s p o n d i e n t e a l r e s i d u o u p o r l a i z q u i e r d a a l o que ya l l e v a m o s en n mero . u d i v i d e n d o = d i v i d e n d o d i v i s o r ; // ( c o c i e n t e e n t e r o ) h a s t a que ( d i v i d e n d o = 0 ) ; / En nmero queda e l r e s u l t a d o / u

Veamos un ejemplo de la aplicacin de este algoritmo a continuacin. o o

EJemplo 1.4.6
Para pasar de base 10 a base 16 el nmero 857510 , el algoritmo se ejecutar u a de la siguiente manera: dividendo10 cociente10 residuo10 S mbolo residuo16 8575 535 15 F 535 33 7 7 33 2 1 1 2 0 2 2 Para que nos convenzamos que, en efecto 857510 componer 217F en potencias de 16: 217F16 N mero16 u F 7F 17F 217F

 217F16, procedemos a des-

  

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

13

Introduccin o

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 a a 2 (binario), observamos que 8  23 . Esto nos indica que cada posicin octal se o convertir exactamente a tres posiciones binarias correspondientes a 22 , 21 y 20 a en ese orden. Lo unico que tenemos que hacer es pasar cada d gito octal presente en el nmero a su representacin binaria: u o 75358 7 5 3 5 421 41 21 41 22 21 20 22 20 21 20 22 20 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. 7A35C16 7 A 3 5 C 421 82 21 41 84 22 21 20 23 21 21 20 22 20 23 22 0111 1010 0011 0101 1100 11110100011010111002

Hay que tener cuidado de no eliminar los ceros a la izquierda en los d gitos intermedios, sino unicamente en el primer d gito octal o hexadecimal a la izquierda. 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 (23 , 22 , 21 y 20 respectivamente), separndolos desde la derecha y rellenando con ceros al primer a grupo de la izquierda: 101111010111012 00102 11112 01012 11012 1 3 2 1 0 2 0 3 10 1111 0101 1101 2 2 2 2 2 2 2 2 22 20 210 1510 510 1310 216 F16 516 D16 2F 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 (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

1.5 La arquitectura de von Neumann

14

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 esa criba 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 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 a la mquina del censo de Hollerith. Cada patrn indica una operacin a realizara o o se. Los patrones son nmeros binarios con un nmero jo de posiciones (binary u u n digits). A cada conjunto de posiciones de un cierto tamao se le llama una palabra. El tamao de palabra es, en general, un nmero de bits que corresponde a n u 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, a que por supuesto es personal de cada modelo o tipo de computadora. Originalmente se distingu entre micro, mini y computadoras por el tamao en bits de a n sus palabras. El tamao de la palabra va unido al poder de la mquina: si tengo n o a ms bits por palabra tengo ms patrones posibles, y por lo tanto un lenguaje de a a mquina ms extenso y posibilidad de representar nmeros ms grandes o con ms a a u a a precisin2 . Las primeras microcomputadores ten palabras de 8 bits, mientras o an que las grandes computadoras ten palabras de 64 bits. Las supercomputadoan ras, que surgieron alrededor de 1985, ten palabras de 128 bits y posibilidades an 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
2

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

15

Introduccin o

1.5 La arquitectura de von Neumann


Se le llama arquitectura de una computadora a la organizacin que tiene en sus o 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 o muy parecida a la de Babbage: tenemos un procesador central el molino de Babbage en el que se ejecutan las operaciones aritmticas y de comparacin e o (lgicas); una memoria central que se utiliza para almacenar datos, resultados o 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 e o programas, de una ejecucin a otra, sin tener que volver a realizar el proceso, o o sin que tengamos que volverlos a proporcionar. Un esquema de una computadora con arquitectura de von Neumann se muestra en la gura 1.1.

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

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

1.5 La arquitectura de von Neumann

16

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 una 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 caracteres, etc. o u El proceso mediante el que se ejecuta un programa en lenguaje de mquina ya a colocado en la memoria de la computadora es el siguiente: i. La unidad de control se coloca frente a la primera instruccin de lenguaje de o mquina una vez cargado el programa. a ii. La unidad de control entra a un ciclo conocido como fetch/execute: (fetch) Carga a la unidad de control la siguiente instruccin a ser ejecutada. o (execute) La Unidad de Control se encarga de ejecutar la instruccin, vao lindose para ello de cualquiera de los componentes de la mquina. e a El tipo de instrucciones que tiene una computadora incluyen instrucciones para sumar, restar, multiplicar, dividir, copiar, borrar, recorrer el patrn de bits, o 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 aquel que nos permite expresar un problema o 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 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

17

Introduccin o

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

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)

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

1.5 La arquitectura de von Neumann

18

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 utilizaban tanto con nes cient cos como comerciales. En el uso cient co era muy comn expresar frmulas matemticas, que ten que u o a an despedazarse en operaciones bsicas para poderse llevar a cabo ver gura 1.3. a

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 b2 b b mul ac a c mul ac 4 ac mul a2 2 a sub rad ac b2 sqrt rad rad sub x1 rad b div x1 x1 a2

x1

El siguiente paso importante fue el de permitirle a un programador que especicara su frmula de manera similar a como se muestra en la parte izquierda de o la gura 1.3 x1  pb ra pb2 4 a cqq{p2 aq. El primer lenguaje de uso z generalizado orientado a esto fue FORTRAN For mula Translator, alrededor de 1956. Pocos aos despus se desarroll un lenguaje para usos comerciales, donde n e o lo que se deseaba es poder manejar datos a distintos niveles de agregacin. Eso te lenguaje se llamaba COBOL COmon B ussiness Oriented Language. Ambos lenguajes, o versiones modernizadas, sobreviven hasta nuestros 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

19

Introduccin o

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 el lenguaje, ALGOL ALGorithmic n no Oriented Language que result ser el modelo de desarrollo de prcticamente too a dos 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 miles de lenguajes de programacin, por lo que pretender mencionar siquiera a los o ms importantes es una tarea titnica y no el objetivo de estas notas. a a

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

Caracteres
Supongamos que tenemos un tamao de palabra de 16 bits y queremos repren sentar letras o caracteres. Simplemente hacemos lo que hac 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 caracteres distintos (en 6 bits hay 64 posibles an

1.5 La arquitectura de von Neumann

20

enteros, del 0 al 63). Con este cdigo alcanzaba para las maysculas, los d o u gitos y algunos caracteres importantes como los signos de operacin y de puntuacin. o o Con el perfeccionamiento de las computadoras se requirieron cada vez ms a caracteres, por lo que se extendi el cdigo a 7 y 8 bits, con el cdigo ASCII, que 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 caracteres de much 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 caracteres y mediante una tabla 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 caracteres. Estas tablas vienen con varias columnas; o en la primera de ellas vendr el carcter y en columnas subsecuentes su cdigo en a a o octal, hexadecimal, binario, utilizando alguno de estos esquemas para dar el cdigo o que le corresponde en ASCII, EBCDIC o Unicode. Por supuesto que requerimos de 32,768 para mostrar la codicacin de Unicode, por lo que no lo haremos, ms o a que en la medida en que tengamos que conocer el de algunos caracteres espec 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. La representacin de 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 pgina opuesta. a

21

Introduccin o

Figura 1.4

Enteros en signo y magnitud


magnitud hkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkikkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkj 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 2931 1 0 0 0 1 0 1 1 0 1 1 1 0 0 1 1 2931 s i g n o

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

Algoritmo 1.2 Suma de dos nmeros enteros representados con signo y magnitud u
1 2 3 4 5 6 7 8 9 10 11 12 13 Sean a y b l o s e n t e r o s a sumar Sa  s i g n o de a ; Ma  magnitud de a ; Sb  s i g n o de b ; Mb  magnitud de b ; i f ( Sa  Sb ) then Ssuma  Sa ; Msuma  Ma Mb ; else i f ( Ma Mb ) then Ssuma  Sa ; Msuma  Ma Mb ; else Ssuma  Sb ; Msuma  Mb Ma ;

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 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 siguiente pgina. a

1.5 La arquitectura de von Neumann

22

Figura 1.5

Nmeros enteros 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 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0

3, 592 216  65, 536 36, 360 29, 176

p1 q p2 q p3 q p4 q

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 1 1

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: 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 32, 767 A partir del nmero 215  32, 768 y hasta 216 1  65, 535, que corresponde u 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 los enteros negativos el bit 15 est siempre prendido, por lo que reconocemos el signo del nmero. a u La magnitud (o el valor absoluto) del nmero que estamos viendo se obtiene u sacando el complemento a 2 de la palabra en cuestin. El complemento a 2 se o obtiene de tres maneras posibles: i. Se resta en binario de un nmero 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. 10000000000000000 1000111000001000 0111000111111000 65536 36360 29176

23

Introduccin o

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 1000111000001000 0111000111110111 1 0111000111111000 Nmero original u Nmero complementado a 1 u Se le suma 1  29176

iii. Se procesan los bits de derecha a izquierda: se dejan igual los d gitos hasta e incluyendo el primer 1; a partir de ese punto se complementan todos los bits (convertir al bit de signo en 0). a 1000111000001000 0111000111111000 Nmero original u Se dejan igual hasta el primer 1 y se complementan  29176

Cabe aclarar que nos interesa sacar el complemento a 2 slo en el caso de los o enteros negativos los que tienen el bit ms alto prendido pues en el caso de los a enteros positivos su magnitud est dada directamente en binario. a 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, 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.6

Suma de dos nmeros enteros 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 0 1 0 0 1 0 1 0 0 1 0 0 1 0 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 1 0 1 0 1 0 1 1 0 0 1 1 1 0

+ +

207 4547 4754 25 939 914

1.5 La arquitectura de von Neumann

24

Pueden vericar, sumando las potencias de 2 donde hay un 1, que las sumas en la gura 1.6 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 1 en el resto de los bits) el resultado resulta ser un nmero negativo, aquel que tiene 1 en todas las posiciones de la palabraver u 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 dos opciones, conocidas como u punto jo y punto otante. Punto jo: Como su nombre l;o indica, la representacin en bits es la de un o entero (posiblemente en complemento a 2) al que se le aplica un factor de escalamiento constante. Por ejemplo, pensando en base 10 y si tenemos un factor de escala de 1{10000 (que corresponde as 4 posiciones decimales), el nmero 325.15 estar representado como 3251500, que al multiplicarlo por u a 1{10000 nos dar precisamente 325.15. El nmero 1.2575 estar represena u a tado como 12575. En el caso de notacin binaria el factor de escala es una o

25

Introduccin o

potencia negativa de 2 (1{2 para una posicin, 1{4 para dos posiciones y o as sucesivamente).ver gura 1.8. Como se puede ver en esta gura, se mantiene la notacin de complemento o a 2, con el bit ms alto indicndonos que el nmero es negativo. a a u

Figura 1.8

Notacin con punto jo o


215 214 213 212 211 210 29 1 28 27 26 25 24 23 22 21 20 0 1 0 0 1 1 0 1 0 1 0 0 0 0 1 1 19779{26  309.047 1 0 0 1 1 0 1 1 0 1 0 1 0 1 1 0 99560{26  1555.625 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 o muy pocos d gitos signicativos en la fraccin. o Punto otante: El punto otante es otra manera de representar nmeros reales. u Bsicamente se logra esta notacin dividiendo a la palabra en dos partes, a o una 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, representando a todos los nmeros reales con ese nmero de enteros. A continuacin damos una u u o potencia de 10 por la que hay que multiplicar el nmero para obtener el u nmero que deseamos. u Una abreviatura de esta notacin consiste en escribir los dos nmeros anteo u riores de la siguiente forma: 1.32456E6 1324560 1.32456E 6 .00000132456 1.32456E3  1324.56

1.5 La arquitectura de von Neumann

26

Esta representacin es much o simo ms verstil que la de punto jo, y de a a hecho es la que se usa generalmente, aunque la representacin de los nmeo u ros es 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 reprea sentacin de punto otante para representar nmeros. o 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 posiciones con 0 y posiciones con u 1 en cualquier tamao dado de palabra. n 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 posibilidad de representar la densidad de los nmeros reales. Como u estamos usando binario para representar a nmeros que manejamos y pensamou s como nmeros en base 10, habr nmeros que no tengan un representacin u a u o exacta al convertirlos a base 2 (por supuesto que estamos hablando de nmeu ros fraccionarios). Adicionalmente, al agregarle 1 a una mantisa, no obtenemos el siguiente nmero real, ya que estamos sumando, posiblemente, en la parte u fraccionaria. Por ello no es posible tener una representacin para todos y cada uno o

27

Introduccin o

de los nmeros reales en un intervalo dado: nuevamente, la cantidad de nmeros u u reales en un intervalo dado es un nmero innito (en realidad, ms grande que u a innito, 0 ) y lo que tenemos es un nmero nito de combinaciones. u

1.6 Ejecucin de programas o


Mencionamos que lo unico que puede ejecutar una computadora, directamente en su hardware, es un programa escrito en su lenguaje de mquina. Por lo que a para que nuestros programas escritos en Java puedan ser ejecutados, debern ser a transformados a 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.7 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.8 Un compilador es un programa que una vez que reside en memoria y o
al ejecutarse, toma un programa fuente y lo traduce completo a un programa equivalente en otro lenguaje de programacin, que generalmente es lenguaje de o mquina. 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 ser ejecutado en un a momento posterior. A los intrpretes se les conoce tambin como mquinas virtuales, porque una e e a

1.6 Ejecucin de programas o

28

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.6.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 requieren bibliotecas muy extensas con funciones mae temticas, 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 rehsa a morir, o VisualBasic que u provee una fabricacin rpida de interfaces con el usuario3 . 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. Tenemos o tambin lenguajes que se utilizan para trabajar en el Web y que se conocen coe mo de desarrollo acelerado, como Python y Ruby. En n, casi cualquier tipo de aplicacin que se nos ocurra, se puede disear un lenguaje de programacin para o n o el cul el lenguaje que se utilice en el algoritmo sea muy cercano al lenguaje de a programacin: ste es el objetivo que se persigue cuando se disean nuevos lenguao e n jes de programacin. Este curso se enfocar a resolver problemas que se expresan o a fcilmente con orientacin a objetos, y el lenguaje que utilizaremos es Java. a o Es importante darse cuenta que 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 programado, termia e naremos con un programa equivalente escrito en lenguaje de mquina. El meollo a del asunto es, simplemente, qu tanto trabajo nos cuesta pensar en el problema e de un cierto tipo cuando lo queremos resolver en un lenguaje pensado para resolver otro tipo distinto de problemas. Buscamos que el lenguaje de programacin 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 interfaces grcas. a
3

29

Introduccin o

se ajuste de manera sencilla a nuestro modo de pensar respecto al problema que deseamos resolver.

1.7 Caracter sticas de Java


Java es un lenguaje orientado a objetos, cuyo principal objetivo de diseo es su n portabilidad, esto es que sin cambios sed pueda ejecutar en cualquier computadora. Otro objetivo de Java fue su uso remoto en la red. Una manera de hacer programas escritos en Java es mediante 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, coe a nocido como una mquina virtual, y se ejecuta el programa en lenguaje de a mquina en la mquina virtual de Java. a a 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.

Ejercicios
1.1.- De entre los conceptos vistos, cul es el que consideras ms relevante para: a a (a) La construccin de la computadora digital. o (b) El diseo e implementacin de lenguajes de programacin. n o o

1. Ejercicios (c) La automatizacin de procesos que manejan informacin. o o

30

1.2.- En las siguientes dos columnas, relaciona una columna con otra. Cada entidad en la primer columna puede estar relacionada con ms de una entidad a en la segunda columna y viceversa. ciencias de la computacin o tecnolog de la informacin as o informtica a algoritmo Hollerith Shannon Turing Gdel o Pascal Mauchly von Neumann entrada denicin o ejecucin o sistema binario automatizacin o logaritmos algebra binario octal programa almacenado eciencia registro unitario

1.3.- Cul o cules son las diferencias entre un algoritmo y un procedimiento? a a 1.4.- Cul de las caracter a sticas de un algoritmo consideras ms importante para a poder ejecutarlo? Justica. 1.5.- Cules son las ventajas y desventajas de un sistema binario frente a un a sistema numrico decimal? e 1.6.- Transforma de base 10 a base 7 las siguientes cantidades: (a) 439210 (b) 342710 (a) 56318 (b) 3578 (a) 286110 (b) 3445710 (a) 4A5C16 (b) 499916 (c) 33610 (d) 25710 (c) 43748 (d) 76548 (c) 36310 (d) 275210 (c) 3A16 (d) F F F16

1.7.- Transforma de base 8 a base 10 las siguientes cantidades:

1.8.- Transforma las siguientes cantidades de base 10 a base 16:

1.9.- Transforma las siguientes cantidades de base 16 a base 10:

31 1.10.- Pasa de hexadecimal a binario las siguientes cantidades: (c) 33316 (a) A35C16 (d) F F F16 (b) 49F 916 1.11.- Pasa de hexadecimal a octal las siguientes cantidades: (c) 3F 316 (a) A4C516 (d) 88816 (b) 944416

Introduccin o

1.12.- Qu ser ms tardado de ejecutar, un programa compilado o uno intere a a pretado? 1.13.- Supongamos que tenemos una representacin de enteros de signo y magnio tud que ocupa 16 bits. (a) Cul es el menor nmero entero que se puede representar? a u (b) Cul es el mayor nmero entero que se puede representar? a u 1.14.- Supongamos que tenemos una representacin de enteros de complemento a o 2 que ocupa 16 bits. (a) Cul es el menor nmero entero que se puede representar? a u (b) Cul es el mayor nmero entero que se puede representar? a u 1.15.- Supongamos que tenemos nmeros reales de punto otante con un expou nente en complemento a 2 con 8 bits y una mantisa en complemento a 2 con 24 bits. (a) Cul es la precisin, en decimal, que tienen los reales con esta reprea o sentacin? o (b) Cul es la mxima magnitud (en decimal) de un real con esta reprea a sentacin? o (c) Cuntos posibles nmeros reales distintos podemos representar de esta a u manera? 1.16.- Es la arquitectura de von Neumann relevante para las computadoras multicore?

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 Software1 ): a
El diagrama que presentamos es lo que corresponde a un proceso de software en cascada, ya que se regresa una y otra vez a etapas anteriores, hasta que se pueda llegar al nal del diagrama.
1

2.1 Qu es la programacin? e o

34

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 bemos determinar de manera precisa las especicaciones: de dnde partimos o (con qu entradas contamos) y a dnde queremos llegar (cul es el resultae o a do que deseamos obtener). Como producto de esta etapa producimos tres incisos: a. Enunciado preciso del problema. b. Entradas. c. Salidas.

35

El proceso del software

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 deo beremos pensar en procesos sucesivos que nos lleven de un estado de cosas (estado de los datos) al estado inmediato sucesor fotos instantneas y cul a a o cules son las transformaciones de los datos que nos producen el siguiente a estado. Dependiendo del ambiente (dominio) de nuestro problema, las soluciones que diseemos debern tener distintas caracter n a sticas. La primera regla, que comparten todos los dominios es: a) La solucin debe ser correcta, eciente y efectiva. o Es claro que toda solucin debe ser correcta, esto es, resolver el problema. o Una solucin es eciente si usa una cantidad razonable de recursos y se o ejecuta en un tiempo razonable. Respecto a esta caracter stica, la unica excepcin posible a esta regla se presenta si estamos haciendo un programa o para ayudarnos a calcular algo o para sacarnos de un brete momentneo o a coyuntural, el programa se va a utilizar pocas veces en un lapso corto de tiempo; tal vez hasta podemos eliminar la caracter stica de que sea eciente. En los primeros aos de las computadoras, casi todos los programas eran n de este tipo: la gente los hac para s mismos, o para un grupo reducido a que estaba muy al tanto de lo que estaba pasando. Que un programa sea efectivo quiere decir que el programa termina. 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) que atacan un cierto subproblema, 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 po-

2.1 Qu es la programacin? e o

36

sible: que haya poco trco entre los mdulos, de tal manera que haya la a o posibilidad de reutilizarlos. 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 Durante las etapas de diseo e implementacin se debe asegurar la don o cumentacin del programa, que algunas veces aparece como una etapa o del proceso. Sin embargo, esto no es o no deber ser una etapa separada a del proceso, ya que lo ideal es que conforme se va progresando se vaya documentando el programa (o en su caso el diseo). Hoy en d existen muchos n a paquetes que ayudan a llevar a cabo el diseo y que apoyan la elaboracin de n o la documentacin, por lo que actualmente es imperdonable que falte docuo mentacin en los programas. En el presente, en que los programas se hacen o 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 a 3 veces, mediante la documentacin . o Prueba y validacin: Debemos tener la seguridad de que nuestro programa hao ce lo que se supone debe 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 defenderse o en situaciones anmalas. Tambin existen mdulos espec o e o cos en ciertos lenguajes de programacin, como JUnit de Java, que permite elaborar pruebas o unitarias para nuestro cdigo. 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.
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 3 En el contexto de aprender a programar la documentacin es muy importante para el que va o a calicar el programa y para el que est aprendiendo, ya que le permite recuperar el contexto a en el que resolvi algo de cierta manera. o
2

37

El proceso del software

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 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 un n sistema no se tape un hoyo haciendo otro. La modularidad ayuda, pues se entiende mejor el funcionamiento del programa si se ataca por mdulos. Peo ro esta propiedad debe tener, adems, la propiedad de encapsulamiento, que a consiste en que cada mdulo tiene perfectamente delimitado su campo de o accin, la comunicacin entre mdulos es muy controlada y una implemeno o o tacin interna (modo de funcionar) que pueda ser cambiada sin que altere o 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, o e en cul es el resultado que se desea obtener. Este resultado puede tomar muy a distintas formas. Digamos que el cmputo corresponde a un modelo de un sistema o (la instrumentacin o implementacin del modelo). o o Podemos entonces hablar de los estados por los que pasa ese sistema, donde un estado corresponde a los valores posibles que toman las variables. Por ejemplo, si tenemos las variables x, y, z, que participan en un determinado clculo, un posible a estado ser a: t x  5, y  7, z  9 u Si tenemos la especicacin de un programa (rutina) que intercambie los valores o

2.1 Qu es la programacin? e o de dos variables x, y, podemos pensarlo as :

38

t t

x  K1 , x  K2 ,

 K2  K1

u u

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 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 x  K1 , t x  K2 ,
A x ponerle K2 A y ponerle K1

 K2 u  K1 u

// estado inicial // Proceso // Proceso // 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

t x  K1

// estado inicial En t copia el valor que tiene x // Proceso En x copia el valor que tiene y // Proceso t x  K2 , y  K2 , t  K1 u estado intermedio // En estos momentos x e y valen lo mismo! En y copia el valor de t // Proceso t x  K 2 , y  K1 u // estado nal y

 K2 u

y este proceso funciona no importando qu valores iniciales dimos para x e 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

39

El proceso del software

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. 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 tres vertientes importantes en cuanto a las maneras de dividir el problema: 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 c) Diseo funcional, que involucra generalmente recursin, y que denota el diseo n o n como la aplicacin sucesiva de funciones a los datos de entrada hasta obtener o el resultado nal.

2.1 Qu es la programacin? e o

40

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, excepto cuando detecten que algo no funciona bien. En las sesiones de laboratorio, sin embargo, tendrn que trabajar con programas ya a hechos y extenderlos, lo que tiene que ver con el mantenimiento. Tambin es la e losof de este material empezar con una aplicacin bsica e irla extendiendo en a o a poder y sosticacin. o o

2.1.1.

Conceptos en la orientacin a objetos o


Al hacer el anlisis de nuestro problema, como ya mencionamos, trataremos a de dividir 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 el 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 conocimiento qu es lo que debe o e saber , conocer, recordar cada objeto de la clase y un patrn de comportamiento: o nos dicen qu y cmo se puede hacer con los datos de la clase. Es como el guin e o o de una obra de teatro, ya que la obra no se nos presenta hasta en tanto no haya actores. Los actores son los objetos, que son ejemplares o instancias 4 de las clases (representantes de las clases).
4

instancia es una traduccin literal que no existe en espaol. o n

41

El proceso del software

Al analizar un problema debemos tratar de identicar a los objetos involucrados. Una vez que tengamos una lista de los objetos (agrupando datos que tienen propsitos similares, por ejemplo), debemos abstraer, encontrando caracter o 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. Su estado: cul es el valor de sus atributos. a ii. Su conducta: Qu cosas sabe hacer. e Cmo va a reaccionar cuando se le hagan solicitudes. o El nmero y tipo de atributos (variables) con que cuenta un objeto est deteru a minado por la denicin de la clase a la que pertenece, aunque el estado est deo a terminado por cada objeto, ya que cada objeto es capaz de almacenar su propia informacin. Lo correspondiente a (ii) est dado por la denicin de la clase, que o a o nos da un patrn de conducta. o 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

2.1 Qu es la programacin? e o

42

En el caso de los objetos, la cpsula alrededor del mismo oculta al exterior la a manera en que el objeto trabaja, el cmo. Cada objeto tiene una interfaz pblica o u y una representacin privada. Esto nos permite hacer abstracciones ms fcil y o a a elaborar modelos ms sencillos, pues lo que tomamos en cuenta del objeto es a exclusivamente su interfaz; posponemos la preocupacin por su representacin o o privada y por el cmo. o 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: 1. Se determina cules funcionalidades e informacin estn relacionadas y dea o a ben permanecer juntas, y se encapsulan en una clase. 2. Entonces se decide cules funcionalidades e informacin se le van a solicitar a a o representantes de esta clase (qu servicios va a prestar). Estos se mantienen e pblicos, mientras que el resto se esconde en el interior de los objetos de esa u clase. 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 o aplicacin). o 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

43

El proceso del software

Atributos
Algunos sistemas o lenguajes orientados a objetos llaman a stos variables e propias, variables miembro o variables de estado. Se reeren a los valores que debe recordar cada objeto y que describe el estado del objeto. Cada objeto puede tener un valor distinto en cada uno de estos atributos. Como uno de sus nombres indica (variables de estado), conforman el estado del objeto, que puede cambiar durante la ejecucin de la aplicacin. o o

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 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

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 mtou o e dos se les llama funciones miembro (member functions), porque son funciones (procedimientos) que estn denidos en, son miembros de, la clase. a

2.1 Qu es la programacin? e o

44

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. En cambio, si hablamos de un ser humano en abstracto, de la categor que a describe a todos los seres humanos, no podremos decir cul es su altura o su color a de pelo, sino unicamente cules son los colores de pelo vlido. Tambin sabemos a a e que come, pero no podemos decir si con las manos, utilizando una cuchara o una tortilla. Un ejemplar tiene caracter sticas concretas, mientras que una clase describe todas las posibles formas que puede tomar cada una de las caracter sticas. 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 animo, posicin. Fulanito puede estar dormido o despierto, contento o o triste, parado o sentado, correspondiendo esto a cada una de las caracter sticas (o 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 gnero, la forma de la nariz5 . e
No estamos considerando, por supuesto, la posibilidad de cirug implantes o pupilentes as, que puedan cambiar el estado de un ser humano.
5

45

El proceso del software

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 u u a funciones. Cuando esto sucede debemos reconocer al ncleo original, identicando u a una clase abstracta (o sper-clase, no forzosamente abstracta) a partir de la u cual cada una de las clases de la familia son una extensin. A esta caracter o stica se le conoce como herencia: las clases en la familia de la que hablamos, heredan de la clase abstracta o clase base, remedando un arbol como el que se muestra en la gura 2.2. En esta gura las 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 jerarqu de clases 6 . Cuando a una clase es abstracta queremos decir que no tenemos suciente informacin para o construir objetos completos de ella. Por ejemplo, la clase Elementos geomtricos e no da suciente informacin para construir a un objeto de la misma, pues no dice o su forma precisa.

Figura 2.2

Arbol de herencia en clases


Elementos geomtricos e

punto

l nea

Volumen

recta

curva Supercie

cubo

cilindro

a esfera pirmide

cuadrado
6

rectngulo a

c rculo

elipse

Vale la pena notar que los niveles en el rbol no estn dibujados adecuadamente, pues clases a a en el mismo nivel jerrquico no estn alineadas en la guras. a a

2.2 Dise o orientado a objetos n

46

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 heredera. A esto, el que el o mismo nombre de mtodo pueda tener un signicado distinto dependiendo de la e clase a la que pertenece el objeto particular que lo invoca, es a lo que se llama polimorsmo tomar varias formas.

2.1.2.

Solucin del problema o


En resumen, presentados ante un problema, estos son los pasos que debemos 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 de objetos. Esta ultima especicacin debe incluir una descripcin completa de o o

47

El proceso del software

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 cules son e a los conocimientos que 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 precisar 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. Revisemos con un e poco de ms detalle estos procesos: a 1. Determina las clases en tu sistema (encuentra los objetos). Para determinar cules son los objetos en tu sistema, debes tener muy claro a cul es el objetivo del mismo. Qu debe lograr el sistema? Cul es la a e a conducta que est claramente fuera del sistema? La primera pregunta se a responde dando una especicacin completa del problema. En un principio o daremos descripciones narrativas y nuestro primer paso deber consistir en a dividir el problema en subproblemas, identicando las 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 especi-

2.2 Dise o orientado a objetos n

48

cacin 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 las responsabilidades de las clases denidas (mtodos y estruce turas de datos). 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 mtodos (o funciones): e Mtodos constructores, que son los encargados de la creacin de los e o objetos, as como de su inicializacin (establecer el estado inicial). o Mtodos de implementacin, que son aquellos que representan a los e o servicios que puede dar un objeto de esa clase. Mtodos de acceso que proporcionan informacin respecto al estado del e o objeto. Mtodos auxiliares que requiere el objeto para poder dar sus servicios, e pero que no interactan con otros objetos o clases. u Mtodos mutantes (de actualizacin y manipuladores), que modican e o el estado 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:

49

El proceso del software

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 este objeto? e o Qu otros objetos saben cmo llevar a cabo alguna operacin que e o o requiere? 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 aplicacin) va a consistir de un objeto enorme que hace todo. o En este paso, aunque no lo hemos mencionado, tenemos que involucrarnos ya con el cmo cumple cada objeto con su responsabilidad, aunque no too dav a mucho nivel de detalle. Un aspecto muy importante es el determinar a dnde es que se inician las acciones. En el esquema de cliente/servidor del o 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, etctera. Es importante recalcar que mientras en la vida real algunos de los e 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 o : agente que inicie la accin, que ponga en movimiento al sistema. o Es necesario en esta etapa describir con mucha precisin cules son las eno a tradas (input) y salidas (output) de cada solicitud 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 sabe hacer lo que le piden, no lo reconoce. Un valor o dato que posee. La realizacin de un proceso o La manera como estas respuestas se maniestan van a cambiar de sistema a sistema (de lenguaje a lenguaje). 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, sino que el mismo objeto controle esta interaccin. o

2.3 Dise o estructurado n

50

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

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 no corresponde a a la orientacin a objetos. Esto se debe fundamentalmente a que dentro de un o mtodo debemos llevar a cabo un algoritmo que nos lleve desde un estado inicial a e otro nal, pero donde no existe colaboracin o responsabilidades, sino simplemente o una serie de 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 2 3 pon 1 en x suma 2 a x copia x a y

se ejecutar exactamente en el orden en que estn listadas. a a

51

El proceso del software

Iteracin, que marca a un cierto conjunto de enunciados secuenciales e indica la o manera en que se van a repetir. Por ejemplo:
1 2 3 4 5 pon 1 en x R e p i t e 10 v e c e s d e s d e e l e n u n c i a d o 3 h a s t a e l 5 : suma 2 a x copia x a y E s c r i b e e l v a l o r 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 R e p i t e 10 v e c e s :

y el solo hecho de que los enunciados 3 a 5 aparecen con mayor sangr da a 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:
1 2 3 4 5 6 7 8 p o n e r un v a l o r a r b i t r a r i o a y , e n t r e 0 y 9999 Si x e s mayor que 1 : d i v i d e a y e n t r e x y c o l o c a e l r e s u l t a d o en z m u l t i p l i c a a z por 1 3 e s c r i b e e l v a l o r de z Si x no e s mayor que 1 : 1 m u l t i p l i c a a y por 6 e s c r i b e e l v a l o r 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

$ 'n pn 1q! para n 1 & n!  ' %


1 para n  1

2.3 Dise o estructurado n

52

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 cmo organizar, en general, los enunciados del o o mtodo. e 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 (qu es lo e 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 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 dentro de cada conjunto es muy importante. La manera en que el mtodo de Warnier-Orr representa estos conceptos se explica e a continuacin: 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 en la pgina opuesta. a Jerarqu en la gura gura 2.3 en la pgina opuesta, nombre agrupa a: a a descr1 , descr2 ,. . . ,descrn ; decimos entonces que las descripciones son un

53

El proceso del software

renamiento de nombre, y que nombre es de mayor jerarqu que las a descripciones.

Figura 2.3

Uso de llaves para denotar composicin o , $ ' / ' descr1 / / ' / ' / ' / ' / ' / ' ' / ' / & .
descr2 nombre

' ' ... ' ' ' ' ' ' ' ' descr % 3

/ / / / / / / / / / -

Como mencionamos, se usa la notacin de conjuntos, encerrando entre llaves o a los elementos del conjunto; la diferencia es que listamos estos elementos verticalmente, separados entre s por cambios de rengln. La llave de la o derecha nos estorba, pues nos impide hacer renamientos de cada uno de los elementos listados, por lo que en adelante la eliminaremos. 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. Por ejemplo, si la cena de esta noche consiste de tres platillos, un ceviche, una sopa, un pescado y un an de postre, quedar representado de la siguiente a forma:

Figura 2.4

Ejemplo de la descripcin de un conjunto o $ ' ' ceviche ' ' ' ' ' ' ' ' sopa &
cena

' ' pescado ' ' ' ' ' ' ' ' an %

En este caso queda muy claro que el orden en que estn listados los platos a

2.3 Dise o estructurado n

54

de la cena es importante: ste es el orden en que se consumirn y en ningn e a u otro. Iteracin: la repeticin se representa con un parntesis abajo del nombre o o e donde se indican las reglas de repeticin: o

Figura 2.5

Iteracin en diagramas de Warnier-Orr o $ ' descr1 ' & descr nombre

pMientras te diganq ' . . . ' %

descrn

En el la gura 2.5, 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. Siguiendo con el ejemplo de la cena, supongamos ahora que la cena consistir de tacos y refrescos, tantos como pida el comensal. Suponemos tambin a e que por cada taco que se come toma un sorbo de refresco. El diagrama de Warnier-Orr se podr mostrar de la siguiente forma: a

Figura 2.6

Un diagrama de iteracin o

pMientras tengas hambreq ' refresco ' '


' %

cena

$ ' ' ' taco ' &

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.7 en la pgina opuesta. a

55

El proceso del software

Figura 2.7

Seleccin en diagramas de Warnier-Orr o " $ 'd  1 . . . ' gito ' ' ... ' ' ' ' ' ' " ' ' 'd  2 . . . ' gito & ... nombre ' ' '. . . ' ' ' ' ' ' ' " ' ' 'd  9 . . . ' gito % ...

Si seguimos con el ejemplo de la cena de tacos y refresco, antes de tomar refresco podr amos preguntar si todav hay refresco, y si ya no hay abrir un a refresco nuevo.

Figura 2.8

Un diagrama de condicional $ ' ' ' ' 'taco ' ' $ ' ' ' ' ' & ' ' 'hay refresco ' guele ' ' 'S ' % & cena pMientras tengas hambreq ' ' ' $ ' ' ' ' ' & ' ' 'No hay refresco Abre un refresco ' ' ' ' ' % ' ' ' ' %
refresco

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.9 y 2.10, donde el e o s mbolo signica obtiene el valor de lo que est a la derecha y coloca ese a valor en la variable que est a la izquierda. a

2.3 Dise o estructurado n

56

Figura 2.9

Diagramas de Warnier-Orr para secuencia

$ 'x 1 & secuencial x x 2 ' %


y

Figura 2.10

Diagramas de Warnier-Orr para iteracin o $ & xx2 Repite

p10q % y x el valor de x escribe

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

Figura 2.11

Diagrama de Warnier-Orr para seleccin o $ 'y randp10000q ' $ ' ' ' ' z y {x ' & ' ' 'x 1 ' ' ' ' z z {3 ' %escribe el valor de z & seleccin o ' ' ' ' ' ' ' # ' ' ' ' y y {6 'x 1 ' % escribe el valor de y
Cuando tenemos una condicional es frecuente que unicamente estemos veri cando si se da o no una condicin, por lo que podemos utilizar la notacin de o o complemento que se usa en lgica para negar una condicin. En lugar de las dos o o condiciones x 1 y x 1 podemos decir x 1 y su negacin, que es x 1; o bien o hay refresco y su negacin hay refresco, que es equivalente a no hay refresco. o 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 completar o cerrar. Veamos un ejemplo, el de cambiar un nmero que viene en u

57

El proceso del software

base B a un nmero en base b. Pediremos que tanto b como B sean menores u o iguales a 10, para tener un d gito por posicin y usar unicamente los d o gitos decimales. Para pasar al nmero de base B a base b primero lo pasaremos a base u 10, y despus de base 10 a base b. Siguiendo la notacin que acabamos de dar, e o todo diagrama de Warnier-Orr empieza con el formato de la gura 2.12.

Figura 2.12

Estado inicial de todo diagrama de Warnier-Orr " $ Inicializar ' .P rincipio ' ' ' Obtener datos ' ' ' " &
N ombre del P roblema

' ' ' ' ' ' ' .F inal %

P roceso

"

... ...

Entregar resultados Atar cabos sueltos

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 y cul a a a es el paso inmediato anterior para que lo podamos producir. Para el caso que nos ocupa, cambiar un nmero de una base a otra, lo podemos u ver en la gura 2.13 en la siguiente pgina. En el primer momento, todo lo que a sabemos es qu es lo que tenemos de datos (b, B, nmeroB ) y qu esperamos de e u e resultado (el nmero convertido a base b). Para el principio y nal de nuestros u procesos podemos observar que lo ultimo que vamos a hacer es proporcionar o escribir nmerob (correspondiente a nmeroB ). Esto corresponde al nal de nuesu u tro proceso. Sabemos, porque lo vimos al principio del curso, que para pasar un nmero en base B a base 10 tenemos que ir calculando los valores de los d u gitos en las distintas posiciones y que en este proceso vamos obteniendo el nmero en u base 10. Tambin sabemos, porque corresponde a nuestros datos, que al principio e de nuestro proceso debemos obtener b, B y el nmero a convertir. Con esta inu formacin, podemos producir el diagrama inicial que, como ya mencionamos, se o encuentra en la gura 2.13 en la siguiente pgina. a Progresamos de atrs hacia adelante. Para poder escribir nmerob primero, a u debemos construirlo. Esto lo hacemos en el bloque inmediato anterior al del nal, que es el que convierte a un nmero en base 10 a un nmero en base b. Para hacer u u esto, primero tenemos que haber convertido el nmero en base B a base 10, lo u que hacemos en el bloque anterior. Y antes que nada, al principio, obtenemos los

2.3 Dise o estructurado n

58

nmeros que queremos procesar. Esta etapa la podemos observar en el diagrama u de la gura 2.14.

Figura 2.13

Diagrama inicial para pasar un nmero de base B a base b u $ $ 'Obtener b ' ' ' '.Principio &Obtener B ' ' ' ' ' %Obtener nmero ' ' u ' B ' &Cambiar a ! Cambio de ... base B a ' base 10 ! base b 'Cambiar a ' ' ' base b ... ' ' ' " ' ' ' ' %
.Final Escribir nmerob u

Figura 2.14

Diagrama de Warnier-Orr para procesar cada una de las partes del proceso $ $ ! 'Obtener b ' ' ' ... ' ' ' ' & ! ' ' '.Principio Obtener B ' ... ' ' ' ' ! ' ' ' ' 'Obtener nmeroB . . . ' % ' u ' ' ' $ ! ' ' ' 'Principio ' ' ... ' ' ' ' ' ' # ' ' & gito 'Cambiar a ' Calcular d ' ' ' base 10 (mientras ... ' ' nmeroB 0q ' Cambio de ' ' u & ' ! ' ' 'Final base B a % ... ' ' base b ' ' $ ! ' ' ' 'Principio ' ' ... ' ' ' ' ' ' # ' ' & gito 'Cambiar a ' Calcular d ... ' ' ' base b (mientras ' ' ' nmero10 0q ... ' ' u ' ' ' ! ' ' ' ' 'Final ' % ' ... ' ' ' ' " ' ' ' '.Final % Escribir nmerob u

59

El proceso del software

Nos falta desarrollar los bloques que corresponden a vericar que la informacin o dada por el usuario es correcta que las bases sean menores a 10, pasar de base B a base 10 y, por ultimo, pasar de base 10 a base b. Vericar que los nmeros dados sean correctos es un proceso fcil. Simplemente u a preguntamos si los nmeros estn en rangos. Si es as seguimos adelante y si no, u a , abortamos el proceso con un mensaje adecuado. Para pasar de base B a base 10 vamos obteniendo el ultimo d gito y mul0 tiplicndolo por la potencia correspondiente. Empezamos con B  1 y en cada a vuelta (iteracin) multiplicamos la potencia por la base, para ir obteniendo las poo tencias correspondientes. Para pasar de base 10 a base b, dividimos sucesivamente el cociente entero entre la base, y vamos pegando, por la izquierda, los d gitos que obtengamos como el residuo. El diagrama correspondiente a estos bloques lo mostramos en la gura 2.15 (en dos partes).

Figura 2.15

Diagrama para pasar un nmero de base 10 a otra base u (1/2) " $ $ $ Mensaje: no se puede 'b 10 ' ' ' ' ' ' ' ' & ' aborta ' ' ' 'Obtener b ' ' ' ' ' ' ' ' ! ' ' ' ' ' ' ' ' %b 10 ' ' ' ' ' ' ' ' & ' " $ ' ' ' Principio 'B 10 Mensaje: no se puede ' ' ' ' ' ' & ' aborta ' ' ' 'Obtener B ' ' ' ' ' ' ' ' ! ' ' ' ' ' ' ' ' %B 10 ' ' ' ' ' ' ' ' ' ' 'Obtener nmeroB ' % u ' ' ' # ' $ ' ' ' ' iniciar potencia  1 ' 'Principio ' ' ' ' ' ' ' iniciar nmero10  0 u ' ' ' ' ' ' ' $ ' ' ' ' ' ' ' gito nmeroB mod 10 u ' ' 'd ' ' ' ' ' 'nmero10 nmero10 ' 'Calcular d ' u u ' ' gito & ' & ' ' Cambiar a (mientras pd potenciaq gito ' ' ' ' base 10 ' nmeroB 0q 'potencia  potencia B; ' ' ' u ' ' ' ' ' ' ' ' ' %nmeroB  nmeroB 10; ' ' u u & ' Cambio de ' ' $ ' base B a ' ' & ' ' base b ' ' ' 'Final ' ' u ' ' ' % %Escribir valor de nmero10 ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '

$ ' ' ' ' ' 2.3 Dise o estructurado ' n 60 ' ' ' ' ' ' ' ' Figura 2.15 Diagrama ' pasar un nmero de una base a otra para u (2/2) ' ' ' ' ' ' # $ ' ' ' ' nmerob 0 u ' 'Principio ' ' ' ' ' ' ' potencia 1 ' ' & Cambio de' ' ' $ ' base B a ' u 'residuo  nmero10 mod b & 'Cambiar a 'Calcular d base b ' &nmero pegar gito ' ' ' u b ' base b '(mientras ' ' ' nmero 0q ' residuo nmerob u ' ' u ' ' ' 10 ' %nmero nmero b ' ' ' ' u u ' 10 10 ' ' ' ' ' ' ' ! ' ' ' ' %Final ' ' ' ' ' ! ' ' '.Final Escribir nmerob u ' ' ' ' %

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 no o tienen encomendada ninguna tarea, por lo que aparecen vac Por ejemplo, el os. proceso de F inal de cambiar a base b podr escribir el resultado, pero esto ya lo a hace el F inal de todo el proceso; en este caso, no hacemos nada en el F inal del cambio a base b. 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 experiencia 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. Con esto queda terminado el algoritmo (y los diagramas correspondientes) para pasar un nmero de u una base a otra. Resumiendo y 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 el cmo cumple con sus responsabilidades cada objeto, cmo elaborar los mtodos o o e (no siempre tenemos presentes a todos los tipos de mtodos), debemos recurrir al e diseo estructurado. Hablaremos ms de este tema ms adelante en el curso El n a a mtodo de Warnier-Orr no proporciona mecanismos para analizar problemas de e naturaleza recursiva. Este tipo de soluciones estar dado por las frmulas mismas, a o o la descripcin de la solucin. o o

61

El proceso del software

Procedemos ahora a incursionar en otros problemas, donde utilizaremos la metodolog que acabamos de describir. a

Ejercicios
2.1.- Cules son las diferencias principales entre el anlisis orientado a objetos a a y el anlisis estructurado? a 2.2.- Tenemos el siguiente relato: La tienda de abarrotes vende productos que tienen un precio jo por unidad y productos que se venden por kilo y tienen precio por peso. Tambin preparan tortas de jamn, queso de puerco y e o salchicha, cada una con un precio distinto pero todas se preparan bsicamente igual. Cuando alguien compra algo, primero paga y a despus se le proporciona lo que pidi. Si lo que compra es una e o torta, sta se prepara al momento, mientras que los otros productos e simplemente se toman de los estantes y se entregan. Tiene una caja registradora donde se cobra y un lugar donde se entrega lo que se pidi. o (a) Subraya los sustantivos (objetos) y tacha los verbos (mtodos). e (b) Clasica a los objetos en clases. (c) Asigna a cada clase sus responsabilidades (qu le toca hacer a cada e quien). (d) Haz un diagrama de Warnier-Orr del proceso de compra de un producto. 2.3.- Tenemos el siguiente relato: La facultad de Ciencias ofrece las carreras de Actuar Biolog a, a, Ciencias de la Computacin, Ciencias de la Tierra, F o sica, Matemticas. Todas, excepto por la de F a sica, se cursan en 8 semestres; la de F sica en 9. Para obtener el t tulo el alumno tienen que cursar el nmero de semestres de la carrera y adems presentar u a un trabajo terminal. (a) Subraya los sustantivos (objetos) y tacha los verbos (mtodos). e

2. Ejercicios (b) Clasica a los objetos en clases.

62

(c) Asigna a cada clase sus responsabilidades (qu le toca hacer a cada e quien). (d) Haz un diagrama de Warnier-Orr del proceso de estudiar en la Facultad de Ciencias, incluyendo unicamente lo que se menciona en el texto. 2.4.- Haz el diagrama de Warnier que corresponde a pasar un nmero de base 16 u a base 2. 2.5.- Haz el diagrama de Warnier que corresponde a resolver una ecuacin de o 2 segundo grado de la forma ax bx c  0 (Nota: toma en cuenta que a  0 o b2 4ac 0). 2.6.- Haz el diagrama de Warnier que corresponde a resolver un sistema de ecuaciones con dos incgnitas: o ax by  c dx ey  f 2.7.- Si pensamos que para obtener el factorial de un nmero n lo que hacemos u es ir multiplicando un resultado parcial, que empieza en 1 por cada uno de los enteros entre 1 y n, decimos que el proceso es iterativo:
r e s u l t P a r c i a l es 1. Repite para j desde 2 hasta n : resultParcial resultaParcial Escribe el resultado

n.

Haz el diagrama de Warnier que corresponde a este pseudocdigo. o 2.8.- Supongamos que queremos obtener la suma de los cuadrados de los nmeros u impares entre el 1 y el 17. Haz el diagrama de Warnier que corresponde a este proceso. 2.9.- Deseas anotar los nombres de todos los estudiantes que estn inscritos en a ms de seis materias en la Facultad de Ciencias. Haz el diagrama de Warnier a que avise a los estudiantes que pasen a anotarse y el proceso de anotarse. 2.10.- Haz un diagrama de Warnier de los pasos que tienes que seguir para despertarte en la maana y salir de tu casa. n

Clases y objetos

3.1 Tarjetas de responsabilidades


Para ilustrar de mejor manera el proceso de anlisis y diseo procederemos a n a trabajar con un ejemplo concreto: el diseo de un reloj digital. Este ejemplo, n 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 1. Encontrar las clases y los objetos. 2. Determinar las responsabilidades. A partir de: Una descripcin o o especicacin del o problema. Produce: Una lista de objetos y un esquema para cada clase.

La lista de objetos. Esquema de clases con lista de funcioLa especicacin o nes miembro o mtodos, clasicados en e del problema. 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.

3. Determinar la colaboracin o entre objetos.

3.1 Tarjetas de responsabilidades Accin o 4. Determinar la accesibilidad de las funciones y datos. A partir de: El esquema de colaboracin y o responsabilidades.

64 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 digital consiste de una cartula de despliegue, con dos manecillas, 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 una 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 minutos manecilla de horas l mites valor de horas valor de minutos valor del reloj

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 use un entero para representarlo, lo mismo que el l mite; excepto que este ultimo debe ser constante porque una vez que se je para cada manecilla, ya no deber cambiar en esa manecilla. Las horas y los minutos, con su valor y l a mite correspondientes, pertenecen a la misma clase. Podemos entonces mostrar nuestra estructura en la gura 3.1.

65

Clases y objetos

Figura 3.1

Estructura de las clases de un reloj digital


Manecilla Datos: Funciones: valor l mite constructores: Poner el l mite e iniciar tiempo incrementa pon valor muestra horas Una manecilla con l mite 12 minutos Una manecilla con l mite 60 constructores incrementa pon valor muestra

Reloj Datos:

Funciones:

En la clase Manecilla tenemos dos variables, valor y LIMITE, 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 e le 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.2 en la a siguiente pgina. a
Usaremos la palabra get en estos casos, en lugar del trmino en espaol da, ya que en Java e n existe la convencin de que los mtodos de acceso a los atributos de una clase sean de la forma get o e seguidos del identicador del atributo empezado con mayscula. Similarmente en los mtodos u e de modicacin o actualizacin de los valores de un atributo la convencin es usar set seguido o o o del nombre del atributo escrito con mayscula. u
1

3.1 Tarjetas de responsabilidades

66

Figura 3.2

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

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

Figura 3.3

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

67

Clases y objetos

Figura 3.4

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 y avisa si alcanz el l o mite Pone valor a la manecilla Pone en una cadena 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, que llamamos el cliente): e Clase: Manecilla valor Datos l mite Mtodos Constructor e incrementa Reloj 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. Avisa si lo regres a o cero.

3.1 Tarjetas de responsabilidades Clase: setValor muestra Reloj Datos Cliente: Reloj Reloj

68 Descripcin: o Pone el valor 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 en una cadena

horas minutos Mtodos Constructor e incrementa setValor

usuario usuario usuario

muestra

usuario

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

Figura 3.5

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 l mite

(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

69

Clases y objetos

Figura 3.6

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 s 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 diseo Smalltalk, un lenguaje o n n 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.

3.2 Programacin en Java o

70

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 xexpresin regulary, que puede contener a su o vez conjuntos o elementos del lenguaje. Una expresin regular es una sucesin de o o s mbolos terminales y no terminales (como en cualquier gramtica), pero donde a extendemos la gramtica de la siguiente manera: usamos parntesis para agrupar a e cuando queramos que aparezca un parntesis tal cual lo marcaremos con negrie tas; el s mbolo | para denotar opciones; el s mbolo * para denotar que el grupo anterior se puede repetir cero o ms veces; y + para denotar que el grupo a anterior se puede repetir una o ms veces. A los elementos del lenguaje (represena tantes de los conjuntos, s mbolos terminales) los escribimos con negritas, tal como deben aparecer en el archivo fuente. Conforme vayamos avanzando quedar ms a a claro el uso de BNF. 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 lo trataremos en aquellos casos en que tenga sentido.

71

Clases y objetos

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 y un conjunto de atributos (campos, variables) que determinan el estado de cada objeto de e 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; no se involucra en lo absoluto con el cmo llevar e o a cabo esos servicios. Una interfaz corresponde a un contrato. Posteriormente podemos construir una o ms clases capaces de cumplir con ese contrato, donde a se describirn las (distintas) formas en que se van a llevar a cabo los servicios. a A esto ultimo le llamamos implementar a la interfaz. Trataremos de trabajar siempre a travs de interfaces, pues nos dan un nivel de abstraccin ms alto que e o a el que nos dan las clases -=-nos dicen el qu, no el cmo. Todo lo relativo a e o una aplicacin (los archivos de cdigo) se agrupa en un paquete que corresponde o o a un subdirectorio al que le asignamos un nombre. 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.7 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. e Los 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
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

3.2 Programacin en Java o

72

enunciados, como las declaraciones (los encabezados de un mtodo juegan el papel e de una declaracin). Por ejemplo, la sintaxis para el xaccesoy es como se ve en la o gura 3.8, mientras que un xidenticadory es cualquier sucesin de letras, d o gitos y carcter de subrayado, que empiece con letra o subrayado. a

Figura 3.7

Encabezado de una interfaz


Sintaxis: x declaracin de interfazy ::= xaccesoy interface xidenticadory { o xencabezado de mtodoy; e (xencabezado de mtodoy;)* e } ntica: Sema 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. Una o e interfaz, en general, no tiene declaraciones de atributos, sino unicamente de mtodos, de los cules unicamente se da el encabezado. Los encabezados e a de los distintos mtodos se separan entre s por un ; (punto y coma). El e 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.8

Sintaxis para el xaccesoy


Sintaxis: xacceso y ::= public | private | protected | 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 que corresponde a un nombre de archivo, 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. Cuando no aparece ningn tipo de acceso, ste es de paquete (package) u e y es el valor por omisin. Slo la pueden ver clases que se encuentren o o declaradas en el mismo subdirectorio (paquete). No existe la palabra reservada package para denominar este tipo de acceso. 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. 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.9. a

73

Clases y objetos

Figura 3.9

Reglas para la formacin de un xidenticadory o


Sintaxis: xidenticador y ::= (xletra y | )(xletray | xd y | )* gito 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 y consideradas de buena educacin en la comunidad de Java) son: o 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, cale u cula, mueve, copia seguido de uno o ms sustantivos. Cada uno de a 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 caracteres como se desee. El lenguaje, adems, distingue entre maysculas y a u minsculas no es lo mismo carta que Carta. u 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 3 programa . Utilizaremos de manera preponderante los comentarios hechos para JavaDoc, en particular para documentar interfaces, clases y mtodos. Los comentarios deben e
Si se est utilizando el editor de Emacs, se puede agregar automticamente la documentacin a a o de JavaDoc tecleando C-c C-v j o, equivalentemente, eligiendo con el ratn JDE Documentation o Add.
3

3.2 Programacin en Java o

74

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, y un e campo @version para anotar ah las modicaciones que vayamos realizando. La interfaz para nuestro reloj deber anunciar a los servicios que listamos para a el reloj en la gura 3.5 en la pgina 68 excepto por el constructor, y la interfaz a para la clase Manecilla debe listar los servicios que listamos en la gura 3.6 en la pgina 69 tambin excluyendo al constructor. Pospondremos por el momento la a e codicacin de los encabezados de los mtodos hasta que veamos este tema con o e ms detalle. La codicacin del encabezado de las interfaces para Reloj y Manecilla a o se encuentran en los listados 3.1 y 3.2.

Cdigo 3.1 Encabezado de la interfaz para Reloj o

ServiciosReloj

10 package R e l o j ; 20 / 30 I n t e r f a c e <code>S e r v i c i o s R e l o j </code> d e s c r i b e l o s s e r v i c i o s 40 que da un r e l o j d i g i t a l . 50 60 @ a u t h o r <a h r e f = m a i l t o : e l i s a v i s o @ g m a i l . com> E l i s a V i s o </a> 70 @ v e r s i o n 1 . 0 80 / 90 p u b l i c i n t e r f a c e S e r v i c i o s R e l o j { / L i s t a de m todos a d e s c r i b i r / e 300 } // S e r v i c i o s R e l o j

Cdigo 3.2 Encabezado de la interfaz para Manecilla o

ServiciosManecilla

100 package R e l o j ; 200 / 300 I n t e r f a c e <code>S e r v i c i o s M a n e c i l l a </code> d e s c r i b e l o s s e r v i c i o s 400 que da una m a n e c i l l a de un r e l o j d i g i t a l . 500 600 @ a u t h o r <a h r e f = m a i l t o : e l i s a v i s o @ g m a i l . com> E l i s a V i s o </a> 700 @ v e r s i o n 1 . 0 800 / 900 p u b l i c i n t e r f a c e S e r v i c i o s M a n e c i l l a { / M todos a i m p l e m e n t a r en l a c l a s e M a n e c i l l a / e 4800 } // S e r v i c i o s M a n e c i l l a

75

Clases y objetos

Notarn que en los comentarios de JavaDoc aparecen los nombres de las clases a entre las cadenas <code> </code>. Esta notacin corresponde a xml y es para o construir las pginas de web donde se describe a cada clase. a Veamos en la gura 3.10 la sintaxis y semntica del encabezado de una clase. a Esta es una descripcin parcial, ya que por el momento no tiene sentido ver la o denicin completa. o

Figura 3.10

Encabezado de una clase


Sintaxis: xdeclaracin de clasey ::= o xaccesoy class (

xidenticadory |p implements xidenticadory p,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 (variables, atributos, referencias) y a los mtodos que vamos a utilizar. El e xmtodo mainy, que en realidad tambin forma parte de las declaraciones, e e se usa para poder invocar a la clase desde el sistema operativo. Si la clase va a ser invocada desde otras clases, no tiene sentido que tenga este mtodo. e Sin embargo, muchas veces para probar que la clase funciona se le escribe un mtodo main, donde se invocan a los mtodos declarados. En Java todo e e identicador tiene que estar declarado para poder ser usado. Cuando un archivo que contiene interfaces o clases se compila bien aparecer en a 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 Java y el a que puede ser ejecutado o invocado. Vamos codicando lo que ya sabemos cmo. Tenemos dos interfaces, Servio

3.2 Programacin en Java o

76

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. (c) Mutantes o de modicacin. Son los que permiten modicar el estado del o 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 pone en la interfaz a los mtodos constructores, pues la e 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 del nombre del mtodo y una lista de los tipos e e

77

Clases y objetos

de los parmetros respetando el orden. Adems de la rma, en el mtodo se a a e marca de alguna manera el tipo de mtodo de que se trata. Esto lo revisaremos e conforme veamos los distintos tipos 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 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.11, donde las deniciones de xtipoy, xaccesoy e xidenticadory son como se dieron antes.

Figura 3.11

Encabezado para los mtodos de acceso e


Sintaxis:

x encabezado de mtodo de acceso y ::= e xaccesoy xtipoy xidenticadory ( xParmetros y) a


Semantica: 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 es el resultado o salida que proporciona el mtodo, seguido de la rma del mtodo, que e e incluye a los xParmetrosy que corresponden a la entrada que le vamos a a proporcionar al mtodo para que trabaje. El identicador del mtodo es e 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 corresponde al identicador que le dimos al atributo.

Los tipos que se manejan en Java pueden ser primitivos o denidos por el

3.2 Programacin en Java o

78

programador (de clase o tipo referencia). Un tipo primitivo es atmico esto es, o no contienen a otros campos o atributos y es aquel cuyas variables no se reeren a objetos. Se encuentran con un valor vlido directamente en la memoria donde se a ejecuta el programa. En la tabla 3.2 se encuentra una lista con los tipos primitivos y los rangos de valores que pueden almacenar.

Tabla 3.2

Tipos primitivos y sus rangos


Nombre del tipo boolean char byte short int long oat double Representacin o 16 bits 16 bits 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 Capacidad true o false Unicode 2.0 -128 ... 127 -32768...32767

216...216 1 263...263 1 2149 ...p2 223 q 2127 21074 ...p2 252 q 21023

Otro tipo de dato que vamos a usar mucho, pero que corresponde a una clase y no a un dato primitivo como en otros lenguajes, es el de las cadenas que consisten de una sucesin de caracteres. La clase se llama String. Las cadenas (String) son o cualquier sucesin de caracteres, menos el de n de l o nea, entre comillas. Los siguientes son objetos tipo String:
"Esta es una cadena 1 2 3 " ""

La primera es una cadena comn y corriente y la segunda es una cadena vac u a, 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:

79
"El lmite es: "+ LIM + "."

Clases y objetos

se guarda como

"El lmite es: 12."

Hablaremos mucho ms de la clase String en lo que sigue. a Nos falta por describir la parte correspondiente a xParmetrosy. En la gua ra 3.12 damos la sintaxis y semntica para la declaracin de esta parte del encaa o bezado de un mtodo. e

Figura 3.12

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 comas. Un parmetro marca lugar y a a 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. Cuando invocamos a un mtodo le tenemos que proporcionar (pasar ) el nmero e u y tipo de argumentos 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 a paso de parmetros: parmetros de entrada, parmetros de salida y parmetros a a a a tanto para entrada como para salida. A estos tres tipos se asocian mecanismos de paso de parmetros, entre los que estn: a a Paso por valor: Se evala al argumento y se pasa nada ms unas copia de ese u a valor; este tipo de paso de parmetro est asociado a los parmetros para a a a entrada. La evaluacin se hace en el momento de la llamada al mtodo, por o e lo que se hace lo antes posible. Paso por referencia: Se toma la direccin en memoria donde se encuentra la o variable y eso es lo que se pasa como argumento. Se piensa en pasar parmea tros por referencia cuando se les va a usar para salida. Evaluacin perezosa o por necesidad: No se evala el argumento hasta que o u se vaya usar dentro de la implementacin del mtodo; evita tambin evaluao e e ciones repetidas del mismo argumento; se dice que se evala el parmetro u a lo ms tarde posible. a Paso por nombre: Parecida a la evaluacin perezosa, se le pasa al mtodo el o e nombre del parmetro y lo evala en cada uso dentro del mtodo. Si en a u e el ujo de la ejecucin no aparece la expresin con el argumento, ste no o o e

3.2 Programacin en Java o

80

se evala. Se evala cada vez que aparece en el punto donde aparece, a u u diferencia de la evaluacin perezosa que lo hace una unica vez. o En el caso de Java todos los argumentos se pasan por valor, incluyendo a las referencias a los objetos. Eso quiere decir que no podemos modicar la referencia a un objeto, pero como tenemos la direccin del objeto podemos acudir a l y o e modicarlo. Aclararemos ms este punto cuando lo enfrentemos. a 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.4 en la pgina opuesta. a En general podemos pedirle a cualquier mtodo que regrese un valor y tendr e a entonces la sintaxis de los mtodos de acceso. Los mtodos de acceso para la clase e e Reloj, como los atributos son objetos de la clase Manecilla, regresan un valor que corresponde a la clase antes mencionada y que se muestra en el listado 3.3. No es claro, sin embargo, que queramos acceso a las manecillas como tales, por lo que no van a usarse estos mtodos. e

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

(ServiciosReloj)

100 package R e l o j ; 200 / 300 I n t e r f a c e <code>S e r v i c i o s R e l o j </code> d e s c r i b e l o s s e r v i c i o s 400 que da un r e l o j d i g i t a l . 500 600 @ a u t h o r <a h r e f = m a i l t o : e l i s a v i s o @ g m a i l . com> E l i s a V i s o </a> 700 @ v e r s i o n 1 . 0 800 / 900 p u b l i c i n t e r f a c e S e r v i c i o s R e l o j { 1000 1100 / 1200 Mtodo <code>g e t H o r a s </code >. D e v u e l v e l a r e f e r e n c i a a l o b j e t o e 1300 que r e p r e s e n t a a l a m a n e c i l l a de l a s h o r a s . 1400 @ r e t u r n v a l o r t i p o <code>M a n e c i l l a </code >: l a s h o r a s . 1500 / 1600 public Manecilla getHoras ( ) ; 1700 / 1800 1900 Mtodo <code>g e t M i n u t o s </code >. D e v u e l v e l a r e f e r e n c i a a l e 2000 o b j e t o que r e p r e s e n t a a l a m a n e c i l l a de l o s m i n u t o s . 2100 @ r e t u r n v a l o r t i p o <code>M a n e c i l l a </code >: l o s m i n u t o s . 2200 / 2300 public Manecilla getMinutos ( ) ; ... 3000 } // S e r v i c i o s R e l o j

81

Clases y objetos
ServiciosManecilla

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

100 package R e l o j ; 200 / 300 I n t e r f a c e <code>S e r v i c i o s M a n e c i l l a </code> d e s c r i b e l o s s e r v i c i o s 400 que da una m a n e c i l l a de un r e l o j d i g i t a l . 500 600 @ a u t h o r <a h r e f = m a i l t o : e l i s a v i s o @ g m a i l . com> E l i s a V i s o </a> 700 @ v e r s i o n 1 . 0 800 / 900 p u b l i c i n t e r f a c e S e r v i c i o s M a n e c i l l a { 1000 / 1100 1200 Mtodo <code>g e t V a l o r </code >. Accede a l a t r i b u t o e 1300 <code>v a l o r </code> y m u e s t r a e l v a l o r que t i e n e . 1400 @ r e t u r n v a l o r t i p o <code>i n t </code >: <code>v a l o r </code >. 1500 / 1600 public int getValor ( ) ; 1700 / 1800 1900 Mtodo <code>getLIMITE </code >. Accede a l a t r i b u t o e 2000 <code>LIMITE</code> y m u e s t r a e l v a l o r que t i e n e . 2100 @ r e t u r n v a l o r t i p o <code>i n t </code >: <code>LIMITE</code >. 2200 / 2300 p u b l i c i n t getLIMITE ( ) ; ... 4800 } // S e r v i c i o s M a n e c i l l a

Mtodos de implementacin e o
Estos mtodos son los que dan los servicios, pero que hacen algo ms que e a simplemente informar sobre el estado del objeto. Por ello, el mtodo muestra cuya e rma aparece en el listado 3.5 en la siguiente pgina es de este tipo. Es comn a u que este tipo de mtodos regresen un valor que indique algn resultado de lo que e u hicieron, o bien que simplemente avisen si pudieron o no hacer lo que se les pidi, o regresando un valor booleano. En el caso de que sea seguro que el mtodo va e a poder hacer lo que se le pide, sin contratiempos ni cortapisas, y no se desee que regrese algn valor4 que haya calculado, se indica que no regresa ningn u u valor, poniendo en lugar de xtipoy la palabra void. Por ejemplo, el encabezado del mtodo que muestra la Manecilla debe mostrar la posicin de la manecilla, lo e o que denotamos con una cadena y queda como se muestra en el listado 3.5 en la siguiente pgina. a
Un valor en este contexto se reere tambin a la referencia a un objeto de alguna clase e especicada, como es el caso de una cadena de caracteres.
4

3.2 Programacin en Java o

82
ServiciosManecilla

Cdigo 3.5 Mtodos de implementacin para Manecilla o e o

100 package R e l o j ; / . . . Documentaci n de l a i n t e r f a z . . . o 900 p u b l i c i n t e r f a c e S e r v i c i o s M a n e c i l l a { / . . . M todos r e g i s t r a d o s h a s t a a h o r a . . . / e ... 2500 / 2600 Mtodo <code>muestra </code >. Mu e st r a en una c a d e n a l a p o s i c i n e o 2700 de l a m a n e c i l l a . 2800 @ r e t u r n v a l o r t i p o a <code>S t r i n g </code >: e l v a l o r de l a 2900 manecilla . 3000 / 3100 public S t r i n g muestra ( ) ; ... 4800 } // S e r v i c i o s M a n e c i l l a

Otra vez aparecen entre <code> y </code> lo que va a aparecer en la pgina a web de la clase o interfaz con tipo de letra distinto. Tambin en el listado 3.6 mostramos el encabezado del mtodo de implemene e tacin muestra para la interfaz ServiciosReloj, que en este caso simplemente va a o mostrar en el dispositivo de salida el estado del reloj, por lo que no entrega ningn u valor.

Cdigo 3.6 Mtodos de implementacin de Reloj o e o

ServiciosReloj

100 package R e l o j ; / . . . Documentaci n de l a i n t e r f a z . . . o 900 p u b l i c i n t e r f a c e S e r v i c i o s R e l o j { / . . . M todos ya d e c l a r a d o s . . . / e ... 2500 / 2600 Mtodo <code>muestra </code >. ( de i m p l e m e n t a c i n ) . Mu e st r a en e o 2700 e l d i s p o s i t i v o de s a l i d a l a h o r a que t e n g a marcada e l r e l o j . 2800 / 2900 public void muestra ( ) ; ... 4300 } // S e r v i c i o s R e l o j

Mientras que el mtodo muestra de la manecilla muestra su valor en una cae dena, el mtodo con el mismo nombre del reloj va a hacer su trabajo sin regresar e ningn valor. Ninguno de los dos mtodos tiene parmetros, ya que toda la inforu e a macin que requerir es el estado del objeto, al que tienen acceso por ser mtodos o a e de la clase.

83

Clases y objetos

Mtodos mutantes o de modicacin e o


Los mtodos mutantes o de modicacin son, como ya mencionamos, aquee o llos que cambian el estado de un objeto. Generalmente tienen parmetros, pues a requieren informacin de cmo modicar el estado del objeto. Los mtodos que o o e incrementan y que asignan un valor son de este tipo, aunque el mtodo que incree menta no requiere de parmetro ya que el valor que va a usar como incremento a es la unidad (1). Muchas veces queremos que el mtodo tambin nos proporcioe e ne alguna informacin respecto al cambio de estado, como pudiera ser un valor o anterior o el mismo resultado; tambin podr e amos querer saber si el cambio de estado procedi sin problemas. En estos casos el mtodo tendr valor de regreso, o e a mientras que si no nos proporciona informacin ser un mtodo de tipo void. Por o a e ejemplo, el mtodo que incrementa, de la interfaz para Manecilla nos interesa saber e 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). En el caso de Reloj, o como el incremento no incide ms all de l, no hay necesidad de que regrese un a a e valor. Las rmas de estos mtodos se muestran en los listados 3.7 a continuacin e o y 3.8 en la siguiente pgina. a

Cdigo 3.7 Mtodos mutantes para Reloj o e


100 package R e l o j ; / 900 . . . d o c u m e n t a c i n de l a i n t e r f a z . . . o ... {

ServiciosReloj

public interface S e r v i c i o s R e l o j

/ . . . e n c a b e z a d o s h a s t a muestra . . . / ... 3100 3200 3300 3400 3500 3600 3700 3800 3900 4000 4100 4200 / Mtodo <code>i n c r e m e n t a </code >, i n c r e m e n t a e l r e l o j en s u e u n i d a d de t i e m p o ms peque n a . a / public void incrementa ( ) ; / Mtodo <code>s e t V a l o r </code> e s t a b l e c e nueva h o r a p a r a e l r e l o j . e @param n v o H o r a s de t i p o <code>i n t </code> nuevo v a l o r de h o r a s . @param nvoMins de t i p o <code>i n t </code> nuevo v a l o r de 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 nvoHoras , i n t nvoMins ) ;

4300 } // S e r v i c i o s R e l o j

3.3 Implementacin de los servicios (clases) o

84
ServiciosManecilla

Cdigo 3.8 Mtodos mutantes para Manecilla o e

100 package R e l o j ; / . . . d o c u m e n t a c i n de l a i n t e r f a z . . . o 900 public interface S er v i ci os Ma n ec i l l a { / . . . e n c a b e z a d o s h a s t a muestra . . . / ... 3300 / 3400 Mtodo <code>i n c r e m e n t a </code> i n c r e m e n t a en una u n i d a d e l e 3500 v a l o r de l a m a n e c i l l a . 3600 @ r e t u r n v a l o r t i p o <code>i n t </code >: 3700 0 s i no c o m p l e t una v u e l t a con e l i n c r e m e n t o . o 3800 1 s i c o m p l e t una v u e l t a c o m p l e t a . o 3900 / 4000 public int incrementa ( ) ; 4100 4200 / 4300 Mtodo <code>s e t V a l o r </code> m o d i f i c a e l v a l o r de <code>v a l o r </code >. e 4400 @param n v o V a l o r t i p o <code>i n t </code >, e l v a l o r que s e d e s e a 4500 registrar . 4600 / 4700 public void s e t V a l o r ( i n t nvoValor ) ; 4800 } // S e r v i c i o s M a n e c i l l a

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.

3.3 Implementacin de los servicios (clases) o


Ya que tenemos los servicios que se deben proveer para que tengamos un reloj y las manecillas del mismo, pasamos a construir las clases que van a implementar esos servicios. Lo primero que veremos es aquella informacin que determina el o estado de cada uno de los objetos que vayamos a querer construir. Entraremos ya al contexto de denir las clases Reloj y Manecilla para determinar el cmo y el con o qu. El cmo nos lo da la implementacin de los mtodos, junto con los mtodos e o o e e auxiliares y constructores. El con qu nos lo dan los atributos de las clases. e 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 la clase Reloj como la primera en el archivo, ya que ser invocada desde fuera del archivo. El archivo se llamar Reloj.java porque la a a

85

Clases y objetos

clase Reloj es la que puede existir como un todo, como ente auto contenido de la aplicacin. o

Atributos
Antes de denir la implementacin de los mtodos que listamos en las interfaces o e trabajemos con los atributos que dimos en las tarjetas, que son los que denen el estado de un objeto. Los sustantivos debern ser atributos (variables o constantes) a datos mientras que los verbos fueron mtodos procesos o clculos. Lo primero e a que tenemos que hacer es determinar el espacio en memoria para los objetos o datos primitivos que se encuentran en cada clase. Esto lo hacemos mediante una declaracin. En la declaracin especicamos el nombre que le queremos dar o o al atributo ya sea objeto o primitivo y el tipo que va a tener entero, tipo Manecilla, etc.. Tambin debemos especicar el acceso a cada atributo. Veamos la e sintaxis y semntica de una declaracin de atributo (dato, campo) en la gura 3.13. a o

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 e los 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 misma combinacin de acceso, modicador y tipo, y todas las variables de la lista o tendrn las mismas caracter a sticas. Al declararse un atributo, el sistema de la mquina le asigna una localidad, esto es, un espacio en memoria donde a 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 5 donde quedar el objeto que o a se asocie a esa variable.
El heap es un espacio de memoria que la Mquina Virtual de Java reserva para colocar a ah a todos los objetos que construye. Tiene un administrador de memoria que se encarga de reutilizar el espacio cuando sabe que un objeto ya no va a ser utilizado.
5

3.3 Implementacin de los servicios (clases) o

86

Declaremos los atributos que se presentan en la aplicacin que estamos armano do en los listados 3.9 y 3.10.

Cdigo 3.9 Declaracin de atributos de la clase Reloj o o


100 package R e l o j ; 200 / 300 C l a s e <code>R e l o j </code >. I m p l e m e n t a un r e l o j d i g i t a l . 400 @ a u t h o r <a h r e f = m a i l t o : e l i s a v i s o @ g m a i l . com> E l i s a V i s o </a> 500 @ v e r s i o n 1 . 0 600 / 700 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 { 800 private Manecilla horas ; // Para p o s i c i n de h o r a r i o o 900 private Ma n e c i l l a minutos ; // Para p o s i c i n de m i n u t e r o o ... 9100 } // c l a s e R e l o j

Reloj

Cdigo 3.10 Declaracin de atributos de la clase Manecilla o o

Manecilla

100 package R e l o j ; 200 / 300 C l a s e <code>M a n e c i l l a </code >. I m p l e m e n t a a cada una de l a s 400 m a n e c i l l a s d e l r e l o j . 500 @ a u t h o r <a h r e f = m a i l t o : e l i s a v i s o @ g m a i l . com> E l i s a V i s o </a> 600 @ v e r s i o n 1 . 0 700 / 800 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 { 900 p r i v a t e i n t v a l o r ; // Guarda l a p o s i c i n o 1000 p r i v a t e f i n a l i n t LIMITE ; // Guarda v a l o r m ximo ( 1) a ... 7900 } // C l a s e M a n e c i l l a

Las declaraciones de las l neas 800 y 900 en el listado 3.9, y 900 y 1000 en el listado 3.10 son declaraciones de atributos del tipo que precede al identicador. En las l neas 800 y 900 del listado 3.9 se estn declarando dos atributos de tipo a Manecilla y acceso privado, mientras que el tipo de la variable declarada en la l nea 900 del listado 3.10 es entero (int) tambin privado. En la l e nea 1000 del listado 3.10 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, es una constante a como veremos, este valor debe ser asignado en el constructor. Siguiendo las reglas de etiqueta de Java, el identicador tiene unicamente maysculas. En el u caso de los atributos de tipo Manecilla, debemos tener claro que nada ms estamos a declarando un atributo, una variable, no estamos construyendo el objeto. Esto quiere decir que cuando se construya el objeto de tipo Manecilla, la variable horas se referir a este objeto, contendr una referencia a un objeto de tipo Manecilla. a a

87

Clases y objetos

Como los objetos pueden tener muy distintos tamaos ser dif acomodarlos n a cil en el espacio de ejecucin del programa, por lo que se construyen siempre en un o espacio de memoria destinado a objetos, que se llama heap y del que hablaremos a detalle ms adelante; la variable asociada a ese objeto nos dir la direccin del a a o (referencia al) objeto en el heap. Cuando una variable representa a un objeto (por construir), el tipo de la variable es el de una referencia cuyo valor es una direccin o del heap. En esa direccin se encuentra el objeto construido. o Como mencionamos antes, en las interfaces no se registran mtodos auxiliares e o constructores. Pero en las clases que implementen a estas interfaces (puede haber ms de una por cada interfaz) debemos incluir a los mtodos constructores y a a e los mtodos auxiliares. La declaracin de la implementacin de un mtodo en una e o o e clase es casi igual que en las interfaces, excepto que al encabezado le sigue la implementacin del mtodo entre llaves. Veamos cmo queda lo que llevamos del o e o programa en los listados 3.11 abajo y 3.12 en la siguiente pgina6 . a

Cdigo 3.11 Esqueleto para la implementacin de Reloj o o


3500 3600 3700 3800 3900 5400 5500 5600 5700 5800 5900 6000 6200 6300 6400 6500 6600 6700 6800 6900 7000 7100 7400

Reloj (1/2)

/ Mtodo <code>muestra </code >. ( de i m p l e m e n t a c i n ) . Mu e st r a en e o e l d i s p o s i t i v o de s a l i d a l a h o r a que t e n g a marcada e l r e l o j . / public void muestra ( ) { / i m p l e m e n t a c i n / o } // f i r m a : m u e s t r a ( ) / Mtodo <code>i n c r e m e n t a </code >, i n c r e m e n t a e l r e l o j en s u e u n i d a d de t i e m p o ms peque n a . a / public void incrementa ( ) { / i m p l e m e n t a c i n / o } // f i r m a : i n c r e m e n t a ( ) / Mtodo <code>s e t V a l o r </code> e s t a b l e c e nueva h o r a p a r a e l r e l o j . e @param n v o H o r a s de t i p o <code>i n t </code >, e l nuevo v a l o r p a r a l a s horas . @param nvoMins de t i p o <code>i n t </code> e l nuevo v a l o r p a r a l o s minutos . / p u b l i c v o i d s e t V a l o r ( i n t nvoHoras , i n t nvoMins ) { / i m p l e m e n t a c i n / o } // f i r m a : s e t V a l o r ( i n t , i n t )

En adelante unicamente listamos, respetando la numeracin, las l o neas involucradas; el orden exacto est dado en trminos de lograr una mejor distribucin en el libro. a e o

3.3 Implementacin de los servicios (clases) o

88
Reloj (2/2)

Cdigo 3.11 Esqueleto para la implementacin de Reloj o o


7600 7700 7800 7900 8000 8200 8300 8400 8500 8600 8700 8800 9000 / Mtodo <code>g e t H o r a s </code : R e g r e s a a l a m a n e c i l l a . e @ r e t u r n v a l o r de t i p o <code>M a n e c i l l a </code >. / public Manecilla getHoras () { / i m p l e m e n t a c i n / o } / Mtodo <code>g e t M i n u t o s </code >: R e g r e s a a l a M a n e c i l l a . e @ r e t u r n v a l o r de t i p o <code>M a n e c i l l a </code >. / public Manecilla getMinutos () { / i m p l e m e n t a c i n / o }

Cdigo 3.12 Esqueleto para la implementacin de Manecilla o o


3300 3400 3500 3600 3700 3800 3900 4000 4300 4400 4500 4600 4700 4800 4900

Manecilla (1/2)

/ Mtodo <code>i n c r e m e n t a </code> i n c r e m e n t a en una u n i d a d e l e v a l o r de l a m a n e c i l l a . @ r e t u r n v a l o r t i p o <code>i n t </code >: 0 s i no c o m p l e t una v u e l t a con e l i n c r e m e n t o . o 1 s i c o m p l e t una v u e l t a c o m p l e t a . o / public int incrementa () { / i m p l e m e n t a c i n / o } // f i r m a : i n c r e m e n t a ( )

/ Mtodo <code>s e t V a l o r </code >, a c t u a l i z a v a l o r de l a m a n e c i l l a . e @param n v o v a l o r t i p o <code>i n t </code >: nuevo v a l o r p a r a la manecilla . / public void s e t V a l o r ( i n t v a l o r ) { / i m p l e m e n t a c i n / o 5200 } // f i r m a s e t V a l o r ( i n t ) 5300 / 5400 5500 Mtodo <code>g e t V a l o r </code >: 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 . e 5600 @ r e t u r n un <code>i n t </code >. 5700 / 5800 public int getValor () { / i m p l e m e n t a c i n / o 6000 } // f i r m a : g e t V a l o r ( )

89

Clases y objetos
Manecilla (2/2)

Cdigo 3.12 Esqueleto para la implementacin de Manecilla o o


6200 6300 6400 6500 6600 6700 6900 7000 7100 7200 7300 7400 7500 7600 7900

/ Mtodo <code>getLIMITE </code >. Accede a l a t r i b u t o e <code>LIMITE</code> y m u e s t r a e l v a l o r que t i e n e . @ r e t u r n v a l o r t i p o <code>i n t </code >. E l v a l o r d e l a t r i b u t o . / p u b l i c i n t getLIMITE ( ) { / i m p l e m e n t a c i n / o } // f i r m a : getLIMITE ( ) / Mtodo <code>muestra </code >. Mu e st r a en una c a d e n a l a p o s i c i n e o de l a m a n e c i l l a . @ r e t u r n v a l o r t i p o <code>S t r i n g </code >: e l v a l o r de l a manecilla . / public S t r i n g muestra ( ) { / i m p l e m e n t a c i n / o } // f i r m a : m u e s t r a ( )

Como declaramos que nuestras clases Reloj y Manecilla implementan, respectivamente, a las interfaces ServiciosReloj y ServiciosManecilla, estas clases tendrn a que proporcionar las implementaciones para los mtodos que listamos en las ine terfaces correspondientes. El esqueleto construido hasta ahora se puede ver en los listado 3.12. En las interfaces no se presentan otra categor de mtodos, ya que las intera e faces no describen objetos, sino unicamente los contratos a los que se obligan las clases que los implementen y que corresponden a mtodos pblicos a los que se e u puede acceder desde fuera de las clases. 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.

3.3 Implementacin de los servicios (clases) o

90

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 e de otros tipos de mtodos. Esta se puede ver en la gura 3.14. e

Figura 3.14

Encabezado de un constructor
Sintaxis: xconstructory ::=xaccesoy xidenticador de Clasey ( xParmetrosy ) { a ximplementaciny o } xParmetrosy ::=xparmetroy(, xparmetroy) | a a a xparmetroy ::= xtipoy xidenticadory a Semantica: 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 Un constructor es el que permite la creacin o construccin (instancing) de un o o objeto (un ejemplar) 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, que son los que denen el estado de un objeto). Puede recibir para la construccin datos en la forma de parmetros. o a 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 los listados 3.13 y 3.14 en la pgina opuesta. Los constructores siempre tienen a el mismo nombre que la clase de la que son constructores. No es necesario decir qu tipo de valor regresan, porque regresan (construyen) a un objeto de su clase. e

91

Clases y objetos
Reloj

Cdigo 3.13 Firmas de los constructores para la clases Reloj o


1100 1200 1300 1400 1500 1600 1700 2000 2100 2200 2300 2400 2500 2600 2700 2800 2900 3000 3300

/ Crea un e j e m p l a r nuevo de <code>R e l o j </code >. Pone r a n g o a l a s m a n e c i l l a s e i n i c i a en h o r a c e r o . @param limH de t i p o <code>i n t </code >, e l l m i t e p a r a h o r a s . @param limM de t i p o <code>i n t </code >, e l l m i t e p a r a m i n u t o s . / p u b l i c R e l o j ( i n t limH , i n t limM ) { / i m p l e m e n t a c i n / o } // f i r m a : R e l o j ( i n t , i n t ) / Crea un e j e m p l a r nuevo de <code>R e l o j </code >. Pone r a n g o a l a s m a n e c i l l a s e i n i c i a con h o r a p r e e s t a b l e c i d a . @param limH de t i p o <code>i n t </code> . @param limM de t i p o <code>i n t </code> . @param h r s de t i p o <code>i n t </code> . @param mins de t i p o <code>i n t </code> . / 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 ) { / i m p l e m e n t a c i n / o } // f i r m a : R e l o j ( i n t , i n t , i n t , i n t )

Cdigo 3.14 Firmas de los constructores para la clase Manecilla o


1300 1400 1500 1600 1700 1800 2000 2100 2200 2300 2400 2500 2600 2700 2800 3100

Manecilla

/ Crea un e j e m p l a r nuevo de <code>M a n e c i l l a </code >. E s t a b l e c e e l rango . @param l i m de t i p o <code>i n t </code >, e l r a n g o [ 0 . . l i m 1 ] . / Manecilla ( int lim ) { / i m p l e m e n t a c i n / o } // M a n e c i l l a ( i n t , i n t ) / Crea un e j e m p l a r nuevo de <code>M a n e c i l l a </code >. E s t a b l e c e e l rango y l a p o s i c i n i n i c i a l . o @param l i m de t i p o <code>i n t </code >, r a n g o [ 0 . . l i m 1] @param v a l de t i p o <code>i n t </code >, v a l o r a e s t a b l e c e r . / M a n e c i l l a ( i n t lim , i n t v a l ) { / i m p l e m e n t a c i n / o } // M a n e c i l l a ( i n t , i n t )

3.3 Implementacin de los servicios (clases) o

92

Es importante notar que la rma de un mtodo consiste unicamente del nombre e del mtodo junto con los tipos de los parmetros; el encabezado de un mtodo es e a e el que contiene al tipo de valor que regresa el mtodo precediendo a la rma. Por e lo tanto, los dos encabezados que se encuentran en el listado 3.15 tienen la misma rma y el compilador dar un mensaje de mtodo duplicado, aunque el nombre a e de los parmetros sea distinto. a

Cdigo 3.15 Mtodos con la misma rma o e


public R e l o j ( i nt hrs , // Firma : R e l o j ( i n t public R e l o j ( i n t lim1 , // f i r m a : R e l o j ( i n t int , int int , int mins , , int lim2 , , int i n t limH , i n t limM ) , int ) int val1 , int val2 ) , int )

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 y que podr codicarse de la siguiente manera: o a
p u b l i c M a n e c i l l a ( ) { / c u e r p o v a co / }

El estado inicial que da el constructor por omisin al objeto es el valor cero (0) o en los atributos numricos, falso en los atributos lgicos y referencia nula (null) e o en los atributos que son objetos (referencias a objetos).

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 de la aplicacin, aunque tambin puede o o e usarse para probar que determinadas clases trabajan bien, invocndolas desde a el mtodo main. El sistema operativo (la mquina virtual de Java) reconoce al e a mtodo main y si se invoca a una clase cualquiera desde el sistema operativo, e ste procede a ejecutar ese mtodo; si la clase no tiene un mtodo main la ejecucin e e e o abortar con un mensaje de error. El encabezado para este mtodo se encuentra a e en el listado 3.16 en la pgina opuesta. a

93

Clases y objetos

Cdigo 3.16 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 el mtodo main es un e mtodo que existe slo para la clase, no para cada objeto de la misma. Cuando un e o atributo o un mtodo tienen este modicador, quiere decir que todos los objetos e que se construyan de esa misma clase van a compartir a ese atributo o mtodo. e Finalmente, el parmetro que tiene es un arreglo (denotado por [ ]) de cadenas a (String) que son las cadenas que aparecen en la l nea de comandos cuando se invoca desde el sistema operativo (un arreglo es simplemente una sucesin, un vector de o datos, que se distinguen entre s por la posicin que ocupan en la sucesin; se o o puede obtener el valor de cada elemento dando, entre corchetes, el lugar que ocupan, como en args[0] que nos dar el primer elemento). La implementacin a o de este mtodo es como la de cualquier otro. Cuando veamos implementacin en e o general daremos las restricciones que presenta por tratarse de un mtodo esttico e a (static).

3.3.1.

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, asignado a una variable particular, qu alcance tiene, esto es, desde dnde e o puede el programa referirse a cada uno de los nombres y cmo distingue entre o distintas variables con el mismo nombre. La pista ms importante para determinar el alcance de un identicador est daa a da por las parejas de llaves que abren y cierran las deniciones de la clase y de cada uno de los mtodos (ms adelante veremos tambin alcances denidos en ale a e gunos enunciados). Para las que corresponden a la clase, todos los nombres que se encuentran en las declaraciones no estticas dentro de la clase son accesibles desde a cualquier mtodo no esttico de la misma clase. Adicionalmente, los nombres que e a tengan acceso pblico o de paquete son accesibles tambin desde fuera de la clase. u e En el caso de los parmetros, las variables pueden ser referidas unicamente dentro a del mtodo del que son parmetros. Si algn identicador usado como nombre e a u de atributo se usa como nombre de parmetro, dentro del mtodo en cuestin el a e o identicador se referir al parmetro, no al atributo. a a

3.3 Implementacin de los servicios (clases) o

94

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 LIMITE. 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.15.

Figura 3.15

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 un operador binario, injo, de seleccin y asocia de izquiero da a derecha. Lo usamos para identicar: el identicador que se encuentra a su derecha pertenece al objeto a su izquierda. Tambin podemos usarlo e para identicar a alguna clase que pertenezca a un paquete. En el caso de un identicador de mtodo, ste deber presentarse con los argumentos e e a correspondientes entre parntesis. la xreferencia de objetoy puede aparecer e en una variable o como resultado de una funcin que regrese como valor o una referencia, que se encuentre en el alcance de este enunciado. Podemos pensar en el . como un operador que trabaja sobre referencias. Si tenemos en la clase Reloj dos objetos que se llaman, respectivamente, horas y minutos, podremos acceder a sus mtodos pblicos como incrementa como se e u muestra en el listado 3.17.

Cdigo 3.17 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 suponiendo que se invoca, forzosamente, con algn objeto. Entonces, u

95

Clases y objetos

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 supone 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, siempre y cuando el e nombre del atributo no aparezca como parmetro de ese mtodo. a 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. Como ya mencionamos, si en e la clase existe algn atributo cuyo nombre sea el mismo que el del parmetro, el u a nombre del parmetro oculta al nombre del atributo y para referirse al atributo a dentro del mtodo se tendr que usar al selector this vase el listado 3.18. e a e

Cdigo 3.18 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 { Ma 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 e R e l o j

6400 6500 6600 7400 9100

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 uso de this e e en las l neas 6500 y 6600 .

3.3.2.

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 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

3.3 Implementacin de los servicios (clases) o

96

lo que tienen que hacer. En general un mtodo va a consistir de su encabezado y e una lista de enunciados entre llaves, como se puede ver en la gura 3.16.

Figura 3.16

Sintaxis para la implementacin de un mtodo o e


Sintaxis: ximplementaciny ::= xLista de enunciadosy o xLista de enunciadosy ::=xenunciado y xLista de enunciadosy | xenunciadoy ::= xenunciado simple y; | xenunciado compuesto y xenunciado simpley ::= xdeclaracin localy | xinvocacin de mtodo y o o e | xenunciado de asignaciny o | return | return xexpresiny o ntica: Sema 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. u Los enunciados pueden ser simples o compuestos7 . Un enunciado simple puede ser una invocacin a un mtodo, una declaracin de variables, una o e o asignacin de valor a una variable o salir del mtodo regresando (posio e blemente) un valor. Noten que todos los enunciados 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 o un mtodo, as como los parmetros formales, van a ser accesibles (recoe a nocidas) unicamente dentro de la implementacin del mtodo en cuestin, o e o a diferencia de las variables de la clase atributos que van a ser accesibles (reconocidos) en las implementaciones de cualquiera de los mtodos de la e clase.

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 a e como los parmetros se consideran como variables locales se estar repitiendo a a un identicador en el mismo alcance, lo que causar un error en el momento de a compilar. La sintaxis para una declaracin se puede ver en la gura 3.17 en la o pgina opuesta. a
No entraremos por ahora a lo que es un enunciado compuesto, ya que todav no los vamos a a usar.
7

97

Clases y objetos

Figura 3.17

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 caracteres para que den el mensaje 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 en realidad, slo salida. Este punto lo abordaremos o cuando implementemos el mtodo. e

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


3900 4000 4100 5400 / Mtodo <code>muestra </code >. ( de i m p l e m e n t a c i n ) . . . e o public void muestra ( ) { // v a r i a b l e s a u x i l i a r e s p a r a m o s t r a r a l u s u a r i o 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 } // f i r m a : m u e s t r a ( )

Reloj

La entrada y salida de Java es un poco complicada para ser manejada por principiantes. Usaremos en una primer instancia un dispositivo de salida, la consola, que Java proporciona automticamente. Es un objeto de la clase System y se a llama out. En Java a los dispositivos de entrada y salida se les denomina archivos, independientemente de si van a funcionar en la pantalla, la impresora, el dispositivo USB o el disco. En el caso del archivo System.out no hay que declararlo, sino que podemos usarlo directamente. Este objeto es de una clase de archivos

3.3 Implementacin de los servicios (clases) o

98

que permiten escribir caracteres en la consola y que cuenta con varios mtodos e que discutiremos ms adelante. Por todo lo anterior, aunque el archivo System.out a no tenga que declararse en ningn punto de nuestra aplicacin, las cadenas que u o queremos construir s deben aparecer como variables locales del mtodo que va a e mostrar al reloj.

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 por lo que quedan como se muestra en el listado 3.20.

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


5800 5900 6000 6700 6800 6900 public int getValor () { return v a l o r ; } // f i r m a : g e t V a l o r ( ) ... p u b l i c i n t getLIMITE ( ) { r e t u r n LIMITE ; } // f i r m a : getLIMITE ( )

Manecilla

El enunciado de asignacin o
Tal vez el xenunciado simpley ms importante es el xenunciado de asignaciny, a o ya que va a ser el que nos va a permitir asignarle un estado inicial a un objeto y la posibilidad de cambiar ese estado. Tambin es el que nos permite construir objetos e y asociar una variable a cada objeto que construimos. 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.18 en la pgina opuesta. a

99

Clases y objetos

Figura 3.18

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 decir que el xenunciado de asignaciny consiste de dos partes, lo o 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 La sintaxis de la expresin para la construccin de objetos se encuentra en la o o gura 3.19.

Figura 3.19

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.20 en la siguiente pgina. a

3.3 Implementacin de los servicios (clases) o

100

Figura 3.20

Invocacin de mtodo o e
Sintaxis: xinvocacin de mtodoy ::= xnombre del mtodoy(xArgumentosy ) o e e xArgumentosy ::= xargumentoy (,xargumentoy)* | xargumentoy ::= xexpresiny 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 indica a o e 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. El operador new nos regresa una direccin en el heap donde qued construido o o el objeto (donde se encuentran las variables atributos del objeto). Tenemos que guardar esa referencia en alguna variable del tipo del objeto para que se pueda usar. Si nos lanzamos a programar los constructores de la clase Reloj, lo hacemos creando (construyendo, instancing) a las manecillas correspondientes. Presentamos primero los constructores para la clase Manecilla, que est compuesta a unicamente de valores primitivos, que no se tienen que construir ya que se crean cuando el objeto se construye, por lo que la asignacin basta. La implementacin o o se encuentra en el listado 3.21.

Cdigo 3.21 Constructores de la clase Manecillas o


1800 1900 2000 2100 2800 2900 3000 3100 public M a n e c i l l a ( i n t l i m i t e ){ LIMITE = l i m i t e ; / v a l o r e m p i e z a en 0 , p o r s e r p r i m i t i v o / } ... M a n e c i l l a ( i n t lim , i n t v a l ) { LIMITE = l i m ; // Rango p a r a l a m a n e c i l l a valor = val ; // p o s i c i n i n i c i a l i n i c i a l o } // f i r m a : M a n e c i l l a ( i n t , i n t )

Manecilla

La implementacin de los constructores para la clase Reloj, que requieren de o la construccin de objetos, se pueden ver en el listado 3.22 en la pgina opuesta. o a

101

Clases y objetos
Reloj { // P r i m e r p a r m e t r o a // Segundo p a r m e t r o a

Cdigo 3.22 Constructores de la clase Reloj o


1700 1800 1900 2000 p u b l i c 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 ) ; 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 ) ...

3000 3100 3200 3300

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 ) 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 )

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 a e e lo que se tiene que hacer, por lo que obviaremos los comentarios para aligerar los listados8 , excepto cuando valga la pena aclarar algo. Para la implementacin o de estos mtodos utilizaremos ampliamente expresiones aritmticas, para poder e e colocarlas del lado derecho de una asignacin. Por ello, conviene primero revisar o cmo son las expresiones aritmticas en Java. o e

3.4 Expresiones en Java


Una expresin en Java es cualquier enunciado que nos regresa (calcula) un o valor. Por ejemplo, new Manecilla(limH) es una expresin, puesto que nos regresa o el lugar (direccin, referencia) donde el sistema construy a un objeto de la clase o o Reloj. Podemos clasicar a las expresiones de acuerdo al tipo del valor que regresen. Si calculan un valor numrico entonces tenemos una expresin aritmtica; si como e o e resultado del clculo dan falso o verdadero tenemos una expresin booleana; si a o calculan o construyen una cadena de caracteres tenemos una expresin tipo String o de cadenas. Tambin podemos hacer que las expresiones calculen (construyan) e un objeto de determinada clase. Cuando escribimos con papel y lpiz una expresin aritmtica tenemos, en a o e general, dos dimensiones en las cuales movernos: una vertical y otra horizontal.
Cuando se introdujeron los encabezados, tanto en las interfaces como en las clases, se comentaron con JavaDoc, por lo que sugerimos consulten esos listados de ser necesario.
8

3.4 Expresiones en Java

102

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 la expresin. o Cuando escribimos una expresin para un programa de computadora no cono tamos con estos niveles, sino que tenemos que poner en un solo nivel en una unica l nea la expresin: si bien vamos a usar, en general, notacin inja, debemos eso o cribir la expresin de tal manera que todo se encuentre en la misma l o nea, pero manteniendo la aridad, asociatividad y precedencia de la expresin matemtica. o a La aridad nos habla del nmero de operandos y, por lo tanto, de a quin afecta u e un operador dado. La asociatividad nos dice, si tenemos la presencia de un mismo operador de manera consecutiva, por ejemplo en a b c d, en qu orden se van e 9 evaluando las sumas , mientras que la precedencia se reere al orden en que se tienen que evaluar las subexpresiones con operadores distintos. Cada operador tiene una precedencia y asociatividad, pero se pueden alterar stas usando parntesis. e e Los parntesis cumplen dos propsitos: 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 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 x1 x 1 x x x{px 1q x {x 1

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
En los lenguajes de programacin, como al evaluar una expresin podemos cambiar el valor o o de alguno de los operandos, no siempre vamos a tener asociatividad como en matemticas. a Tambin tenemos el problema de los redondeos donde el resultado depende del orden en que se e hagan.
9

103

Clases y objetos

Otra diferencia fuerte entre escribir frmulas o expresiones con papel y lpiz, o a y escribirlas en un programa, es que la multiplicacin siempre debe ser expl o cita en el programa: 4ac 3px 2y q debe escribirse debe escribirse 4ac 3 px 2 y q

Finalmente, son pocos los lenguajes de programacin que tienen como operador la o 2 exponencial, por lo que expresiones como b se tendrn que expresar en trminos a e de la multiplicacin de b por s misma, o bien usar alguna funcin (en Java a o o las funciones se les llama mtodos) como el que usamos para ra cuadrada, que e z proporcione el lenguaje o alguna de sus bibliotecas. La famosa frmula para la o solucin de una ecuacin de segundo grado quedar entonces o o a c b b2 4ac x1  pb M ath.sqrtppb bq p4 a cqqq{p2 aq x1  2a 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 calcularla). Despus se resta este resultado de b, se obtiene el producto de e 2 a y lo ultimo que se hace es la divisin. Si no usramos parntesis excepto o a e por los que tienen que aparecer si se usa alguna funcin para que no d un error o e de sintaxis, la expresin se interpretar as o a :

c2
b

4ac
2

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

Sin embargo en la primera expresin no todos los parntesis son necesarios. Poo e demos eliminar a muchos de ellos, dejando que la precedencia de los operadores funcione; de se as ser suciente con escribir , a x1

 b

c2
b 2a

4ac

x1  pb M ath.sqrtpb b 4 a cqq{p2 aq,

eliminando los parntesis alrededor de b b y 4 a c, ya que la multiplicacin e o tiene mayor precedencia que la resta. 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 2) 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 clao e se Math), listados en orden de precedencia y con su asociatividad y nmero de u

3.4 Expresiones en Java

104

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.

Tabla 3.3

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 binario injo binario injo binario injo

1/2

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 resta corrimiento de bits a la izquierda llenando con ceros corrimiento de bits a la derecha propagando el signo corrimiento de bits a la derecha llenando con cero relacional menor que relacional menor o igual que relacional mayor que

Prec 1

2 3 4

5 6

(contina en la siguiente pgina) u a

105

Clases y objetos

Tabla 3.3

Operadores de Java
Operandos 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

2/2 (Contina de la pgina anterior) u a

S mbolo

instanceof

&

 !

|| xexp logy?xexpy :xexpy     { %   


&

&&

binario injo binario injo

binario injo binario injo binario injo

 |

Descripcin o relacional mayor o igual que relacional ejemplar de relacional, igual a relacional, distinto de AND de bits XOR de bits OR de bits AND lgico o OR lgico o Condicional aritmtica e asignacin o auto suma y asignacin o auto resta y asignacin o auto producto y asignacin o auto divisin y asignacin o o auto mdulo y asignacin o o auto corrimiento derecho con propagacin y o asignacin o auto corrimiento izquierdo y asignacin o auto corrimiento 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

Prec

8 9 10 11 12 13 14 15

15

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

3.4 Expresiones en Java

106

Estamos ya en condiciones de escribir prcticamente todas las implementacioa nes de 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, por lo que los revisamos en el apartado relacionado con la asignacin de valores y se encuentran en los o listados 3.21 y 3.22 en la pgina 101. Hay que recordar que estos mtodos, por ser a e constructores, de hecho regresan 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. Se encuentran implementados en los listados 3.20 en la pgina 98. Los mtodos de acceso de la clase Reloj se encuentran en el a e listado 3.23.

Cdigo 3.23 Mtodos de acceso de la clase Reloj o e


8000 8100 8200 8800 8900 9000 public Manecilla getHoras () { return horas ; } ... public Manecilla getMinutos () { return minutos ; }

Reloj

Implementacin de los mtodos de manipulacin o e o


Estos mtodos corresponden a todos aquellos 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.26 en la pgina opuesta. a

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


6000 6100 6200

Reloj (1/2)

public void incrementa ( ) { horas . s e t V a l o r ( horas . getValor () + minutos . incrementa ( ) ) ; } // f i r m a : R e l o j . i n c r e m e n t a ( )

107

Clases y objetos
Reloj (2/2)

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


71 72 73 74 p u b l i c v o i d s e t V a l o r ( i n t nvoHoras , i n t nvoMins ) { h o r a s . s e t V a l o r ( n v o H o r a s ) ; // 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 ( nvoMins ) ; // p i d e a s m i n u t o s que cambie } // f i r m a : R e l o j . s e t V a l o r ( i n t , i n t )

En el caso de la clase Manecilla, el trabajo de estos dos mtodos es ms come a plicado, pues debe vigilar que la posicin de las manecillas sea la correcta (entre o 0 y LIMITE-1). En el caso del mtodo incrementa adems debe avisar si dio o e a no una vuelta completa. La implementacin de los mtodos se encuentra en el o e listado 3.26.

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


4000 4100 4200 4300 4400 4500 4600 5000 5100 5200 5300

Manecilla

public int incrementa () { v a l o r ++; i n t d i o V u e l t a = v a l o r / LIMITE ; // V e r i f i c a r s i d i o l a v u e l t a v a l o r % LIMITE ; = // R e g r e s a r l a a l r a n g o // [ 0 . . LIMITE 1] return dioVuelta ; // A v i s a r s i d i o l a v u e l t a } // f i r m a : M a n e c i l l a . i n c r e m e n t a ( ) ... public void s e t V a l o r ( i n t nvoValor ) { v a l o r = n v o V a l o r % LIMITE ; // V i g i l a r que e s t e // en r a n g o [ 0 . . LIMITE 1] } // f i r m a : M a n e c i l l a . s e t V a l o r ( i n t )

Llenado de los mtodos de implementacin e o


Los mtodos de manipulacin, como su nombre lo indica, manipulan, conviere o ten, combinan el valor de uno o ms atributos para proporcionar al usuario una a respuesta o realizar una accin, sin cambiar el estado del objeto. El mtodo mueso e tra de la clase Manecilla es de este tipo y unicamente tiene que convertir a cadena (String) el valor numrico que tiene en su atributo valor. La implementacin del e o mtodo se encuentra en el listado 3.27. e

Cdigo 3.27 Mtodos de implementacin de la clase Manecilla o e o


7700 7800 7900 public S t r i n g muestra () { r e t u r n "" + v a l o r ; // Para o b l i g a r a c o n v e r t i r a c a d e n a } // f i r m a : M a n e c i l l a . m u e s t r a ( )

Manecilla

3.4 Expresiones en Java

108

Por ultimo tenemos el mtodo que muestra el reloj en la pantalla y que co e rresponde al mtodo muestra de la clase Reloj. Como ya mencionamos antes, este e mtodo debe combinar los valores de sus manecillas en una cadena e imprimirla e en la pantalla. Ya vimos que todo programa de Java proporciona al archivo System.out para salida sin que lo tengamos que declarar. As que lo podemos usar directamente en cualquier momento. Vamos a utilizar, por el momento, unicamente dos mtodos e de la clase System.out, el que escribe en la consola y el que al terminar de escribir salta al principio del siguiente rengln. Sus rmas se encuentran a continuacin: o o
System . o u t . p r i n t ( S t r i n g ) System . o u t . p r i n t l n ( S t r i n g ) // E s c r i b e en l a t e r m i n a l l a c a d e n a dada // E s c r i b e l a c a d e n a en l a t e r m i n a l y // da un s a l t o de l n e a .

Con el uso de un dispositivo de salida, la implementacin de los mtodos se o e puede hacer para que escriban directo a la consola. El cdigo de este mtodo se o e encuentra en el listado 3.28.

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


3900 4000 4100 3830 3840 3850 3860 3870 3880 3890 3900 3910 3920 3930 3940 3950 3960 public void muestra ( ) { // v a r i a b l e s a u x i l i a r e s p a r a m o s t r a r a l u s u a r i 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 " ; // Para i n t e r c a l a r con m e n s a j e 2 = " horas con " ; // l o s v a l o r e s m e n s a j e 3 = " minutos " ; / Se u s a System . o u t p a r a i m p r i m i r d i r e c t o en p a n t a l l a y s e u s a e l m todo p r i n t l n que t e r m i n a l a l i n e a e con un cambio de r e n g l n o / System . o u t . p r i n t l n ( m e n s a j e 1 + h o r a s . g e t V a l o r ( ) + mensaje2 + minutos . g e t V a l o r ( ) + mensaje3 ) ; / E l o p e r a d o r + cuando t r a b a j a con c a d e n a s o b l i g a a l o s n meros a c o n v e r t i r s e en c a d e n a y l a s pega . u / } // f i r m a : m u e s t r a ( )

Reloj

Uso de las clases denidas


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.29 en la pgina opuesta. a

109

Clases y objetos
UsoReloj

Cdigo 3.29 Clase usuaria de la clase Reloj o


100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400 2500 2600 2700 2800 2900 3000 3100 3200 3300

package R e l o j ; / C l a s s <code>U s o R e l o j </code> p a r a p r o b a r e l f u n c i o n a m i e n t o de l a s c l a s e s <code>R e l o j </code> y <code>M a n e c i l l a </code >. @ a u t h o r <a h r e f = m a i l t o : e l i s a v i s o @ g m a i l . com> E l i s a V i s o </a> @version 1.0 / public class UsoReloj { / Unicamente usamos un m todo main , p o r q u e s o l o l a vamos a u s a r e p a r a p r o b a r ( i n v o c a r ) l o s m todos de l a s c l a s e s R e l o j y e Manecilla . / 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 l o c a l e s a main : / 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 ; / E l r e l o j s e m o s t r a r en System . o u 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 ) ; / 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 ( ) ; r e l o j i t o . incrementa ( ) ; r e l o j i t o . muestra ( ) ; r e l o j i t o . setValor (10 ,59); r e l o j i t o . muestra ( ) ; 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 como atributo de la a e o clase. La razn es que un mtodo esttico de la clase no puede tener acceso a o e a atributos de esa clase. Por ello hay que declararlo en el mtodo. De cualquier e forma, como la clase Reloj es pblica, cualquiera puede pedir constructores de esa u clase. La ejecucin de la aplicacin anterior, junto con la l o o nea de comandos, se encuentra en la gura 3.21. Noten que como estamos trabajando con el paquete Reloj, las clases correspondientes van a ser buscadas en el subdirectorio Reloj, por lo que es necesario invocar desde el subdirectorio superior, en el que se encuentra

3.4 Expresiones en Java el paquete Reloj.

110

Figura 3.21

Ejecucin de la aplicacin UsoReloj o o


e l i s a @ l a m b d a : / ICC1 / p r o g r a m a s $ j a v a R e l o j / U s o R e l o j Son l a s 11 h o r a s con 59 m i n u t o s Son l a s 0 h o r a s con 0 m i n u t o s Son l a s 10 h o r a s con 59 m i n u t o s Son l a s 11 h o r a s con 0 m i n u t o s

La primera l nea es la invocacin de la aplicacin, llamando a la clase que o o tiene un mtodo main, UsoReloj. La segunda l e nea es la ejecucin de la l o nea 2500, despus de incrementar un reloj que inici a las 11 horas con 58 minutos. La e o segunda l nea resulta de la ejecucin de la l o nea 2700, que resulta de incrementar el reloj y hacer que ambas manecillas den la vuelta completa. La tercera l nea corresponde a ejecutar la l nea 2900 despus de ponerle una hora nueva al reloj e de las 10 horas con 59 minutos en la l nea 2800 del mtodo main. La ultima l e nea de la ejecucin corresponde a incrementar el reloj, lo que hace que la manecilla o de minutos complete una vuelta e incremente a la manecilla de las horas.

3.4.1.

Redireccionamiento de la salida de la aplicacin o


Como ya sabrn, cuando se le pide a la mquina virtual de Java que ejecute a a la aplicacin desde la consola, que en este caso es con o
e l i s a @ l a m b d a : / ICC1 / p r o g r a m a s }$ j a v a Reloj / UsoReloj

siempre se puede redirigir la salida que va al dispositivo estndar que es, precia samente, la consola asignada a System.out. Esta redireccin puede ser para crear o un archivo nuevo, usando el s mbolo y a continuacin el nombre de un archivo; o si el archivo ya existe, lo sustituye por el que se est creando, mientras que si el a archivo no existe todav simplemente crea uno nuevo. Tambin se puede redirigir a e para agregar al nal de un archivo que ya existe, usando dos juntos, seguidos por el nombre de un archivo; si el archivo ya existe, agrega al nal de lo que ya haya en el mismo; si no existe, simplemente lo crea nuevo y contiene unica mente lo que acabamos de escribir. Por ejemplo, si deseamos guardar la salida de la aplicacin en un archivo que se llame usoReloj . txt , invocar o amos la aplicacin o de la siguiente manera:
e l i s a @ l a m b d a : / ICC1 / p r o g r a m a s $ j a v a R e l o j / U s o R e l o j

usoReloj . txt

111

Clases y objetos

Eso crear el archivo usoReloj . txt y colocar ah todo lo escrito en System.out. Si a a el comando fuera
e l i s a @ l a m b d a : / ICC1 / p r o g r a m a s $ j a v a R e l o j / U s o R e l o j

usoReloj . txt

agregar al nal del archivo creado en alguna otra ejecucin o a travs del editor a o e favorito.

3.5 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 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 ) ;

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 LIMITE despus ya no podr e amos volverle a asignar un valor pues est declarada como constante, y todos los objetos tendr el mismo a an l mite, algo que no queremos que suceda. Es necesario enfatizar que las constantes (con el modicador nal) toman un valor una unica vez. En general este valor se les asigna cuando se declaran, pero como pudimos ver en el caso de la clase Manecilla, cada objeto debe trabajar con una constante distinta, no es la misma para todos los objetos de la clase. Java permite para estos casos asignar valor a una constante en el constructor; ninguna otra clase de mtodo aceptar la asignacin de un valor a una variable declarada e a o con el modicador nal, pues no puede vigilar que esto se haga slo una vez. En o cambio, con los constructores, una vez construido el objeto no se puede volver a construir ese mismo objeto. Todos los listados completos de este cap tulo los pueden encontrar en http://lambda.fciencias.unam.mx/icc1

3. Ejercicios

112

Ejercicios
3.1.- En la siguiente especicacin de un problema, identica los objetos y los o mtodos. e La profesora dej una tarea de aritmtica que consiste de 10 mulo e tiplicaciones largas. Los estudiantes tienen que resolver la tarea y la profesora deber calicarla. a 3.2.- En la siguiente especicacin de un problema, identica los objetos y los o mtodos. e Tenemos una cena que consiste de tres tiempos. El primero es una ensalada, el segundo tiempo es una sopa y el tercer tiempo es un plato de carne o pescado. El comensal puede elegir si es ensalada fresca o de pasta; si es sopa o crema de verduras; y si es carne o pescado. La cena deber prepararla el cocinero. a 3.3.- Elabora las tarjetas de responsabilidades para la maestra del problema 1 3.4.- Elabora las tarjetas de responsabilidades para el comensal del problema 2. 3.5.- Si pensamos en la cena como una clase, cules son los atributos de esta a clase? (dado que el segundo y el tercer tiempo hay que elegir entre dos cosas, podemos representarlas con variables booleanas s o no o, previniendo ms a opciones, con un entero). 3.6.- Escribe el encabezado en Java de un mtodo de acceso que nos diga si el e segundo tiempo es sopa o crema. 3.7.- Escribe el encabezado de un mtodo mutante que elija si la cena va a ser e con sopa o crema. 3.8.- Escribe en Java el mtodo de implementacin al que le damos como parmee o a tros el tipo del segundo tiempo y del tercer tiempo y los actualiza. 3.9.- Escribe en Java el constructor de la clase cena. 3.10.- Modica la clase Reloj para que tenga una manecilla para los segundos y para dcimas de segundo (prubalo en la computadora). e e

113

Clases y objetos

3.11.- Modica la clase Reloj para que sea un reloj de 24 horas y marque si es AM o PM (prubalo en la computadora). e 3.12.- Tenemos un registro en una agenda telefnica con el nombre, direccin o o telfono jo, posiblemente extensin, telfono celular y d de cumpleaos. e o e a n Escribe la clase en Java que represente a este registro, con sus mtodos de e acceso, mutantes, constructores y que muestre el registro completo. 3.13.- Tenemos el registro bibliogrco de un libro. Escribe una clase en Java que a lo represente. 3.14.- Tenemos el registro de un estudiante en la seccin escolar, que tiene su o nombre, nmero de cuenta, fecha de nacimiento y clave de la carrera. Escribe u la clase en Java que represente a un estudiante con estos datos. 3.15.- Tenemos un caon que puede ser utilizado con la computadora. Tiene los n botones de encendido, foco, distancia y enfriamiento y responde acorde a cada uno de estos botones. Escribe en Java una clase que represente a un can como se describi. no o

Manejo de cadenas y expresiones

Uno de los ingredientes que ms comnmente vamos a usar en nuestros prograa u mas son las expresiones. Por ello dedicaremos este cap tulo a ellas. Sin embargo, antes de entrar en materia respecto a lo que sucede dentro de la aplicacin o acerca de cmo combinar los elementos con los que trabajamos, deseamos poner o un poco de atencin a la manera que podemos tener de colocar valores en esos o elementos. Por lo pronto unicamente contamos con dos mecanismos: la asignacin o y la construccin de objetos (ejemplares) de una clase. Vamos a explorar cmo o o podemos asignar valores que el usuario proporciona. Por lo pronto lo haremos uni camente a travs de la consola, pues, como ya mencionamos, el manejo de entrada e y salida de Java no es para principiantes.

4.1 Lectura de datos primitivos


En el cap tulo anterior vimos cmo escribir en la pantalla y, usando redireccioo namiento en la invocacin de la aplicacin, cmo hacerlo en un archivo en disco. o o o Veremos ahora cmo leer datos primitivos y cadenas desde la pantalla. o

4.1 Lectura de datos primitivos

116

Java proporciona un dispositivo (objeto) para la lectura de caracteres, que se encuentra en la clase System y se llama in y que al igual que System.out, existe en cuanto se empieza a ejecutar cualquier aplicacin, por lo que no hay que declararlo o ni denirlo. Sin embargo, como la lectura es siempre peligrosa, pues descansa en las acciones de un usuario, todo lo que deseamos realizar en el dispositivo asociado a System.in la consola tiene que ser vigilado sintcticamente en la aplicacin a o mediante el manejo de excepciones, algo que veremos cmo hacer y qu signica o e hacia la ultima cuarta parte del material1 . Esto diculta el uso directo de System.in en nuestras aplicaciones. Por lo pronto nos preocuparemos por leer unicamente datos primitivos y ca denas. Para ello Java proporciona una clase, Scanner, que permite la lectura de datos primitivos y que, si se coloca sobre System.in, no requiere de vigilancia si hay un error en la ejecucin, el programa simplemente abortar. o a La clase Scanner se encuentra en el paquete java . util y se tiene que avisar en la aplicacin que se desea usar y que la ponga disponible. Esto se hace con el o comando import, cuya sintaxis se encuentra en la gura 4.1.

Figura 4.1

Importacin de clases o paquetes o


Sintaxis: x aviso de importaciny ::= import xlista de paquetes o clasesy ; o xlista de paquetes o clasesy::=xpaquete o clasey, xlista de paquetes o clasesy | xpaquete o clasey Semantica: Un xpaquete o clasey es un identicador que ubica a una clase particular en un directorio de clases de Java. Si se trata de un paquete completo, como el que pudiese ser el de entrada y salida, escribir amos import java. util .; indicando que queremos tener disponibles todas las clases del paquete []util
que se encuentra en el subdirectorio de clases de Java. El indica que queremos todas las clases de ese paquete. Si slo queremos importar una sola clase, como o en el caso de Scanner, ubicamos en qu paquete se encuentra y pedimos la e importacin unicamente de esa clase con import java. util .Scanner;. Podemos o pedir ms de una importacin, separando a los identicadores de los paquetes o a o clases con una coma y terminado la especicacin con un punto y coma. Pueden o aparecer tantos enunciados de import como queramos.

La mayor de los mtodos de la mayor de las clases que realizan entrada y salida lanzan a e a lo que se conoce como excepciones, que hay que vigilar y prever. Revisaremos este tema en el cap tulo correspondiente a errores en ejecucin. o

117

Manejo de cadenas y expresiones

Los comandos import deben aparecer antes de la declaracin de la clase, pero o despus de la especicacin del paquete al que pertenece la clase. e o Veremos unicamente algunos de los mtodos de la clase Scanner, pues muchos de e ellos requieren conocimientos ms extensos del lenguaje que todav no poseemos. a a Un revisor (scanner ) simple de texto es un dispositivo que puede revisar tipos primitivos y cadenas usando expresiones regulares. Un Scanner divide su entrada en atomos usando un patrn delimitador, que por o omisin corresponde a un blanco, tabulador o cambio de l o nea. El tomo resultante a se convierte a un valor que puede ser de distintos tipos usando las variaciones de los mtodos next. e Por ejemplo, el siguiente cdigo permite leer un nmero desde System.in: o u
S c a n n e r s c = new S c a n n e r ( System . i n ) ; int i = sc . nextInt ( ) ;

En este cdigo, montamos al scanner en el dispositivo System.in para leer de o la consola construimos un objeto tipo Scanner, inicializado sobre System.in. Si deseamos leer de un archivo en disco basta con redireccionar la entrada en la invocacin de la aplicacin, usando el s o o mbolo seguido del nombre del archivo, a continuacin de la invocacin de la aplicacin con la mquina virtual de Java: o o o a
e l i s a @ v i s o : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $ j a v a UsoCadenas aCadenas . t x t

par

En la invocacin anterior estamos solicitando a la mquina virtual de Java que lea o a sus datos (dirigidos al dispositivo estndar de la computadora, el teclado) desde a el archivo en disco llamado paraCadenas.txt y que se encuentra en el mismo subdirectorio desde el que se est invocando a la aplicacin. a o 2 Una operacin del scanner puede bloquearse esperando que se teclee algo. o Los mtodos next() , hasNext() y sus variantes para leer tipos primitivos, (tales e como nextInt () y hasNextInt() ) primero saltan cualquier entrada que cace con el delimitador y despus intentan devolver el siguiente atomo, que termina cuando e se encuentran nuevamente al delimitador (o sucesiones de ste). Ambos mtodos e e hasNext y next pueden bloquearse esperando entrada. El que un hasNext se bloquee no tiene relacin con que su next asociado se vaya a bloquear. o Un scanner puede leer texto en cualquier objeto que implemente la interfaz Readable y se traga los errores, suponiendo que se alcanz el n del archivo. Tamo bin puede interpretar cadenas ( String ). e Al cerrar a un Scanner, cerrar su fuente de entrada si es que la fuente de a entrada tiene implementada la interfaz Closeable .
Cuando se bloquea un dispositivo la aplicacin se congela y no sucede nada hasta que el o dispositivo se desbloquea o la aplicacin se aborta. o
2

4.1 Lectura de datos primitivos

118

A menos que se especique, si se pasa un parmetro nulo a cualquier mtodo a e de Scanner, habr un error de NullPointerException y el programa terminar. a a Un scanner, por omisin, espera la representacin de nmeros en decimal, a o o u menos que se establezca otra base con el mtodo Radix(int). El mtodo reset () rese e tablecer el valor del scanner a base 10, aunque haya sido cambiada previamente. a Para ver la sintaxis particular de cada dato primitivo, sta se encuentra en la e descripcin de la clase en la pgina correspondiente de Java, denotada con BNF. o a Al leer datos primitivos los espacios no son relevantes: uno o ms espacios (o a tabuladores o nes de l nea) funcionan para separar a los datos primitivos. En el caso de las cadenas, sin embargo, que no son datos primitivos, todo lo que se encuentra a partir de que se le pide que lea una cadena y hasta que encuentre el primer n de l nea forma parte de la cadena. Al leer un dato primitivo, ste termina con el primer separador pero no lo e consume. Esto quiere decir que el siguiente dato empezar a buscarse a partir a del ultimo separador encontrado inclusive. Por lo tanto, si leemos un nmero, que u se encuentra en una sola l nea y termina con un n de l nea, y a continuacin o leemos una cadena, sta no contendr ningn carcter, pues se encontrar inmee a u a a diatamente con el n de l nea. La clase Scanner se encuentra disponible a partir de la versin 1.5 de Java. o En la tabla 4.1 se encuentran los mtodos que vamos a usar. e

Tabla 4.1

Mtodos y atributos de Scanner e

(1/4)

Class java.util.Scanner
Dispositivo para lectura simple de datos primitivos y cadenas. Constructores:
Scanner(InputStream source) Construye un nuevo Scanner que produce valores revisados

en el ujo de entrada especicado.


Scanner(String source)

Construye un nuevo Scanner que produce valores revisados de la cadena especicada.

Mtodos: e
void close ()

Cierra este scanner.

Pattern delimiter ()

Regresa el patrn que est usando el scanner para empao a tar delimitadores.
(Contina en la pgina siguiente) u a

119

Manejo de cadenas y expresiones

Tabla 4.1

Mtodos y atributos de Scanner e


boolean hasNext()

(Viene de la pgina anterior) a

(2/4)

Regresa verdadero si este scanner tiene otro atomo en su entrada.

boolean hasNextBoolean()

Regresa verdadero si el siguiente atomo en la entrada de este scanner puede ser interpretado como un valor booleano, usando un patrn independiente de maysculas o o u minsculas creado de alguna de las cadenas true | false. u Regresa verdadero si el siguiente tomo en este scanner a puede ser interpretado como un valor de byte en la base por omisin (base 10 entre -128 y 127). o Regresa verdadero si el siguiente tomo en este scanner a puede ser interpretado como un valor de byte en la base indicada en radix. Regresa verdadero si el siguiente tomo en este scanner a puede ser interpretado como un valor double. Regresa verdadero si el siguiente tomo en este scanner a puede ser interpretado como un valor oat.

boolean hasNextByte()

boolean hasNextByte(int radix )

boolean hasNextDouble()

boolean hasNextFloat()

boolean hasNextInt()

Regresa verdadero si el siguiente tomo en este scanner a puede ser interpretado como un valor int en la base por omisin. o Regresa verdadero si el siguiente tomo en este scanner a puede ser interpretado como un valor int en la base especicada en radix. Regresa verdadero si hay una l nea ms por revisar en a este scanner.

boolean hasNextInt(int radix )

boolean hasNextLine()

boolean hasNextLong()

Regresa verdadero si el siguiente tomo en este scanner a puede ser interpretado como un valor long.
(Contina en la pgina siguiente) u a

4.1 Lectura de datos primitivos

120

Tabla 4.1

Mtodos y atributos de Scanner e


boolean hasNextLong(int radix)

(Contina de la pgina anterior) u a

(3/4)

Regresa verdadero si el siguiente atomo en este scanner puede ser interpretado como un valor long en la base especicada en radix. Regresa verdadero si el siguiente atomo en este scanner puede ser interpretado como un valor short. Regresa verdadero si el siguiente atomo en este scanner puede ser interpretado como un valor short en la base especicada en radix. Regresa el resultado del apareamiento hecho por la ultima operacin ejecutada por este scanner. o

boolean hasNextShort()

boolean hasNextShort(int radix )

MatchResult match()

String next()

Encuentra y regresa el siguiente atomo completo de este scanner.

boolean nextBoolean()

Revisa y regresa el siguiente atomo interpretando como un boolean. Revisa y regresa el siguiente atomo interpretando como un byte. Revisa y regresa el siguiente atomo interpretando como un byte en la base indicada por radix. Revisa y regresa el siguiente atomo interpretando como un double. Revisa y regresa el siguiente atomo interpretando como un oat. Revisa y regresa el siguiente atomo interpretando como un int. Revisa y regresa el siguiente atomo interpretando como un int en la base indicada por radix.
(Contina en la pgina siguiente) u a

byte nextByte()

byte nextByte(int radix )

double nextDouble()

oat nextFloat ()

int nextInt ()

int nextInt (int radix )

121

Manejo de cadenas y expresiones

Tabla 4.1

Mtodos y atributos de Scanner e


String nextLine()

(4/4) (Viene de la pgina anterior) a

Avanza el scanner a la siguiente l nea y regresa lo que se salt. o

long nextLong()

Revisa y regresa el siguiente atomo interpretando como un long. Revisa y regresa el siguiente atomo interpretando como un long en la base indicada como radix. Revisa y regresa el siguiente atomo interpretando como un short. Revisa y regresa el siguiente atomo interpretando como un short en la base indicada como radix. Regresa el valor con el que el scanner est trabajando a como base. Reinicia este scanner. Regresa la representacin en cadena de este scanner. o Establece la base numrica para la base especicada. e

long nextLong(int radix )

short nextShort()

short nextShort(int radix )

int radix ()

Scanner reset ()

String toString ()

Scanner useRadix(int radix )

Supongamos que queremos leer tres enteros de la consola en el mtodo main. e El cdigo se ver como se muestra en el listado 4.1, asociando a un objeto de la o a clase Scanner con el teclado.

Cdigo 4.1 Ejemplo del uso de un objeto Scanner o

Pruebas (1/2)

package L e c t u r a ; import j a v a . u t i l . S c a n n e r ; public c l a s s Pruebas { 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 ) { S c a n n e r c o n s = new S c a n n e r ( System . i n ) ; System . o u t . p r i n t l n ( "Dame un entero , seguido de un real " + " y despu s una cadena " ) ; e

4.1 Lectura de datos primitivos

122
Pruebas (2/2)

Cdigo 4.1 Ejemplo del uso de un objeto Scanner o


i n t i = cons . n e x t I n t ( ) ; double x = c o n s . n e x t D o u b l e ( ) ; S t r i n g l i n e a = cons . nextLine ( ) ; System . o u t . p r i n t l n ( "El entero : " + i + "\tEl real: " + x + "\nLa cadena : \"" + l i n e a + "\"" ) ; } }

Compilamos este programa y lo ejecutamos redireccionando tanto la entrada como la salida. Supongamos que ejecutamos el programa con la siguiente l nea de comando:

Datos0 . t x t

e l i s a @ v i s o : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $ j a v a L e c t u r a . P r u e b a s Salida0 . txt

El archivo de entrada tiene lo siguiente:

3245 3870.23Esta es una cadena

Noten que no hay nada entre el ultimo d gito del nmero real que espera y la u cadena. En este caso, como no encuentra un blanco o n de l nea que avise que el nmero ya termin, no acaba de leer el real y aborta con el siguiente mensaje en u o la pantalla: Exception in thread "main" java.util.InputMismatchException at java.util.Scanner.throwFor(Scanner.java:840) at java.util.Scanner.next(Scanner.java:1461) at java.util.Scanner.nextDouble(Scanner.java:2387) at Lectura.Pruebas.main(Pruebas.java:8) En el archivo de salida Salida0 . txt alcanza a escribirse lo siguiente: Dame un entero, seguido de un real y despus una cadena e Si colocamos al menos un blanco entre el nmero y la cadena, todo sale bien. u Invocamos de la siguiente manera:
e l i s a @ v i s o : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $ j a v a L e c t u r a . P r u e b a s <DatosA . t x t > S a l i d a A . t x t

lo que aparece en el archivo de salida es lo siguiente:

123

Manejo de cadenas y expresiones

Dame un entero, seguido de un real y despus una cadena e El entero: 3245 El real: 3870.23 La cadena: " Esta es una cadena"

Algunas llamadas de atencin o


Deseamos insistir que todos los nmeros que van a aparecer en una entrada u deben ir terminados por un blanco, un tabulador o un n de l nea, para que la mquina virtual sepa que ya termin ese elemento. Sin embargo, no lo consume, a o sino que lo deja como primer elemento de lo siguiente que se quiera leer. Por lo tanto si a un nmero sigue una cadena, debe haber al menos un blanco entre el u nal del nmero y la cadena, aunque ese blanco sea tomado como parte de la u cadena. Si se coloca a un nmero solo en una l u nea y se quiere leer a continuacin de o l una cadena, se tiene que consumir el n de l e nea antes de leer la cadena. Esto se logra buscando una cadena vac antes de tratar de leer la cadena. Si no se a hace as como el carcter de n de l , a nea no ser consumido con el nmero, al dar a u la lectura de la cadena sta quedar vac pues proceder a consumir el carcter e a a a a de n de l nea que qued atorado. Para leer una cadena vac simplemente se o a invoca cad = cons.nextLine() dos veces consecutivas, pero la primera vez no se usa el resultado. Conforme vayamos revisando la aplicacin, veremos un poco ms sobre entrada o a y salida.

4.2 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, un valor booleano o de cualquier otra clase accesible al mtodo en el que se encuentra la expresin. e 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, como objeto de bsquedas, de agrupamiento, etctera. u e 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

4.2 Manejo de cadenas en Java

124

mediante el tipo String, reservamos espacio unicamente para la referencia, ya que se trata de objetos. Sin embargo, por lo comn que son las cadenas, la sintaxis de u Java es mucho ms exible para la creacin de cadenas que de objetos en general a o 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:
1 2 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, por lo que se crea en el heap y se deja en minusc la referencia a esta u cadena (lo denotamos en los esquemas como una echa ya que el contenido de la variable apunta a lo localidad en el heap donde se crea el objeto; el contenido de la variable es la direccin en el heap). En el lado derecho de la asignacin en o o el segundo rengln se construye una cadena nueva que es la cadena minusc pero o pasada a maysculas, crendose una nueva cadena en el heap; lo ultimo que se u a hace es reasignar la referencia de minusc a que ahora apunte a esta nueva cadena. Veamos un esquema de qu es lo que pasa en la gura 4.2 en la siguiente pgina. e a

125

Manejo de cadenas y expresiones

Figura 4.2

Inmutabilidad de cadenas

Memoria
1: minusc

Heap
est en minsculas a u

2: minusc

est en minsculas a u ESTA EN MINUSCULAS

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. e No mostramos todos, pues algunos de ellos tienen parmetros o entregan valores a de tipos que no hemos visto todav A continuacin se encuentra la tabla 4.2, con a. o los mtodos que podemos querer usar de la clase String. e

Tabla 4.2

Mtodos y atributos de la clase String e

(1/4)

Class String
Constructores

Representa a las cadenas de caracteres.


String () String ( String )

Construye una nueva cadena, nula en el primer caso y una 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 argumento. e
(Contina en la siguiente pgina) u a

4.2 Manejo de cadenas en Java

126
(2/4)
(Contina de la pgina anterior) u a

Tabla 4.2

Mtodos y atributos de la clase String e


Mtodos para crear nuevas cadenas: e
String replace (char, char)

Crea una nueva cadena en la que reemplaza las presencias del primer carcter por el segundo. a
String replace ( String , String )

Crea una nueva cadena en la que reemplaza las presencias de la primera cadena por la segunda.
String substring (int) String substring (int , int)

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, pero no lo incluye.
String toLowerCase()

Crea una nueva cadena convirtiendo todos los caracteres a minsculas. u


String toUpperCase()

Crea una nueva cadena convirtiendo todos los caracteres a maysculas. u


String trim()

Crea una nueva cadena quitando los blancos del principio y nal
static String valueOf(boolean) static String valueOf(char) static String valueOf(int)

Crea una cadena con el valor que corresponde al tipo del dato. Como es esttica se puede llamar desde la clase: a String .valueOf( valor ) .
static String valueOf(long) static String valueOf( oat ) static String valueOf(double)

Crea una cadena con el valor que corresponde al tipo del dato. Como es esttica se puede llamar desde la clase: a String .valueOf( valor ) .
(Contina en la siguiente pgina) u a

127

Manejo de cadenas y expresiones

Tabla 4.2

Mtodos y atributos de la clase String e


Mtodos de comparacin: e o
int compareTo(String)

(3/4)
(Contina de la pgina anterior) u a

Comparan dos cadenas en el orden del cdigo Unicode. o 6


90 9 9 9 9 8 9 9 9 9 9 7

si las cadenas son idnticas e si xcad1y va despus en el orden e que xcad2y. si xcad1y va antes en el orden que xcad2y.

Regresa

0 0

boolean equals(Object)

Dice si la cadena en el parmetro es idntica a la que a e invoca.


boolean equalsIgnoreCase( String )

Dice si la cadena en el argumento es igual a la que invoca, ignorando diferencias entre maysculas y minsculas. u u Mtodos de b squeda: e u
boolean endsWith(String)

Dice si la cadena con la que se invoca termina con la cadena en el parmetro. a


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

El primer entero corresponde al cdigo de un carcter en o a Unicode (se puede pasar como argumentos tambin un e carcter). La cadena se reere a una subcadena. En las a cuatro versiones, regresa la primera posicin en la cadena o que invoca donde se encuentra el primer parmetro. Si a se da un segundo parmetro, ste indica que se busque a a e partir de esa posicin. Regresa -1 si no encuentra lo que o est buscando. a
boolean startsWith ( String ) boolean startsWith ( String , 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 argumento entero. a
(Contina en la siguiente pgina) u a

4.2 Manejo de cadenas en Java

128
(4/4)
(Contina de la pgina anterior) u a

Tabla 4.2

Mtodos y atributos de la clase String e


Mtodos de b squeda: e u
int int int int lastIndexOf (char) lastIndexOf (char, int) lastIndexOf ( String ) lastIndexOf ( String , int)

El carcter corresponde a un carcter (se puede pasar coa a mo argumentos tambin un cdigo entero de un carcter e o a en Unicode). La cadena se reere a una subcadena. En las cuatro versiones regresa la ultima posicin en la cadena o que invoca donde se encuentra el primer parmetro. Si a se da un segundo parmetro, ste indica que se busque a a e partir de esa posicin. Regresa -1 si no encuentra lo que o est buscando. a
boolean regionMatches(int, String , int , int) boolean regionMatches(boolean, int, String , int , int)

Determina si una regin de la cadena que invoca es igual o a una regin de la cadena en el argumento. La segunda o versin, si el argumento booleano es verdadero, compara o ignorando diferencias entre maysculas y minsculas. El u u primer entero es la posicin de la regin en la cadena o o que invoca; el segundo entero es la posicin inicial en la o cadena del argumento. La tercera posicin es el nmero o u de caracteres a comparar. Mtodos de conversin e o
char charAt(int)

Regresa el carcter que se encuentra, en la cadena que a invoca, en la posicin dada por el argumento. o
String toString ()

Genera la representacin en cadena del objeto con el que o se le invoca. Otros Mtodos e
int length ()

Regresa el tamao de la cadena, el nmero de caracteres. n u Supongamos que queremos comparar dos cadenas para ver si son la misma. Queremos quitar los blancos del principio y nal y descontar si una tiene algunas maysculas y la otra no. Para ello aplicamos primero la funcin que poda (trim) u o

129

Manejo de cadenas y expresiones

y despus comparamos convirtiendo ambas a minsculas. En el listado 4.2 en la e u siguiente pgina se encuentra un ejemplo del uso de mtodos de cadenas, junto a e con la ejecucin del mismo. La l o nea con la que se invoc es la siguiente: o
e l i s a @ v i s o : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $ j a v a UsoCadenas p a r a Cadenas . t x t UsoCadenas . t x t

En este comando estamos pidiendo que redirija la entrada (de System.in) al archivo de texto que se llama paraCadenas.txt; respecto a la salida, estamos pidiendo que lo pegue al nal del archivo UsoCadenas.java, que es el de nuestra aplicacin. o Los archivos de entrada (gura 4.3), de cdigo (listado 4.2) y de salida (gura 4.4) o se encuentran en sta y las pginas que siguen. e a

Figura 4.3

Archivo de entrada para UsoCadenas.java


Cadenas Para c o m p a r a r c a d e n a s p a r a Comparar

Cdigo 4.2 Uso de cadenas y redireccionamiento de entrada y salida o

UsoCadenas (1/2)

1 import j a v a . u t i l . S c a n n e r ; 2 / 3 C l a s e <code>UsoCadenas </code> p a r a e j e m p l i f i c a r e l u s o de a r c h i v o 4 de e n t r a d a y c a d e n a s . 5 @ a u t h o r <a h r e f = m a i l t o : e l i s a @ v i s o > E l i s a V i s o G u r o v i c h </a> 6 @version 1.2 7 / 8 p u b l i c c l a s s UsoCadenas { 9 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 ) { 10 S c a n n e r s I n = new S c a n n e r ( System . i n ) ; 11 S t r i n g s1 = s I n . n e x t L i n e ( ) ; 12 S t r i n g s2 = s I n . n e x t L i n e ( ) ; 13 System . o u t . p r i n t l n ( "/*\n Las cadenas que se leyeron " 14 + "son las siguientes :\n" 15 + " \"" + s 1 + "\"\n" 16 + " \"" + s 2 +"\"\n*/" ) ; 17 boolean i g u a l e s = s 1 . t r i m ( ) . t o L o w e r C a s e ( ) 18 . e q u a l s ( s2 . trim ( ) . toLowerCase ( ) ) ; 19 System . o u t . p r i n t l n ( "\n/* Despu s de las operaciones , " e 20 + " s1 es: \n \"" 21 + s 1 +"\"\n s2 es :\n \"" 22 + s 2 + "\"\n" ) ;

4.2 Manejo de cadenas en Java

130
UsoCadenas (2/2)

Cdigo 4.2 Uso de cadenas y redireccionamiento de entrada y salida o


23 System . o u t . p r i n t l n ( "\n/* Salida para el programa :" 24 +"\n Las cadenas :\n*" 25 + s 1 + "\"\n \"" + s 2 + "\"\n" 26 + ( i g u a l e s ? " " : " no" ) 27 + " contienen el mismo texto " 28 + "\n*/" ) ; 29 } // main 30 } // UsoCadenas

Figura 4.4

Archivo de salida para UsoCadenas.java


/ Las c a d e n a s que s e l e y e r o n s o n l a s s i g u i e n t e s : Cadenas Para c o m p a r a r c a d e n a s p a r a Comparar / / Despu s de l a s o p e r a c i o n e s , e s1 es : Cadenas Para c o m p a r a r s2 es : c a d e n a s p a r a Comparar

/ S a l i d a p a r a e l programa : Las c a d e n a s : Cadenas Para c o m p a r a r c a d e n a s p a r a Comparar c o n t i e n e n e l mismo t e x t o /

Es frecuente tambin que queramos obtener algn valor primitivo a partir de e u una cadena o que queramos conocer propiedades de los tipos primitivos con que contamos. Por ejemplo, si tenemos la cadena "127.5", queremos el valor representado por esta cadena, no la sucesin de d o gitos que la forman. Para ello podemos 3 recurrir a las clases que envuelven a los tipos primitivos y que tienen mtodos e estticos (de la clase), entre muchos otros, que sirven para obtener valores pria mitivos que corresponden a una cadena y conocer la representacin o el valor de o alguna variable de tipo primitivo. Por el momento no mencionaremos todos los mtodos de todas las clases envolventes, sino unicamente los que tienen que ver e con conversin de cadenas a tipos primitivos y aquellos atributos que se reeren a o stos. Como se trata de mtodos estticos, no se tienen que construir objetos de e e a la clase para poder usarlos. Se muestran en la tabla 4.3 en la pgina opuesta. a
3

Del ingls wrappers. e

131

Manejo de cadenas y expresiones

Tabla 4.3

Mtodos y atributos de clases envolventes de tipos primitivos e

(1/9)

Class Boolean
Es una envoltura para el tipo primitivo boolean. Mtodos: e
static boolean getBoolean(String name)

Convierte a la cadena true en el valor booleano verdadero y a cualquier otra cadena en falso.
static boolean parseBoolean(String s)

Revisa la cadena como si fuera un valor booleano.


static String toString (boolean b)

Regresa una cadena que representa al valor booleano especicado.

Class Byte
Es una envoltura para el tipo primitivo byte. Contiene tambin algunos campos (atributos) interesantes que vamos a lise tar. Atributos:
static byte MAX VALUE

Una constante que almacena el mximo valor que puede a tomar un byte, 27 1.
static byte MIN VALUE

Una constante que almacena el valor m nimo que puede 7 tomar un byte, 2 .
static int SIZE

El nmero de bits que se usan para representar un valor u tipo byte en notacin de complemento a 2. o Mtodos: e
static byte parseByte( String s)

Revisa a la cadena en el parmetro para obtener un valor a decimal con signo para un byte.
static byte parseByte( String s , int radix )

Revisa a la cadena en el parmetro para obtener un valor a decimal con signo para un byte, en la base indicada por radix .
(Contina en la siguiente pgina) u a

4.2 Manejo de cadenas en Java

132
(2/9)

Tabla 4.3

Mtodos y atributos de clases envolventes de tipos primitivos e

Contina de la pgina anterior u a

Class Character
Atributos:

Envuelve al tipo primitivo char.

static char MAX VALUE

El valor constante de este campo es el mximo valor que a se puede almacenar en una variable de tipo char, que es el representado como "zuFFFF". El valor constante de este campo es el mximo valor que a se puede almacenar en una variable de tipo char, que es el representado como "zu0000".

static char MIN VALUE

static int MAX RADIX

La mxima base disponible para convertir n umeros de y a hacia cadenas.

static int MIN RADIX static int SIZE

La m nima base disponible para convertir nmeros de y u hacia cadenas. El nmero de bits que se usan para representar a un de u tipo char en formato binario sin signo.

Mtodos: e
static int digit (char ch, int radix )

static char forDigit (int digit , int radix ) static int getNumericValue(char ch)

Regresa el valor numrico del carcter ch en la base espee a cicada por radix .

Da la representacin como carcter de un d o a gito espec co ( digit ) en la base ( radix ) especicada. Regresa el valor como entero que el carcter char tiene a como cdigo binario. o Determina si el carcter especicado es un d a gito.

static boolean isDigit (char ch)

static boolean isLetter (char ch)

Determina si el carcter especicado es una letra a ( [azAZ]). Determina si el carcter especicado es una letra a ( [azAZ]) o un d gito ([09]).
Contina en la siguiente pgina u a

static boolean isLetterOrDigit (char ch)

133

Manejo de cadenas y expresiones

Tabla 4.3

Mtodos y atributos de clases envolventes de tipos primitivos e


Mtodos de la clase Character e
static boolean isLowerCase(char ch)

(3/9)

Contina de la pgina anterior u a (contina) u

Determina si el carcter especicado es una letra a minscula ( [az]). u


static boolean isUpperCase(char ch)

Determina si el carcter especicado es una letra a mayscula ( [az]). u


static boolean isSpaceChar(char ch)

Determina si el carcter especicado es una espacio en a Unicode.


static char toLowerCase(char ch)

Convierte al carcter en el argumento a minscula. a u


static char toUpperCase(char ch)

Convierte al carcter en el argumento a mayscula. a u


static String toString (char ch)

Regresa una cadena que representa al carcter especicaa do.

Class Double
Es una envoltura para nmeros reales de doble precisin. u o Atributos:
static int MAX EXPONENT

Mximo exponente que puede tener una variable de tipo a double.


static double MAX VALUE

static int MIN EXPONENT

Una constante que guarda el valor nito positivo ms a grande que puede guardar una variable de tipo double, (2 252 q 21023 ). M nimo exponente que puede tener una variable de tipo double normalizada.

static double MIN VALUE

Una constante que guarda el valor nito positivo ms a pequeo posible que puede guardar una variable de tipo n double, (21074 ).
Contina en la siguiente pgina u a

4.2 Manejo de cadenas en Java

134
(4/9)

Tabla 4.3

Mtodos y atributos de clases envolventes de tipos primitivos e


Atributos de la clase Double
static double NEGATIVE INFINITY
(contina) u

Contina de la pgina anterior u a

Una constante que guarda el valor innito negativo de tipo double.


static double POSITIVE INFINITY

Una constante que guarda el valor innito positivo de tipo double.


static int SIZE

El nmero de bits que se usan para representar a un u double. Mtodos: e


static int compare(double d1, double d2) 6 9 0 si d1 d2 9 8 Regresa: 1 si d1 d2 9 9 7 1 si d1 d2

static double parseDouble(String s)

Regresa un de tipo double inicializado al valor representado en la cadena, convertido con el mtodo valueOf de e esta misma clase.
static String toHexString(double d)

Regresa una cadena hexadecimal con la representacin o (en bits) del nmero dado. u
static String toString (double d)

Regresa una representacin en cadena del real en el arguo mento.

Class Float
Atributos:

Es una envoltura para los nmeros reales de precisin sencilla u o ( oat ).

static int MAX EXPONENT static int MIN EXPONENT

Mximo exponente que puede tener una variable de tipo a oat . M nimo exponente que puede tener una variable de tipo oat .
Contina en la siguiente pgina u a

135

Manejo de cadenas y expresiones

Tabla 4.3

Mtodos y atributos de clases envolventes de tipos primitivos e


Atributos de la clase Float
static oat MAX VALUE
(contina) u

(5/9)

Contina de la pgina anterior u a

Una constante que guarda el mximo valor posible para a un oat , 2 223 2127 .
static oat MIN VALUE

Una constante que guarda el m nimo valor posible para 149 . un oat , 2
static oat NEGATIVE INFINITY

Una constante que guarda el innito negativo para el tipo oat .


static oat POSITIVE INFINITY

Una constante que guarda el innito positivo para el tipo oat .


static int SIZE

El nmero de bits que se usan para representar a un oat . u Mtodos: e


static int compare(oat 6 , oat f2) f1 9 0 si f1 9 8

9 9 7 1 static oat parseFloat ( String s)

Regresa:

 si f1 si f1

f2 f2 f2

Regresa un de tipo oat inicializado al valor representado en la cadena, convertido con el mtodo valueOf de esta e misma clase.
static String toHexString( oat f )

Regresa una cadena hexadecimal con la representacin o (en bits) del nmero dado. u
static String toString ( oat f )

Regresa una representacin en cadena del real de tipo o oat en el argumento.

Class Integer
Atributos:

Es una envoltura para los valores de tipo int .

static int MAX VALUE

Una constante que guarda el mximo valor posible para a 31 un int , 2 1.


Contina en la siguiente pgina u a

4.2 Manejo de cadenas en Java

136
(6/9)

Tabla 4.3

Mtodos y atributos de clases envolventes de tipos primitivos e


Atributos de la clase Integer
static int MIN VALUE

Contina de la pgina anterior u a (contina) u

Una constante que guarda el m nimo valor posible para un int , 231 .
static int SIZE

El nmero de bits que se usan para representar a un int . u Mtodos: e


static int bitCount(int i )

Regresa el nmero de bits prendidos en la representacin u o binaria en complemento a dos del valor int especicado.
static int parseInt ( String s)

Revisa a la cadena para convertirla a un entero con signo


static int parseInt ( String s , int radix )

Revisa a la cadena para convertirla a un entero con signo en la base denida por radix .
static int signum(int i )

6 9 1 9 8

si i

Regresa el signo del entero: signum(i)


static String toBinaryString (int i )

9 9 7 1

0 si i  0 si i 0

Regresa la cadena binaria sin signo en base 2 correspondiente a este entero.


static String toOctalString (int i )

Regresa la cadena octal sin signo en base 2 correspondiente a este entero.


static String toHexString(int i )

Regresa la cadena hexadecimal sin signo en base 2 correspondiente a este entero.


static String toString (int i )

Regresa la cadena que representa al entero.


static String toString (int i , int radix )

Regresa la cadena que representa al entero en base radix .


Contina en la siguiente pgina u a

137

Manejo de cadenas y expresiones

Tabla 4.3

Mtodos y atributos de clases envolventes de tipos primitivos e

(8/9)

Contina de la pgina anterior u a

Class Long
Atributos:

Es una envoltura para los valores del tipo long.

static long MAX VALUE

static long MIN VALUE

Una constante que guarda el mximo valor posible para a 63 un long, 2 1. Una constante que guarda el m nimo valor posible para 63 un long, 2 . El nmero de bits que se usan para representar a un long. u

static int SIZE

Mtodos: e
static long parseLong(String s)

Revisa a la cadena para convertirla a un entero con signo


static long parseLong(String s , int radix )

Revisa a la cadena para convertirla a un long con signo en la base denida por radix .
static int signum(long i)

6 9 1 9 8

si i

Regresa el signo del entero: signum(i)


static String toBinaryString (long i )

9 9 7 1

0 si i  0 si i 0

Regresa la cadena binaria sin signo en base 2 correspondiente a este long.


static String toOctalString (long i )

Regresa la cadena octal sin signo en base 2 correspondiente a este long.


static String toHexString(long i )

Regresa la cadena hexadecimal sin signo en base 2 correspondiente a este long.


static String toString (long i )

Regresa la cadena que representa al entero.


static String toString (long i , int radix )

Regresa la cadena que representa al entero en base radix .


Contina en la siguiente pgina u a

4.3 Implementacin de una base de datos o

138
(9/9)

Tabla 4.3

Mtodos y atributos de clases envolventes de tipos primitivos e

Contina de la pgina anterior u a

Class Short
Es una envoltura para los valores de tipo short. Atributos:
static short MAX VALUE

Una constante que guarda el mximo valor posible para a 63 un short, 2 1.


static short MIN VALUE

Una constante que guarda el m nimo valor posible para 63 un short, 2 .


static int SIZE

El nmero de bits que se usan para representar a un short. u Mtodos: e


static int bitCount(short i )

Regresa el nmero de bits prendidos en la representacin u o binaria en complemento a dos del valor short especicado.
static short parseShort( String s)

Revisa a la cadena para convertirla a un short con signo


static short parseShort( String s , int radix )

Revisa a la cadena para convertirla a un short con signo en la base denida por radix .
static String toString (short i )

Regresa la cadena que representa al short.

4.3 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:

139

Manejo de cadenas y expresiones

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 correo electrnico. a o 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, correo, 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.5 en la siguiente pgina. a

Figura 4.5

Tarjeta de responsabilidades para Curso.

Clase: Curso
P u b l i c o
Constructores getNombre getCarrera daCarrera getCorreo getCuenta agregaAlumno eliminaAlumno 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 clave de la carrera de un estudiante Regresa el nombre de la carrera de un estudiante Regresa el correo 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

4.3 Implementacin de una base de datos o

140

En la tarjeta de responsabilidades usamos el prejo get para aquellos atributos que el sistema simplemente va a regresar, tal cual est almacenado, y el preja e da cuando se trata de un mtodo de manipulacin que transforma o interpreta el e o campo.

De esto, podemos denir ya una interfaz de Java que se encargue de denir estos servicios. La podemos ver en el Listado 4.3 en la pgina opuesta. a

141

Manejo de cadenas y expresiones


ServiciosCurso (1/3)

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

1 package C o n s u l t a s ; 2 / 3 I n t e r f a c e S e r v i c i o s C u r s o d e s c r i b e l o s s e r v i c i o s de un s i s t e m a de 4 b a s e s de d a t o s p a r a l o s alumnos i n s c r i t o s en un g r u p o . 5 Cre ad a : L un es Mar 8 0 9 : 1 1 : 5 5 2 0 1 0 . 6 @ a u t h o r <a h r e f = m a i l t o : e l i s a @ l a m b d a > E l i s a V i s o </a> 7 @version 1.0 8 / 9 public interface ServiciosCurso { 10 11 / 12 Mtodo <code>getGrupo </code >: R e g r e s a e l n mero d e l g r u p o . e u 13 @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> 14 / 15 S t r i n g getGrupo ( ) ; 16 17 / 18 Mtodo <code>getNombre </code >: r e g r e s a e l nombre d e l e 19 e s t u d i a n t e en l a p o s i c i n <code>c u a l </code >. o 20 @param c u a l v a l o r de t i p o <code>i n t </code >. 21 @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> 22 / 23 S t r i n g getNombre ( i n t c u a l ) ; 24 25 / 26 Mtodo <code>g e t C a r r e r a </code >: r e g r e s a l a c a r r e r a d e l e 27 e s t u d i a n t e en l a p o s i c i n <code>c u a l </code >. o 28 @param c u a l de t i p o <code>i n t </code >: P o s i c i n d e l r e g i s t r o . o 29 @ r e t u r n v a l o r t i p o <code>S t r i n g </code >: C l a v e de l a c a r r e r a . 30 / 31 String getCarrera ( int cual ); 32 / 33 34 Mtodo <code>d a C a r r e r a </code >: r e g r e s a e l nombre de l a c a r r e r a e 35 d e l e s t u d i a n t e en l a p o s i c i n <code>c u a l </code >. o 36 @param c u a l de t i p o <code>i n t </code >: P o s i c i n d e l r e g i s t r o . o 37 @ r e t u r n v a l o r t i p o <code>S t r i n g </code >: Nombre de l a c a r r e r a . 38 / 39 String daCarrera ( int cual ) ;

4.3 Implementacin de una base de datos o

142
ServiciosCurso (2/3)

Cdigo 4.3 Interfaz para el manejo de una base de datos 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 86 87 88

/ Mtodo <code>g e t C o r r e o </code >: r e g r e s a e l c o r r e o d e l e e s t u d i a n t e en l a p o s i c i n <code>c u a l </code >. o @param c u a l v a l o r de t i p o <code>i n t </code >. @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> / String getCorreo ( int cual ) ; / Mtodo <code>g e t C u e n t a </code >: r e g r e s a e l n mero de c u e n t a d e l e u e s t u d i a n t e en l a p o s i c i n <code>c u a l </code >. o @param c u a l v a l o r de t i p o an <code>i n t </code >. @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> / S t r i n g getCuenta ( int cual ) ; / Mtodo <code>l o c a l i z a A l u m n o </code >: r e g r e s a l a p o s i c i n d e l e o e s t u d i a n t e que c a c e con l a c a d e n a s o l i c i t a d a . . @param c a d e n a v a l o r de t i p o a <code>S t r i n g </code >. @ r e t u r n v a l o r t i p o an <code>i n t </code> / i n t l o c a l i z a A l u m n o ( S t r i n g cadena ) ; / Mtodo <code>agregaAlumno </code >: a g r e g a a un alumno a e l a b a s e de d a t o s , h a c i e n d o l a s c o n v e r s i o n e s n e c e s a r i a s . @param n v a l o r de t i p o a <code>S t r i n g </code >. @param ca v a l o r de t i p o a <code>i n t </code >. @param c t a v a l o r de t i p o a <code>S t r i n g </code >. @param co v a l o r de t i p o a <code>S t r i n g </code >. / v o i d agregaAlumno ( S t r i n g n , i n t ca , S t r i n g c t a , S t r i n g co ) ; / Mtodo <code>e l i m i n a A l u m n o </code >: e l i m i n a a l alumno en l a e p o s i c i n <code>c u a l </code> en l a b a s e de d a t o s . o @param c u a l v a l o r de t i p o an <code>i n t </code >. / void eliminaAlumno ( i n t c u a l ) ; / Mtodo <code>a r m a R e g i s t r o </code >: Arma e l r e g i s t r o en l a e p o s i c i n <code>c u a l </code> p a r a l i s t a r l o . o @param c u a l v a l o r de t i p o an <code>i n t </code >. @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> / String armaRegistro ( int cual ) ;

143

Manejo de cadenas y expresiones


ServiciosCurso (3/3)

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


90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

/ Mtodo <code>d a m e L i s t a </code >: R e g r e s a l a l i s t a d e l g r u p o e organizada . @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> / S t r i n g dameLista ( ) ; / Mtodo <code>dameCurso </code >: R e g r e s a t o d a e l a c t a d e l grupo , e i n c l u y e n d o e l n mero d e l g r u p o . u @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> / S t r i n g dameCurso ( ) ;

/ Mtodo <code>losQueCazanCon </code >: r e g r e s a l a l i s t a de l o s e r e g i s t r o s que c o n t i e n e n l a s u b c a d e n a <code>q u i e n </code >. @param q u i e n v a l o r de t i p o a <code>S t r i n g </code >. @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> / S t r i n g losQueCazanCon ( S t r i n g q u i e n ) ; ... 134 } // S e r v i c i o s C u r s o

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 decir e o a la posicin relativa del registro que contiene esa informacin listado 4.4. o o

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


112 113 114 115 116 117 118 119

ServiciosCurso

/ 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 ) ;

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

4.3 Implementacin de una base de datos o

144

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

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


121 122 123 124 125 126 127 128 129 130 131 132 133

ServiciosCurso

/ 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 busqueda . @ 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 misn o ma. Siguiendo la metodolog que tenemos para denir las clases, una vez denidas a las responsabilidades debemos decidir cul es la informacin que requiere la clase a o 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 manera, o la descripcin de qu contiene cada registro. La manera como he decidido guardar o e esta base de datos es en una cadena enorme, pero subdividida en pedazos, cada uno 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 el correo electrnico a otro, el nmero de cuenta o u a un tercero 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 o columna de la tabla a un campo (o atributo). Supongamos que tenemos una lista de alumnos como la que se muestra en la Tabla 4.4 en la pgina opuesta: a 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 nmero u de posiciones que ocupa, por ejemplo, el nombre tambin debe ser el mismo para e que podamos calcular, de manera sencilla, la posicin en la cadena donde empieza o cada campo dentro de cada registro. La declaracin con valores iniciales para esta o lista se ver entonces, como se ve en el listado 4.6 en la pgina opuesta. a, a

145

Manejo de cadenas y expresiones

Tabla 4.4

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 C. de la Tierra Biolog a Actuar a ... Cuenta: 97541219-1 99036358-4 02598658-3 00276238-7 02639366-8 ... Correo: aguilarS. . . cruzCruz. . . garciaVi. . . hubardE. . . tapiaV. . . ...

Cdigo 4.6 Ejemplo de declaracin de una lista inicial para la base de datos o o
private String lista = " Aguilar Sol s Aries Olaf + " aguilarS@ciencias .mx" + "Cruz Cruz Gil No e + " cruzCruz@ciencias .mx" + " Garc a Villafuerte Israel + " garciaV@ciencias .mx " + " Hubard Escalera Alfredo + " hubardE@ciencias .mx " + " Tapia V zquez Rogelio a + " tapiaV@ciencias .mx " ; " + "122" + " 975412191 " " + "104" + " 990363584 " " + "127" + " 025986583 " " + "201" + " 002762387 " " + "101" + " 026393668 "

Recuerden que podemos construir una cadena concatenando cadenas, usando el operador +. Adems, aparecen los caracteres blancos dentro de las cadenas con a para que podamos ver fcilmente el nmero de posiciones que ocupa. Dividimos la a u cadena arbitrariamente en registros, uno por rengln, y cada registro en campos. o Noten que cada nombre ocupa 35 posiciones y cada carrera 3; el nmero de cuenta u ocupa 9 posiciones y el correo 20 posiciones. Cada elemento completo de la lista ocupa 67 posiciones. Ntese tambin que elegimos codicar la carrera, pues o e con tres caracteres tengo suciente para reconocerla; para el nmero de cuenta u usamos 9 posiciones, pues la que estar entre el octavo d a gito y el noveno siempre contiene un guin; por ultimo, para el correo de usuario utilizamos 20 posiciones, o completando cuando es necesario como lo hicimos en el nombre. La primera posicin en lista es la 0 y tiene en total 5 67  335 posiciones o (de la 0 a la 334). El primer registro (i  1) empieza en la posicin 0; el segundo o registro (i  2) empieza 67 posiciones despus, en la posicin 67. En general, el e o i-simo registro empieza en la posicin pi 1q 67. Por qu i 1? Porque hay e o e

4.3 Implementacin de una base de datos o

146

que saltar i 1 registros para llegar a donde empieza 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 35, una vez saltadas las primeras 35 posiciones (de o la 0 a la 34) que corresponden al nombre. La del segundo registro empieza en la posicin 102 (67+35), que corresponde a saltar las primeras 67 posiciones (de la o 0 a la 66) que corresponden al primer registro, ms las primeras 35 posiciones a que corresponden al nombre del segundo registro. En general, la posicin de la o carrera del i-simo registro empieza en la posicin pi 1q 67 35, donde 67 e o es el nmero de posiciones que hay que saltar por cada elemento de la tabla que u se encuentra antes que el que queremos y 35 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 i-simo e e o e registro (pi 1q 67) y a eso se le suma el total de las posiciones que ocupan los campos desde el primero hasta el j 1.

Figura 4.6

Tarjeta de responsabilidades para Curso. Clase: Curso


Constructores P u b l i c o getNombre daCarrera getCorreo getCuenta agregaAlumno eliminaAlumno listaCurso losQueCazanCon armaRegistro P r i v a d o lista grupo nmero de registros u

Responsabilidades
A partir de una base de datos inicial y a partir de una lista vac a. Regresa el nombre completo de un estudiante Regresa la carrera de un estudiante Regresa el correo 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 e identicarlo. 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 u que contiene el grupo

Construyamos una clase para manejar listas de cursos. Ya tenemos, de la interfaz, los mtodos pblicos que vamos a requerir; ahora hay que decidir qu atributos e u e

147

Manejo de cadenas y expresiones

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.6 en la pgina opuesta. a 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.7.

Cdigo 4.7 Clase que maneja listas de cursos o

Curso

100 package C o n s u l t a s ; 200 / 300 C l a s e C u r s o i m p l e m e n t a l a b a s e de d a t o s de e s t u d i a n t e s i n s c r i t o s 400 en un c u r s o . T i u e d n e l a s opc 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 500 y s e r u t i l i z a d a d e s d e un men . a u 600 C r e a t e d : Lun Mar 8 0 9 : 2 5 : 3 7 2 0 1 0 . 700 @ a u t h o r <a h r e f = m a i l t o : e l i s a @ t u r i n g > E u l i s a V i s o </a> 800 @ v e r s i o n 3 . 0 900 / 1000 p u b l i c c l a s s C u r s o implements S e r v i c i o s C u r s o { 1100 p r i v a t e S t r i n g g r u p o ; // C l a v e d e l g r u p o 1200 p r i v a t e S t r i n g l i s t a ; // I n f o r m a c i n de e s t u d i a n t e s o 1300 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 1400 / 1500 TAM XXXX : Tamao d e l campo n 1600 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 1700 / 1800 private static f i n a l int 1900 TAM REG = 67 , 2000 T NMBRE = 35 , 2100 T CARR = 3, 2200 T CTA = 9, 2300 T CORR = 20 , 2400 OFF NMBRE = 0 , 2500 OFF CARRE = 3 5 , 2600 OFF CTA = 38 , 2700 OFF CORR = 4 7 ;

En esta pequea prueba estamos utilizando unicamente una funcin de cadenas n o (String), substring, que entrega la subcadena que empieza en el primer parmetro a

4.3 Implementacin de una base de datos o

148

y termina en el segundo parmetro, ambos enteros. Este mtodo lo invocamos a e desde lista (que contiene los nombres y el resto de los datos). Un conjunto de mtodos relevantes de la clase String en esta etapa se encuentran en la tabla 4.2 e en la pgina 125. Tenemos un mtodo que nos encuentra la primera posicin del a e o i-simo registro, daPos(int i), y a partir de ah saltamos los campos que van antes e del que queremos. As el campo que corresponde al nombre est en la posicin , a o 0 de cada registro (OFF NMBRE = 0), mientras que para llegar al campo con el correo del usuario hay que saltar el tamao del nombre, ms el tamao de la n a n cuenta ms el tamao de la carrera (OFF CORR = 47 = 35 + 3 + 9). Similarmente a n localizamos el inicio de cada uno de los otros campos. Veamos ahora la implementacin de los constructores en el diagrama de la o gura 4.7. 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.7

Diagrama de Warnier-Orr para los constructores.


6 9Construye lista inicial 9 9 9 8 3 3

Copia la referencia numRegs

Constructor

9 estudiantes 9 3 9 9 7Registra clave del grupo Completa 4 posiciones

Calcula nmero de u

tam. lista{Tam. registro

Hab amos comentado que queremos dos constructores, uno que trabaje a partir de una lista inicial que d el usuario y otro que inicie con una lista vac En los e a. dos casos se podrn agregar nombres conforme el usuario lo solicite. El cdigo a o para ambos constructores se puede ver en el listado 4.8.

Cdigo 4.8 Constructores para la clase Curso o


3700 3800 3900 4000 4100 4200 4300 4400 4500 4600 4700 4800 4900 5000

Curso (1/2)

/ 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 t i p o <code>S t r i n g </code >: N mero de g r u p o u / public Curso ( S t r i n g grupo ) { i n t tamanho = g r u p o . l e n g t h ( ) ; t h i s . g r u p o = tamanho > 4 ? grupo . s u b s t r i n g ( 0 , 4) : ( ( tamanho < 4 ) && ( tamanho > 0 ) ) ? "0000" . s u b s t r i n g (0 ,4 tamanho ) : grupo ; l i s t a = "" ; // new S t r i n g ( ) ; numRegs = 0 ; }

149

Manejo de cadenas y expresiones


Curso (2/2)

Cdigo 4.8 Constructores para la clase Curso o


5200 5300 5400 5500 5600 5700 5800 5900 6000 6100 6200 6300 6400 6500 6600 6700 6800 6900 7000 7100

/ 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 d e l e usuario . @param g r u p o t i p o <S t r i n g >: C l a v e d e l g r u p o . @param l i s t a t i p o <code>S t r i n g </code >: l i s t a b i e n armada . / 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 ) { i n t tamanho = g r u p o . l e n g t h ( ) ; t h i s . g r u p o = tamanho > 4 ? grupo . s u b s t r i n g ( 0 , 4) : ( ( tamanho < 4 ) && ( tamanho > 0 ) ) ? "0000" . s u b s t r i n g (0 ,4 tamanho ) : grupo ; int tamLista = l i s t a . length ( ) ; numRegs = t a m L i s t a / TAM REG ; // V e r i f i c a que l o s r e g i s t r o s e s t n c o m p l e t o s e t h i s . l i s t a = t a m L i s t a % TAM REG == 0 // R e g i s t r o s c o m p l e t o s ? new S t r i n g ( l i s t a ) : l i s t a . s u b s t r i n g ( 0 , t a m L i s t a t a m L i s t a % TAM REG ) ; }

Ambos constructores editan el nmero de grupo, vericando que tenga cuau tro d gitos. Si tiene ms de cuatro lo recorta y si tiene menos de cuatro lo rellena a con ceros a la izquierda l neas 4200 a 4700 y 5900 a 6400. El primer constructor, que trabaja a partir de un a lista inicial de alumnos, verica que la lista tenga registros completos l neas 6800 a 7000; si no es as le quita los caracteres que , sobren ms all del ultimo registro completo. a a El mtodo que da la lista completa es muy sencillo, ya que unicamente regresa e la lista. Lo podemos ver en el listado 4.9.

Cdigo 4.9 Mtodo que regresa toda la lista o e


7400 7500 7600 7700 7800 7900 8000 8100 / Metodo <code>g e t L i s t a </code> P r o p o r c i o n a e l a t r i b u t o <code>l i s t a </code >. @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >: l a l i s t a . / public String getLista () { return l i s t a ; }

Curso

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

4.3 Implementacin de una base de datos o

150

Figura 4.8

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


6 5 9 9 9Calcula el inicio del lugar 9 9 9 9 i-simo registro e 9 9 9 5 9 9 9 9 9Calcula el inicio del 9 empza 8 3 9 9Calcula el nal del campo nal 9 9 9 9 9 9 9 9 9Toma la subcadena entre 9 9 9 7 esas dos posiciones

pi 1q TAM REG lugar OFF XXX

Obtiene campo del i-simo registro 9 e 9

campo en ese registro

empza TAM XXX

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

 pi 1q T AM

REG OF F XXX.

donde OFF XXX es un entero que corresponde a la distancia del campo al inicio del registro. Hay que tomar en cuenta ac al usuario, que generalmente va a numerar a los registros empezando desde el 1 (uno), no desde el 0 (cero). Con esto en mente y de acuerdo a lo que es el primer ndice en una cadena, que discutimos al inicio de este tema, el mtodo queda como se puede observar en el listado 4.10. e

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


8300 8400 8500 8600 8700 8800 8900 9000 9100 9200 9300

Curso

/ Da l a p o s i c i n en l a c a d e n a en l a que e m p i e z a e l campo dado en o e l i s i m o r e g i s t r o . e @param c u a l de t i p o <code>i n t </code >: nm . de r e g i s t r o . u @param o f f s e t de t i p o <code>i n t </code >: campo . @ r e t u r n t i p o <code>i n t </code >: P o s i c i n de l a c a d e n a en l a que o empieza e l r e g i s t r o s o l i c i t a d o . / p r i v a t e i n t daPos ( i n t c u a l , i n t o f f s e t ) { r e t u r n ( c u a l 1 ) TAM REG + o f f s e t ; }

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

151

Manejo de cadenas y expresiones


Curso (1/2)

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


9500 9600 9700 9800 9900 10000 10100 10200 10300 10400 10500 10600 10700 10800 10900 11000 11100 11200 11300 11400 11500 11600 11700 11800 11900 12000 12100 12200 12300 12400 12500 12600 12700 12800 12900 13000 13100 13200 13300 13400 13500 13600 13700 13800 13900 14000 14100 14200 14300 14400 14500

/ Mtodo <code>getGrupo </code >: r e g r e s a e l v a l o r de e <code>grupo </code >. @ r e t u r n v a l o r t i p o <code>S t r i n g </code >: e l v a l o r d e l a t r i b u t o . / public S t r i n g getGrupo ( ) { return grupo ; } / Mtodo <code>getNumRegs</code >: r e g r e s a e l n mero de r e g i s t r o s e u en l a b a s e de d a t o s . @ r e t u r n v a l o r t i p o <code>i n t </code >: N mero de r e g i s t r o s . u / p u b l i c i n t getNumRegs ( ) { r e t u r n numRegs ; } / Mtodo <code>getNombre </code >: R e g r e s a e l nombre en e l e registro solicitado . @param n t i p o <code>i n t </code >: e l n mero de r e g i s t r o . u @ r e t u r n v a l o r t i p o <code>S t r i n g </code >: e l nombre s o l i c i t a d o . / p u b l i c S t r i n g getNombre ( i n t c u a l ) { i n t p o s = daPos ( c u a l , OFF NMBRE ) ; r e t u r n ( ( c u a l < 0 ) | | ( c u a l > numRegs ) ) ? "No existe el registro " : l i s t a . s u b s t r i n g ( pos , p o s + T NMBRE ) ; } / Mtodo <code>g e t C o r r e o </code >, e x t r a e e l campo c o r r e s p o n d i e n t e e a correo del registro s o l i c i t a d o . @param n t i p o <code>i n t </code >: e l r e g i s t r o s o l i c i t a d o . @ r e t u r n v a l o r t i p o a <code>S t r i n g </code >: e l campo s o l i c i t a d o . / public String getCorreo ( int cual ) { i n t p o s = daPos ( c u a l , OFF CORR ) ; r e t u r n ( c u a l > numRegs ) ? "El registro no existe " : l i s t a . s u b s t r i n g ( pos , p o s + T CORR ) . t r i m ( ) ; } / Mtodo <code>g e t C u e n t a </code >: R e g r e s a e l campo e <code>c u e n t a </code> d e l r e g i s t r o s o l i c i t a d o . @param n t i p o <code>i n t </code >: e l r e g i s t r o s o l i c i t a d o . @ r e t u r n v a l o r t i p o <code>S t r i n g </code >: e l n mero de c u e n t a . u / public S t r i n g getCuenta ( int cual ) {

4.3 Implementacin de una base de datos o

152
Curso (2/2)

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


14600 14700 14800 14900 15000 15100 15200 15300 15400 15500 15600 15700 15800 15900 16000 16100 16200 16300 16400 i n t p o s = daPos ( c u a l , OFF CTA ) ; r e t u r n ( c u a l > numRegs ) ? "El registro no existe " : l i s t a . s u b s t r i n g ( pos , p o s + T CTA ) . t r i m ( ) ; } / M t o d o <code>g e t C a r r e r a </code >: r e g r e s a l a c a d e n a que e r e p r e s e n t a a l n mero de c a r r e r a . u

@param c u a l de t i p o <code>i n t </code >: n mero de r e g i s t r o . u @ r e t u r n de t i p o <code>S t r i n g </code >: c l a v e en c a d e n a . / public String getCarrera ( int cual ) { i n t p o s = daPos ( c u a l , OFF CARRE ) ; r e t u r n ( c u a l > numRegs ) ? "El registro no existe " : l i s t a . s u b s t r i n g ( pos , p o s + T CARR ) . t r i m ( ) ; }

Para regresar el nombre de la carrera hay que hacer un poco ms de trabajo a pues se encuentra codicada con una cadena de tres d gitos, que corresponde a la clave ocial de la carrera correspondiente. Agregamos las claves de las carreras en la l nea 2800 de la clase Curso:
2800 p r i v a t e s t a t i c f i n a l S t r i n g n C a r r e r a s = " 000101104106122127201217000 " ;

En la gura 4.9 se encuentra el algoritmo en un diagrama de Warnier.

Figura 4.9

Algoritmo para obtener el nombre de la carrera


6 9Obtener el registro deseado 9 9 3 9 9 9Existe 8 Obtener clave de la carrera 5 9 9 9 Avisar registro invlido a 9 9Existe 9 7 Salir 6 6 Obtener 9 9Buscar su posicin en claves nombre de la o 9 9 3 9 9 9 9 9 carrera 9 9 8Est 9 9 a lugar posicin 3 o 9 8 9 9 o 9Proceso Obtener posicin 9 9 9 de clave 9 3 9 9 9 9 9 9 9 9 7Est lugar 9 9 a 1 9 9 9 9 9 7 9 Obtener nombre de carrera en posicin obtenida o 9 9 3 9 9 7Final 6 9 9 9 9 9 9 9 9 9 9Inicio 9 9 9 9 9 9 9 9 9 9 9 8

153

Manejo de cadenas y expresiones


Curso

Cdigo 4.12 Mtodo que regresan el nombre de la carrera o e


16600 16700 16800 16900 17000 17100 17200 17300 17400 17500 17600 17700 17800 17900 18000 18100 18200 18300 18400 18500 18600 18700 18800 18900 19000 19100 19200

/ Mtodo <code>d a C a r r e r a </code >: Usa l a c l a v e de c a r r e r a e r e g i s t r a d a en e l r e g i s t r o s o l i c i t a d o p a r a d e v o l v e r e l nombre de l a c a r r e r a . @param n t i p o <code>i n t </code >: n mero de r e g i s t r o . u @ r e t u r n v a l o r t i p o <code>S t r i n g </code >: Nombre de l a c a r r e r a . / public String daCarrera ( int cual ) { f i n a l i n t TAMCAD = 2 6 ; i n t p o s = daPos ( c u a l , OFF CARRE ) ; S t r i n g s c a r r = l i s t a . s u b s t r i n g ( pos , p o s + T CARR ) . t r i m ( ) . t o L o w e r C a s e ( ) ; int cualCarr = nCarreras . indexOf ( s c a r r ) ; i n t posCa = c u a l C a r r < 0 ? 0 : c u a l C a r r / T CARR ; S t r i n g cadCarr = ( "No identificada " // 000 + " Actuar a " // 101 + " Ciencias de la Computaci n " // 104 o + " F sica " // 106 + " Matem ticas a " // 122 + " Ciencias de la Tierra " // 127 + " Biolog a " // 201 + " Manejo Sust de Zonas Cost " ) // 217 . s u b s t r i n g ( posCa TAMCAD, ( posCa + 1 ) TAMCAD) . trim ( ) ; return cadCarr ; }

Los mtodos que acabamos de programar suponen 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.10. e

Figura 4.10

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

6 9lugar la posicin de la subcadena4 o 9 9 5 9 9 9 la posicin del inicio del registro o 9 9Se encontr inicio 9 o 9 9 lugar Calcular el nmero de registro u Determinar 8 nmero u de registro 9 4 9 9 9 9 9Se encontr lugar 1 o 9 9 9 9 9 7

Regresar lugar

Este mtodo regresa la posicin si la encuentra, o e o

1 si no.

4.3 Implementacin de una base de datos o

154

Para encontrar la posicin de la subcadena simplemente usamos mtodos de o e la clase String. Para calcular el inicio del registro procedemos a restarle lo que sobre del mltiplo menor ms cercano del tamao del registro; nalmente vemos u a n cuntos registros caben en ese nmero ver diagrama de la gura 4.11. Si la a u primera presencia del nombre Lpez aparece a partir de la posicin 421 en la o o cadena, el primer registro que contiene ese apellido empieza en la posicin 414 o y corresponde al registro nmero 10 caben 9 registros completos antes de la u presencia de Lpez. La programacin se encuentra en el listado 4.13 de la o o siguiente pgina. a

Figura 4.11

Clculo del primer registro que contiene a una subcadena a

6 Reg. 0
A l b a

Buscamos: Lpez o lugar = 609 residuo de 609 67  6, 609 6

3 2 1 0

6 Reg. 10

Ga r c a

L p e o

 603 Registro empieza ac a 609 67 1  10 z Ma r a


615 614 613 612 611 610 609 608 607 606 605 604 601 603

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


19400 19500 19600 19700 19800 19900 20000 20100 20200 20300 20400 20500 20600 20700 / Mtodo <code>d a P o s i c i o n </code >: da e l o r d i n a l que e c o r r e s p o n d e a l p r i m e r r e g i s t r o que c o n t i e n e a l a s u b c a d e n a . @param nombre t i p o <code>S t r i n g </code >: s u b c a d e n a a buscar . @ 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 . toLowerCase ( ) . 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 ; }

Curso

155

Manejo de cadenas y expresiones

Supongamos ahora que no queremos al primero que contenga a 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 una posicin inicial a partir de dnde buscar. Le damos al mtodo daPosicion otra o o e rma que tome en cuenta este parmetro adicional. La programacin se encuentra a o en el listado 4.14.

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


20800 20900 21000 21100 21200 21300 21400 21500 21600 21700 21800 21900 22000 22100 22200 22300 22400 22500

Curso

/ Mtodo <code>d a P o s i c i o n </code >: Da e l o r d i n a l que c o r r e s e ponde a l r e g i s t r o que c o n t i e n e a l a s u b c a d e n a , a p a r t i r de l a p o s i c i n dada . o @param nombre t i p o <code>S t r i n g </code >: s u b c a d e n a a b u s c a r . @param d e s d e t i p o <code>i n t </code >: p o s i c i n a p a r t i r de o la cual buscar . @ r e t u r n t i p o <code>i n t </code >: 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 1 ) TAM REG ; i n t l u g a r = l i s t a . toLowerCase ( ) . 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 ; }

Una vez que podemos movernos en la cadena calculando el nmero de registro u que contiene a una cierta subcadena, podemos pasar al mtodo que localiza a un e alumno cuando se le da una subcadena que debe estar en el registro correspondiente. Veamos el diagrama de Warnier correspondiente en la gura 4.12.

Figura 4.12

Algoritmo para localizar un registro


6 9Inicio 9 9 9 9 9 9 9 9 9 9 8 9 9 9 9 9 9 9 9 9 7 3 6 5 9 nmero u 9 9Est 9 a 9 8 lugar 3 9 9 9 7Est a nmero u 3

lugar

Localiza estudiante 9Calcula registro 9 9 9 Final

posicin de la subcadena en lista o p plugar %TAM REGqq{TAM REG 1 1

Regresa nmero u

4.3 Implementacin de una base de datos o

156

Nuevamente tenemos dos puntos de partida para localizar una subcadena: el principio del registro o desde una posicin particular en la subcadena. En el o listado 4.15 se muestra el cdigo para ambos mtodos. o e

Cdigo 4.15 Implementacin de los mtodos que localizan un alumno o o e


22400 22500 22600 22700 22800 22900 23000 23100 23200 23300 23400 23500 23600 23700 23800 23900 24000 24100 24200 24300 24400 24500 24600

Curso

/ Mtodo <code>l o c a l i z a A l u m n o </code >: r e g r e s a l a p o s i c i n d e l e o e s t u d i a n t e que c a c e con l a c a d e n a s o l i c i t a d a . . @param c a d e n a t i p o <code>S t r i n g </code >. @ r e t u r n v a l o r t i p o <code>i n t </code> / public i n t l o c a l i z a A l u m n o ( S t r i n g subcad ) { i n t pos = d a P o s i c i o n ( subcad ) ; r e t u r n p o s < 0? 1 : p o s ; } / Mtodo <code>l o c a l i z a A l u m n o </code >: r e g r e s a l a p o s i c i n d e l e o e s t u d i a n t e que c a c e con l a c a d e n a s o l i c i t a d a , b u s c a n d o a p a r t i r d e l r e g i s t r o dado . @param c a d e n a t i p o <code>S t r i n g </code >: c a d e n a b u s c a d a . @param d e s d e t i p o <code>i n t </code >: a p a r t i r de . @ r e t u r n t i p o <code>i n t </code >: N mero de r e g i s t r o . y u / p u b l i c i n t l o c a l i z a A l u m n o ( S t r i n g subcad , i n t d e s d e ) { i n t p o s = d a P o s i c i o n ( subcad , d e s d e ) ; r e t u r n p o s < 0? 1 : p o s ; }

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.13. Lo unico relevante es preguntar si el registro que nos piden existe o no. Para preguntar si un registro existe basta ver si su nmero u es vlido: debe estar entre 1 y el nmero total de registros. a u

Figura 4.13

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 registro 3 9 9 7Existe el registro i Di que no existe

157

Manejo de cadenas y expresiones

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

Cdigo 4.16 Edicin de un registro individual o o


24800 24900 25000 25100 25200 25300 25400 25500 25600 25700 25800 25900 26000 26100 26200 26300

Curso

/ Mtodo <code>a r m a R e g i s t r o </code >: arma e l r e g i s t r o e que s e e n c u e n t r a en l a p o s i c i n i p a r a m o s t r a r l o . o @param c u a l t i p o <code>i n t </code >: p o s i c i n o del registro solicitado . @ r e t u r n v a l o r t i p o <code>S t r i n g </code >: e l d e s p l i e g u e del registro . / public String armaRegistro ( int cual ) { r e t u r n ( ( c u a l > 0 && c u a l <= numRegs ) ? ( ( getNombre ( c u a l ) + b l a n c o s ) . s u b s t r i n g ( 0 ,T NMBRE) + "\t" +( d a C a r r e r a ( c u a l ) + b l a n c o s ) . s u b s t r i n g ( 0 , 3 0 ) + "\t" + ( g e t C u e n t a ( c u a l ) + b l a n c o s ) . s u b s t r i n g ( 0 , T CTA ) + "\t" + ( g e t C o r r e o ( c u a l ) + b l a n c o s ) . s u b s t r i n g ( 0 , T CORR ) ) : "No se encontr al nombre buscado " ) ; o }

Vale la pena mencionar que estamos rellenando los campos con blancos a la derecha por ejemplo, (daCarrera(cual) + blancos) porque nuestros mtodos nos e van a regresar los campos podados. Una vez que le pegamos un nmero jo de u blancos lo recortamos a cierto tamao . substring (0,30) tambin para mejorar la n e apariencia de nuestras tablas. De los procesos 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.14. a

Figura 4.14

Algoritmos para listar el curso.


6 9 9 9Principio 9 9 9 9 9 9 9 9 8 5

sLista xGrupo:zt + grupo i

lista curso

e 9 muestra i-simo registro 9 avanza al siguiente 9(mientras i 9 numRegs) 9 7 9 9 9 9 9 3 9 9 7Final Entrega la lista armada

6 9arma el registro i 8

4.3 Implementacin de una base de datos o

158

En Java tenemos varios enunciados compuestos que iteran. El ms general de a ellos es de la forma
6 9 enunciado simple 9 9 9 8 9 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.15.

Figura 4.15

Enunciado compuesto while.


Sintaxis: xenunciado compuesto whiley::= while ( xexpresin booleanay ) { o xenunciado simple o compuestoy ... xenunciado simple o compuestoy } ntica: Sema 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 iteracin o o puede no ejecutarse, ya que la condicin puede no cumplirse desde la primera vez o que se intenta iterar; por ultimo, en el cuerpo de la iteracin se debe cambiar el o estado de una o ms de las variables involucradas en la condicin, para que haya a o posibilidad de salir de la iteracin. o

159

Manejo de cadenas y expresiones

Un buen ejemplo del uso de esta iteracin es el 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.16 e y la codicacin en el listado 4.17. o

Figura 4.16

Encontrar el l mite de

Calcular l mite de

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

1 , 2n

dado
6 9 = .001 9 8

double fAnt 1.0 pf0 q 9double fActl 1.0{2.0 pf1 q 9 7 n1

2n

9 Calcular siguiente trmino 9 e 9 9 9 mientras fant factl 7f Actl 9 9 9 i++ 9 9 9 9 3 9 9 7Final

6 8f Ant

fActl fAnt{2

(Vamos contando)

Reporta fActl e i

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


100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400 package C o n s u l t a s ; import j a v a . u t i l . S c a n n e r ; class Limites { p u b l i c v o i d l i m i t e ( double e p s i l o n ) { double f A c t l = 1 . 0 / 2 . 0 ; double f A n t = 1 ; int i = 2; w h i l e ( Math . a b s ( f A n t f A c t l ) >= e p s i l o n ) { System . o u t . p r i n t l n ( "fAnt= " + f A n t + "\ nfActl = "+f A c t l ) ; fAnt = f A c t l ; fActl = fActl / 2; i ++; } System . o u t . p r i n t l n ( "f(n)= " + f A c t l + " con n= " + i ) ; } 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 ) { S c a n n e r c o n s = new S c a n n e r ( System . i n ) ; double e p s i l o n ; L i m i t e s l i m = new L i m i t e s ( ) ; System . o u t . p r i n t ( "Dame el valor para epsilon : " ) ; e p s i l o n = cons . nextDouble ( ) ; lim . l i m i t e ( epsilon ) ; } }

4.3 Implementacin de una base de datos o

160

Como se puede ver, esta iteracin es ideal cuando no tenemos claro el nmero o u de veces que vamos a repetir el proceso 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 para ilustrar que vamos a sumar nmeros que nos proporu cionen desde la consola hasta que nos den un 1. El algoritmo se puede ver en la gura 4.17.

Figura 4.17

Sumar nmero mientras el usuario no de un u


6 9 9Inicializar 9 9 9 9 9 9 9 8 9 9 9 9 7Final 4

suma 0 nmero 0 u

Sumar una lista Sumar nmeros u de nmeros u ( mientras 9 9 9 nmero $ -1) 9 u 9

4 3

Sumar nmero le u do Leer siguiente nmero u

Entregar resultado

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

Figura 4.18

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 llevar a cabo 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 booleanay. Si sta se evala a verdadero, la ejecucin contina o e u o u en el primer enunciado del bloque; si es falsa, sale de la iteracin y sigue o adelante con el enunciado que sigue al do . . . while.

161

Manejo de cadenas y expresiones

Tambin en esta iteracin debemos tener cuidado en inicializar y declarar e o 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.18 en la o n pgina opuesta en el listado 4.18. a

Cdigo 4.18 Suma de nmeros le o u dos


100 200 300 400 500 600 700 800 900 1000 public i n t sumaLeidos ( Scanner cons ) { i n t suma = 0 ; i n t numero = 0 ; do { suma += numero ; System . o u t . p r i n t ( "Dame un n mero . Termina con -1 --> " ) ; u numero = c o n s . n e x t 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 o con la 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, como se puede observar en el listado 4.19. Esto nos va a repetir el cdigo, o o pero el resultado de la ejecucin va a ser exactamente el mismo. Por otro lado, si o 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, o y como cuerpo de la condicional colocamos el bloque original. De esta manera si la condicin no se cumple al principio el bloque no se ejecuta. Esto quiere decir que si o un lenguaje de programacin unicamente cuenta con una de estas dos iteraciones, o sigue teniendo todo lo necesario para elaborar mtodos pensados para la otra e iteracin. o Cdigo 4.19 Suma de nmeros le con while o u dos
100 200 300 400 500 600 700 800 900 1000 1100 1200 p u b l i c i n t sumaLeidosW ( S c a n n e r c o n s ) { i n t suma = 0 ; i n t numero = 0 ; System . o u t . p r i n t ( "Dame un n mero . Termina con -1 --> " ) ; u numero = c o n s . n e x t I n t ( ) ; w h i l e ( numero != 1) { suma += numero ; // E l u l t i m o que s e l e y o System . o u t . p r i n t ( "Dame un n mero . Termina con -1 --> " ) ; u numero = c o n s . n e x t I n t ( ) ; } // w h i l e r e t u r n suma ; } // sumaLeidosW

4.3 Implementacin de una base de datos o

162

Tenemos una tercera iteracin conocida como for, que resulta ser el caon o n 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 nuestro dispositivo u de salida usaremos System.out, mientras que para la entrada de datos utilizaremos java.util.Scanner, a quien, como ya mencionamos antes, tendremos que importar. Podemos regresar ahora al problema de listar todo el curso en una pantalla proporcionada por el usuario, usando la clase Scanner y el enunciado compuesto while. El algoritmo lo mostramos en la gura 4.19 y la programacin se encuentra o en el listado 4.20 en la pgina opuesta. a

Figura 4.19

Algoritmo para listar el grupo


6 9Principio 9 9 9 9 9 9 9 9 9 9 9 9 9 8 9 9 9 9 9 9 9 9 9 9 9 7 3

Inicia cadenas
6 9 9Principio 9 9 9 9 9 8 9 9 9 9 7

sListado Grupo: grupo z n 4 Colocarse al principio de la serie


5 3

Listar todo Procesa estudiante Procesa registro el grupo 9Lista estudiantes 9 (mientras haya) 9 Avanza al siguiente 9 9 9
3

nal Entrega lista

nal

Cdigo 4.20 Mtodo que lista todo el curso o e


26500 26600 26700 26800 26900 27000 27100 27200 27300 27400 27500 27600 27700 27800 / Mtodo <code>d a m e L i s t a </code >: R e g r e s a l a l i s t a d e l g r u p o e organizada . @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> / public S t r i n g dameLista () { int cual = 1; S t r i n g s L i s t a = "" ; w h i l e ( c u a l <= numRegs ) { s L i s t a = s L i s t a + "\n" + c u a l + "\t" + a r m a R e g i s t r o ( c u a l ) ; c u a l ++; } return s L i s t a ; }

Curso

163

Manejo de cadenas y expresiones


Curso

Cdigo 4.20 Mtodo que lista todo el curso o e


28000 28100 28200 28300 28400 28500 28600 28700 28800 28900

/ Mtodo <code>dameCurso </code >: R e g r e s a t o d a e l a c t a d e l grupo , e i n c l u y e n d o e l n mero d e l g r u p o . u @ r e t u r n v a l o r t i p o a <code>S t r i n g </code> / p u b l i c S t r i n g dameCurso ( ) { S t r i n g s C u r s o = " GRupo :\t" + g r u p o + "\n\n" + d a m e L i s t a ( ) + "\n" ; return sCurso ; }

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.20.

Figura 4.20

Algoritmo para agregar un estudiante.

6 9Ajusta tama o de nombre n 9 9 9Ajusta tama o de correo n 9 8

Agrega registro

n u 9Ajusta tama o de n mero de cuenta 9 9 9Agrega la cadena completa a la lista 9 7 Incrementa contador de registros

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, como lo o n hicimos al armar la tabla para la impresin. La programacin se encuentra en el o o listado 4.21 en la siguiente pgina. a

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


29100 29200 29300 29400 29500 29600 29700 29800

Curso (1/2)

/ Mtodo <code>agregaAlumno </code >: Crea un r e g i s t r o de alumno e con l o s d a t o s d a d o s . @param nmbre t i p o <code>S t r i n g </code >: Nombre . @param c a r r e r a t i p o <code>i n t </code >: C l ; a v e de c a r r e r a . @param c u e n t a t i p o <code>S t r i n g </code >: e m a i l . @param c o r r e o t i p o <code>S t r i n g </code >: N mero de c u e n t a . u /

4.3 Implementacin de una base de datos o

164
Curso (2/2)

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


29900 30000 30100 30200 30300 30400 30500 30600 30700 p u b l i c v o i d agregaAlumno ( S t r i n g nmbre , i n t c a r r e r a , S t r i n g cuenta , S t r i n g c o r r e o ) { nmbre = ( nmbre + b l a n c o s ) . s u b s t r i n g ( 0 ,T NMBRE ) ; String sCarrera = String . valueOf ( c a r r e r a ) ; c o r r e o = ( c o r r e o + b l a n c o s ) . s u b s t r i n g ( 0 , T CORR ) ; c u e n t a = ( c u e n t a + b l a n c o s ) . s u b s t r i n g ( 0 , T CTA ) ; l i s t a = l i s t a + nmbre + s C a r r e r a + c u e n t a + c o r r e o ; numRegs++; }

No es tan fcil eliminar a un estudiante de la lista usando subcadenas. Si el a 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 inmediatamente antes de donde empieza el ultimo; pero en cambio, si al que deseamos eliminar se encuentra en una posicin o intermedia, tenemos que partir la lista en tres pedazos: lo que va antes, el registro que deseamos eliminar y lo que va despus ver gura 4.21. e

Figura 4.21

Posibles situaciones para eliminar a un registro.

Caso 1

loooooooooooooooooooooooooooooooooooooooooooooomoooooooooooooooooooooooooooooooooooooooooooooon looooooooooooooooooooooooomooooooooooooooooooooooooon

loooooooooooooooooomoooooooooooooooooon

Caso 2

loooooooooooooooooooooooooooooooooooooooooooooomoooooooooooooooooooooooooooooooooooooooooooooon

Caso 3

De la gura podemos ver que tenemos que hacer un anlisis por casos; estamos a obligados a ello porque el mtodo que vamos a usar para extraer subcadenas no e puede recibir una posicin que no existe, que ser el caso si queremos eliminar o a al primero o al ultimo. El algoritmo se muestra en la gura 4.22 en la pgina a opuesta.

165

Manejo de cadenas y expresiones

Figura 4.22

Algoritmo para eliminar a un estudiante de la lista.


6 3 9Es el primero Se queda desde el segundo hasta el ultimo 9 9 9 9 9 3 9 9 8Es el ultimo Se queda desde el primero hasta el penltimo u Eliminar el 3 registro i 9 9 9Est en medio Juntar del 1 al i-1 y del i+1 al nal a 9 9 9 9 9 7Disminuye numRegs

Conocemos de cul de estas tres situaciones se trata (no hay otra posibilidad) a dependiendo de la posicin del registro: o Es el primero si i vale 1. Es el ultimo si i vale NumRegs. Est en medio si 1 i NumRegs. a Para este tipo de enunciado es conveniente que introduzcamos el xenunciado compuesto condicionaly cuya sintaxis y semntica se encuentra en la gura 4.23. a Sintaxis: xenunciado compuesto condicionaly::= if ( xexpresin booleanay ) { o xenunciado simple o compuestoy ... } else { xenunciado simple o compuestoy ... } ntica: Sema 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 Con esto podemos ya pasar a programar el mtodo que elimina al i-simo e e registro de nuestra lista, en el listado 4.22 en la siguiente pgina. a

Figura 4.23

Enunciado compuesto condicional

4.3 Implementacin de una base de datos o

166
Curso

Cdigo 4.22 Mtodo que elimina al registro i o e


30900 31000 31100 31200 31300 31400 31500 31600 31700 31800 31900 32000 32100 32200 32300 32400 32500 32600 32700 32800 32900 33000 33100 33200 33300 33400 33500 / Mtodo <code>e l i m i n a A l u m n o </code >: E l i m i n a a l i s i m o e e r e g i s t r o de l a b a s e de d a t o s . @param n t i p o <code>i n t </code >: E l n mero d e l r e g i s t r o u a eliminar . / public void eliminaAlumno ( i n t c u a l ) { i f ( c u a l <= 0 | | c u a l > numRegs ) { // n mero i n v l i d o ? u a return ; } i n t p o s = daPos ( c u a l , 0 ) ; i f ( c u a l == 1 ) { // e s e l p r i m e r o i f ( numRegs > 1 ) { // No e s e l u n i c o l i s t a = l i s t a . s u b s t r i n g (TAM REG ) ; // b r i n c a e l p r i m e r o } e l s e { // e s e l u n i c o l i s t a = "" ; } } else { i f ( 1 < c u a l && c u a l < numRegs ) { // e s t en medio a l i s t a = l i s t a . s u b s t r i n g (0 , pos ) + l i s t a . s u b s t r i n g ( p o s + TAM REG ) ; } else { // e s e l u l t i m o l i s t a = l i s t a . s u b s t r i n g (0 , pos ) ; } } numRegs ; // Decrementa e l n mero de r e g i s t r o s u }

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 localizamos al primer registro que contiene a la subcadena. A partir de ese momento, buscamos la siguiente presencia de la subcadena a partir del siguiente registro y lo inclu mos en el listado. Terminamos cuando ya no haya presencia de la subcadena en lo que resta de la lista. El algoritmos lo podemos ver en la gura 4.24.

Figura 4.24

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


6 9 9 9Principio 9 9 9 9 8 5

donde Primer registro que caza. cazaCon (cadena vac a)

Arma cadena

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

cazaCon donde

cazaCon + armaRegistro(donde) Siguiente registro que caza

167

Manejo de cadenas y expresiones

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.23. a

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


33700 33800 33900 34000 34100 34200 34300 34400 34500 34600 34700 34800 34900 35000 35100 35200 35300 35400 / Mtodo que 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 e 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 t i p o <code>S t r i n g </code >: La que s e b u s c a en cada r e g i s t r o @ r e t u r n t i p o <code>S t r i n g </code >: 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 ) { i n t p o s = l i s t a . i n d e x O f ( s u b c a d ) ; // E l p r i m e r o que c a z a i n t c u a l ; // N mero de r e g i s t r o u S t r i n g l o s Q u e S i = "" ; w h i l e ( p o s != 1 && ( c u a l = d a C u a l ( p o s ) ) <= numRegs ) { l o s Q u e S i = l o s Q u e S i + a r m a R e g i s t r o ( c u a l ) + "\n" ; p o s = l i s t a . i n d e x O f ( subcad , c u a l TAM REG ) ; } return losQueSi ; }

Curso

Lo unico merecedor de atencin es la invocacin al mtodo daCual(int) que nos o o e regresa, dada una posicin en la cadena que corresponde a la lista, el nmero de o u registro correspondiente. Este mtodo est en el listado 4.24. e a

Cdigo 4.24 Mtodo que calcula el nmero de registro a partir de una posicin en la lista o e u o
35600 35700 35800 35900 36000 36100 36200 36300 36400 / Mtodo <code>daCual </code >: Dada l a p o s i c i n en l a c a d e n a e o r e g r e s a e l n mero de r e g i s t r o . u @param p o s t i p o <code>i n t </code >: p o s i c i n en l a l i s t a . o @ r e t u r n t i p o <code>i n t </code >: N mero de r e g i s t r o . u / p r i v a t e i n t daCual ( i n t pos ) { r e t u r n p o s / TAM REG + 1 ; }

Curso

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.4 Una clase para el men u

168

4.4 Una clase para el men u


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.25.

Figura 4.25

para Cursos 9 9 9

6 9 9 9Principio 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 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 7Final

Men para uso de la clase Curso. u


5 6 9Muestra el men u 9 9 9 9Pide opcin al usuario 3 o 9 9 9 9opcin == Termina Salir o 9 9 9 9 9 9 3 9 9 Pide registro 9opcin == Agrega 9 o 9 Agrega registro 9 9 9 9 6 9 9 9 9Pregunta a quin 9 e 9 9 9 9 9Buscarlo 9 9 9 8 9 2 9 9 Encontrado Quitarlo 9opcin == Quita o 9 9 9 9 9 9 9 3 9 9 9 Maneja Men 9 u 8 7Encontrado Reporta No encontrado (mientras desee 9 6 9 el usuario) 9 9 9Pregunta a quin 9 e 9 9 9 9 9 9 9Buscarlo 9 8 9 2 9 9 9opcin == Busca Encontrado Reportarlo o 9 9 9 9 9 9 3 9 9 9 9 Reporta 9 9 7Encontrado 9 9 No encontrado 9 9 9 9 3 9 9 9 Lista todo el curso 9opcin == Lista o 9 9 9 9 9 5 9 9 9 9 opcin == Todos los Pregunta subcadena 9 o 9 9 que cazan Arma la lista 7 3

Preparar acumuladores Aplicar el men al curso solicitado u

Despedirse

169

Manejo de cadenas y expresiones

Para construir el cdigo correspondiente al del diagrama de la Figura 4.25 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.26.

Figura 4.26

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 xvalor1 y, . . . deben ser del mismo tipo que la xexpresiny, 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 xexpresiny y va comparando contra los valores que aparecen u o frente a la palabra case. El primero que coincide hace que a partir de ese punto se ejecuten todos los enunciados 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 etiqueta de escape, o default, que puede o no aparecer. Si aparece y la expresin no toma ninguno o de los valores expl citamente listados, se ejecuta el bloque correspondiente a default. Si no hay etiqueta de escape y el valor de la expresin no caza con o ninguno de los valores en los case, entonces el programa abortar dando un a error de ejecucin. o El comando break que mencionamos en la gura 4.26 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.27. a a o

Figura 4.27

Enunciado break.
Sintaxis: xenunciado breaky ::= break; ntica: Sema 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.

4.4 Una clase para el men u

170

El enunciado break tambin se puede usar dentro de una iteracin en una e o condicional y hace que el hilo de ejecucin del programa salga de la iteracin o o ms cercana al break. a Veamos algunos ejemplos de la forma que podr tomar distintos switches. an
boolean e s t a = i != 1; switch ( e s t a ) { case f a l s e : System . o u t . p r i n t l n ( "NO est " ) ; a break ; case t r u e : System . o u t . p r i n t 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 el enunciado if queda de la siguiente manera:
boolean e s t a = i != 1; i f ( e s t a ) System . o u t . p r i n t l n ( "SI est " ) ; a else System . o u t . p r i n t 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 para soltero; c para casado; d para divorciado; v para viudo; y u para unin libre. o En una variable de tipo char tenemos una de estas opciones y deberemos proporcionar una cadena con el texto adecuado. La programacin quedar algo o a parecido a lo que se observa en el listado 4.25.

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


100 200 300 400 500 600 700 800 900 1000 1100 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 ; // Para g u a r d a r e l l e t r e r o 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 " ; } // 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

171

Manejo de cadenas y expresiones

En general, la estructura switch lo que hace es ir comparando el valor del selector con cada uno de las constantes que se encuentran a continuacin de la o palabra case. Pueden aparecer varias etiquetas case frente a un unico bloque de cdigo, para referirse a que esa es la accin a realizarse en ms de un caso: o 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 correspono diente 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.26.

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


100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400 2500 2600 2700 2800 2900 3000 3100 3200

MenuCurso (1/4)

package C o n s u l t a s ; import j a v a . u t i l . S c a n n e r ; / c l a s s MenuCurso e s l a c l a s e de u s o p a r a l a b a s e de d a t o s d e l grupo . C r e a t e d : Lun Jun 20 2 0 1 1 . @ a u t h o r <a h r e f = m a i l t o : e l i s a @ l a m b d a . f c i e n c i a s . unam . mx> E l i s a V i s o G u r o v i c h </a> @version 3.0 / p u b l i c c l a s s MenuCurso { p r i v a t e s t a t i c f i n a l i n t FIN = 0 , AGREGA = 1 , BUSCA = 2 , LISTA = 3 , ELIGE = 4 , QUITA = 5 , MUESTRA = 6 ; p r i v a t e s t a t i c f i n a l S t r i n g elMenu = "[0] Terminar \n" + "[1] Agregar estudiante \n" + "[2] Buscar estudiante \n" + "[3] Listar todos \n" + "[4] Listar los que cazan con ...\n" + "[5] Eliminar estudiante \n" + "[6] Mostrar estudiante \n" ; private int opcion = 0; p r i v a t e S t r i n g s o p c i o n = "" ; / Mtodo <code>daMenu</code >: P i d e a l u s u a r i o l a o p c i n e o y la procesa .

4.4 Una clase para el men u

172
MenuCurso (2/4)

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


3300 3400 3500 3600 3700 3800 3900 4000 4100 4200 4300 4400 4500 4600 4700 4800 4900 5000 5100 5200 5300 5400 5500 5600 5700 5800 5900 6000 6100 6200 6300 6400 6500 6600 6700 6800 6900 7000 7100 7200 7300 7400 7500 7600 7700

@param c o n s t i p o <code>S c a n n e r </code >: d i s p o s i t i v o de s a l i d a . @param miCurso t i p o <code>Curso </code >: c u r s o a p r o c e s a r . @ r e t u r n t i p o <code>i n t </code >: o p c i n e l e g i d a . o / p u b l i c i n t daMenu ( S c a n n e r cons , C u r s o miCurso ) { s o p c i o n = "" ; opcion = 0; System . o u t . p r i n t l n ( elMenu ) ; System . o u t . p r i n t l n ( " Elige una opcion -->\t" ) ; sopcion = cons . nextLine ( ) ; i f ( s o p c i o n . l e n g t h ( ) == 0 ) { // t e c l e a r o n [ e n t e r ] System . o u t . p r i n t l n ( " Opcion invalida " ) ; return 0; } o p c i o n = " 0123456 " . i n d e x O f ( s o p c i o n . c h a r A t ( 0 ) ) ; S t r i n g quien , subcad ; int cual ; boolean b r e s p = f a l s e ; switch ( opcion ) { case FIN : System . o u t . p r i n t l n ( " Mucho gusto en haberle servido " ) ; r e t u r n 1; case AGREGA : S t r i n g nombre = pideNombre ( c o n s ) ; S t r i n g cuenta = pideCuenta ( cons ) ; S t r i n g c o r r e o = pideCorreo ( cons ) ; i n t c a r r e r a = p i d e C a r r e r a ( cons ) ; miCurso . agregaAlumno ( nombre , c a r r e r a , c o r r e o , c u e n t a ) ; r e t u r n AGREGA ; case BUSCA : System . o u t . p r i n t l n ( "Dame la cadena a buscar : " ) ; subcad = cons . n e x t L i n e ( ) ; i f ( s u b c a d . l e n g t h ( ) == 0 ) { System . o u t . p r i n t l n ( "Hubo un error de entrada " ) ; r e t u r n BUSCA ; } c u a l = miCurso . l o c a l i z a A l u m n o ( s u b c a d ) ; i f ( c u a l < 0) { System . o u t . p r i n t l n ( "No existe registro con esta cadena " ) ; r e t u r n BUSCA ; } do { System . o u t . p r i n t l n ( "El alumnos buscado es :\t" + miCurso . a r m a R e g i s t r o ( c u a l ) ) ; System . o u t . p r i n t l n ( " Deseas ver al siguiente ? (S/N)" ) ; S t r i n g sResp = cons . n e x t L i n e ( ) ;

173

Manejo de cadenas y expresiones


MenuCurso (3/4)

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


7800 7900 8000 8100 8200 8300 8400 8500 8600 8700 8800 8900 9000 9100 9200 9300 9400 9500 9600 9700 9800 9900 10000 10100 10200 10300 10400 10500 10600 10700 10800 10900 11000 11100 11200 11300 11400 11500 11600 11700 11800 11900 12000 12100 12200 12300 12400 12500 12600 12700

i f ( s R e s p == n u l l | | s R e s p . l e n g t h ( ) == 0 ) s R e s p = "N" ; b r e s p = s R e s p . c h a r A t ( 0 ) == S ; i f (! bresp ) { r e t u r n BUSCA ; } i f ( c u a l > miCurso . getNumRegs ( ) ) { System . o u t . p r i n t l n ( "Se acab la base de datos " ) ; o r e t u r n BUSCA ; } c u a l = miCurso . l o c a l i z a A l u m n o ( subcad , c u a l ) ; } while ( cual > 0 ) ; System . o u t . p r i n t l n ( "No hay mas registros con esa subcadena " ) ; r e t u r n BUSCA ; case LISTA : System . o u t . p r i n t l n ( miCurso . d a m e L i s t a ( ) ) ; r e t u r n LISTA ; case ELIGE : System . o u t . p r i n t l n ( "Qu subcadena deben cazar :" ) ; e subcad = cons . n e x t L i n e ( ) ; i f ( s u b c a d . l e n g t h ( ) == 0 ) { System . o u t . p r i n t l n ( "No se dio una cadena v lida " a + " a buscar " ) ; r e t u r n ELIGE ; } System . o u t . p r i n t l n ( miCurso . losQueCazanCon ( s u b c a d ) ) ; r e t u r n ELIGE ; case QUITA : System . o u t . p r i n t l n ( "Dame el alumno a eliminar " ) ; quien = cons . nextLine ( ) ; i f ( q u i e n . l e n g t h ( ) == 0 ) { System . o u t . p r i n t l n ( "Me diste una cadena inv lida " ) ; a r e t u r n QUITA ; } bresp = false ; cual = 0; do { c u a l = miCurso . l o c a l i z a A l u m n o ( q u i e n , c u a l ) ; i f ( c u a l < 0) { System . o u t . p r i n t l n ( "Ya no hay m s con este campo " ) ; a r e t u r n QUITA ; } System . o u t . p r i n t ( " Eliminar a *" + miCurso . getNombre ( c u a l ) . t r i m ( ) + "*, (S/N) --> " ) ; S t r i n g sResp = cons . n e x t L i n e ( ) ; i f ( s R e s p == n u l l | | s R e s p . l e n g t h ( ) == 0 ) s R e s p = "N" ; b r e s p = s R e s p . c h a r A t ( 0 ) == S ; i f ( bresp ) { miCurso . e l i m i n a A l u m n o ( c u a l ) ; System . o u t . p r i n t l n ( " Alumno eliminado del curso " ) ;

4.4 Una clase para el men u

174
MenuCurso (4/4)

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


12800 12900 13000 13100 13200 13300 13400 13500 13600 13700 13800 13900 14000 14100 14200 14300 14400 14500 14600 14700 14800 14900 15000 15100 15200 15300 15400 15500

r e t u r n QUITA ; } System . o u t . p r i n t l n ( " Deseas ver el siguiente ? (S/N)" ) ; S t r i n g sResp = cons . n e x t L i n e ( ) ; i f ( s R e s p == n u l l | | s R e s p . l e n g t h ( ) == 0 ) s R e s p = "N" ; b r e s p = s R e s p . c h a r A t ( 0 ) == S ; } w h i l e ( b r e s p && c u a l <= miCurso . getNumRegs ( ) && c u a l > 0 ) ; System . o u t . p r i n t l n ( "No se elimin a nadie " ) ; o r e t u r n QUITA ; case MUESTRA: System . o u t . p r i n t l n ( "Dame una subcadena a buscar " ) ; quien = cons . nextLine ( ) ; i f ( q u i e n . l e n g t h ( ) == 0 ) { System . o u t . p r i n t l n ( "Dato invalido " ) ; r e t u r n MUESTRA; } c u a l = miCurso . l o c a l i z a A l u m n o ( q u i e n ) ; i f ( c u a l != 1) { System . o u t . p r i n t l n ( miCurso . a r m a R e g i s t r o ( c u a l ) ) ; } else { System . o u t . p r i n t l n ( "No hay registro con esta subcadena " ) ; } r e t u r n MUESTRA; default : System . o u t . p r i n t l n ( " Opcion no existente " ) ; return 0; } }

Para el men construimos una cadena que se muestra separada por renglones, u como se observa en las l neas 1900 a 2600, y le pedimos al usuario que elija un nmero de opcin, en las l u o neas 4000 a 4200. En esta ultima l nea aparece algo nuevo, ya que estamos interaccionando con el usuario. La manera de hacerlo es a travs de la consola para escribir (System.out) y un objeto de la clase Scanner e para leer (por eso aparece un objeto de esta clase como parmetro). Por lo pronto a vamos a leer cadenas, pero antes de leer escribiremos en la pantalla una cadena en la que se solicita el dato. Estamos suponiendo 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 4700 del listado 4.26. 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.26. En el caso de nuestro problema, cada bloque del switch termina con un return,

175

Manejo de cadenas y expresiones

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.4.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 l u neas 5200 y 5300 del listado 4.26.

4.4.2.

Agrega estudiante
Esta opcin tiene que funcionar como una intermediaria (interfaz) entre el o mtodo de la base de datos y el usuario, para agregar de manera correcta a un e estudiante. Por ello, deberemos primero llenar cada uno de los campos del registro (ser absurdo pedirle al usuario que conociera cmo est implementada nuestra a o a base de datos). Para eso se procede a solicitarle al usuario cada uno de los campos involucrados. Elegimos hacer un mtodo distinto para cada campo, para poder e indicarle al usuario qu tipo de cadena estamos esperando. La codicacin de e o cada uno de estos mtodos se encuentran en el listado 4.27 en la siguiente pgina. e a Algunos de estos mtodos los volveremos a usar, ya que nos proporcionan por e parte del usuario informacin. Cada uno de estos mtodos simplemente le dice al o e usuario qu es lo que espera y recibe una cadena que, idealmente, deber ser lo e a que el mtodo espera l e neas 5400 a 6000 del listado 4.26.

Cdigo 4.27 Mtodos para pedir datos del estudiante al usuario o e


15100 15200 15300 15400 15500 15600 15700 15800 15900 16000 16100 16200 16300 16400 16500 16600 16700 16800

MenuCurso (1/2)

/ Mtodo <code>pideNombre </code >, p i d e e l nombre d e l e s t u d i a n t e . e @param c o n s t i p o <code>S c a n n e r </code> p a r a l e e r d a t o s del usuario . @ r e t u r n t i p o <code>S t r i n g </code >, e l d a t o p r o p o r c i o n a d o . / p r i v a t e S t r i n g pideNombre ( S c a n n e r c o n s ) { System . o u t . p r i n t l n ( "Dame el nombre :\t" ) ; S t r i n g nombre = c o n s . n e x t L i n e ( ) ; r e t u r n nombre ; } / Mtodo <code>p i d e C u e n t a </code >, p i d e e l numero de c u e n t a d e l e estudiante . @param c o n s t i p o <code>S c a n n e r </code> p a r a l e e r d a t o s d e l u s u a r i o . @ r e t u r n t i p o <code>S t r i n g </code >, e l d a t o p r o p o r c i o n a d o . /

4.4 Una clase para el men u

176
MenuCurso (2/2)

Cdigo 4.27 Mtodos para pedir datos del estudiante al usuario o e


16900 17000 17100 17200 17300 17400 17500 17600 17700 17800 17900 18000 18100 18200 18300 18400 18500 18600 18700 18800 18900 19000 19100 19200 19300 19400 19500 19600 19700 19800 19900 20000 20100 20200 20300 20400 20500 20600 20700 20800 20900 21000 21100 21200 21300 21400 private S t r i n g pideCuenta ( Scanner cons ) { System . o u t . p r i n t l n ( "Dame el numero de cuenta :\t" ) ; S t r i n g cuenta = cons . nextLine ( ) ; return cuenta ; }

/ Mtodo <code>p i d e C o r r e o </code >, p i d e e l c o r r e o d e l e s t u d i a n t e . e @param c o n s t i p o <code>S c a n n e r </code> p a r a l e e r d a t o s d e l usuario . @ r e t u r n t i p o <code>S t r i n g </code >, e l d a t o p r o p o r c i o n a d o . / private S t r i n g pideCorreo ( Scanner cons ) { System . o u t . p r i n t l n ( "Dame el correo :\t" ) ; S t r i n g c o r r e o = cons . nextLine ( ) ; return correo ; } / Mtodo <code>p i d e C a r r e r a </code >, p i d e l a c a r r e r a d e l e s t u d i a n t e e y v e r i f i c a que s e a una c l a v e v l i d a . a @param c o n s t i p o <code>S c a n n e r </code> p a r a l e e r d a t o s d e l usuario . @ r e t u r n t i p o <code>i n t </code >, e l d a t o p r o p o r c i o n a d o . / private i nt p i d e C a r r e r a ( Scanner cons ) { i n t c a r r e r a = 1; while ( c a r r e r a < 0) { System . o u t . p r i n t l n ( "Dame la clave de carrera : \n" + " (101) Actuar a \n" + " (201) Biolog a \n" + " (104) Ciencias de la Computaci n \n" o + " (127) Ciencias de la Tierra \n" + " (106) F sica \n" + " (217) Manejo Sust de Zonas Cost\n" + " (122) Matem ticas \n--> " ) ; a c a r r e r a = cons . n e x t I n t ( ) ; c o n s . n e x t L i n e ( ) ; // Para u s a r e l r e t o r n o de c a r r o i f (! esCarrera ( carrera )) { System . o u t . p r i n t l n ( " Carrera inv lida " a + "\ nIntenta otra vez" ) ; c a r r e r a = 1; } } return c a r r e r a ; }

177

Manejo de cadenas y expresiones

Es necesario vericar, cuando nos proporcionan la clave de una carrera, que sea correcta. En la l nea 20700 invocamos a un mtodo que hace esto. El mtodo e e se encuentra en el listado 4.28 y hace lo siguiente: i. Convierte el entero a cadena, rellenando con ceros por la izquierda si es necesario l neas 224 a 226. ii. Verica que esta subcadena se encuentre en la lista de claves l nea 227. iii. Finalmente verica que la haya encontrado (!= 1) y que empiece en la primera posicin de alguna clave de tres d o gitos ( % 3 == 0) l nea 228.

Cdigo 4.28 Vericacin de que la clave de carrera es correcta o o


216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

MenuCurso

/ Mtodo <code>e s C a r r e r a </code >: V e r i f i c a que l a c l a v e de l a e c a r r e r a sea vl i d a . . a @param c a r r e r a v a l o r de t i p o <code>i n t </code> p a r a @ r e t u r n t i p o <code>b o o l e a n </code >: c o r r e c t a o i n c o r r e c t a . / p r i v a t e boolean e s C a r r e r a ( i n t c a r r e r a ) { f i n a l S t r i n g n C a r r e r a s = " 000101104106122127201217000 " ; String sCarrera = String . valueOf ( c a r r e r a ) ; i n t tam = s C a r r e r a . t r i m ( ) . l e n g t h ( ) ; s C a r r e r a = "0000" . s u b s t r i n g (0 ,3 tam ) + s C a r r e r a ; i n t pos = n C a r r e r a s . indexOf ( s C a r r e r a ) ; i f ( p o s == 1 | | p o s % 3 != 0 ) r e t u r n f a l s e ; return true ; }

4.4.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. Tambin debemos e vericar si hay algn otro estudiante con la misma subcadena en caso de que no u se haya encontrado al que el usuario ten en mente. Para ello entramos en un a ciclo del que saldremos cuando el usuario decida, o bien no haya ms estudiantes a con la caracter stica solicitada. El cdigo de este ciclo se puede ver en las l o neas 11300 a 13400 del listado 4.26 en la pgina 173. El cdigo de la opcin completa a o o se encuentra en en el mismo listado, l neas 10400 a 13600.

4.4 Una clase para el men u

178

4.4.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. Tambin ac debe mostrar, de as desearlo el usuario, uno por e a uno los registros que contienen a la subcadena. Esto se realiza de la l nea 7300 a 8800 del listado 4.26 en la pgina 172. Toda la opcin se encuentra de la l a o nea 6100 a la 9000 del mismo listado.

4.4.5.

Lista todo el curso


Simplemente invoca al mtodo correspondiente de la base de datos l e neas 9100 a 9300 del listado 4.26.

4.4.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 contenie a dos de cadenas y nos dir siempre que no son iguales l a neas 9400 a 10300 del listado 4.26.

4.4.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 l o neas 15100 a 15300 del listado 4.26.

4.4.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 l u neas 24100 a 25300 del listado 4.29. Hay que crear un objeto de la clase MenuCurso ya que siendo main un mtodo esttico, e a de la clase, no tiene acceso ni a los atributos ni a los mtodos de la clase; stos e e slo son accesibles desde algn objeto de la clase. Una vez hecho esto simplemente o u entraremos en una iteracin mostrando el men y recibiendo la opcin, mientras o u o

179

Manejo de cadenas y expresiones

el usuario no decida terminar. La programacin de este mtodo se encuentra en o e el listado 4.29, l neas 23800 a 26000.

Cdigo 4.29 Mtodo principal (main) de la clase MenuCurso o e


23800 23900 24000 24100 24200 24300 24400 24500 24600 24700 24800 24900 25000 25100 25200 25300 25400 25500 25600 25700 25800 25900 26000

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 ) { S c a n n e r c o n s = new S c a n n e r ( System . i n ) ; System . o u t . p r i n t l n ( " Bienvenido al Sistema de registro " ) ; C u r s o miCurso = new C u r s o ( "7050" , // C u r s o " Aguilar Sol s Aries Olaf " + "122" + " 400001528 " + " aaguilar " + "Cruz Cruz Gil No e " + "104" + " 098034011 " + " ncruz " + " Garc a Villafuerte Israel " + "127" + " 098159820 " + " igarcia " + " Hubard Escalera Alfredo " + "201" + " 099586197 " + " ahubard " + " Tapia V zquez Rogelio a " + "101" + " 098162075 " + " rtapia " // l i s t a ); MenuCurso menu = new MenuCurso ( ) ; int resp = 0; do { r e s p = menu . daMenu ( cons , miCurso ) ; } w h i l e ( r e s p >= 0 ) ; System . o u t . p r i n t l n ( "Fue un placer servirte " ) ; cons . c l o s e ( ) ; }

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

4.5 Colofn o
Vale la pena mencionar un truco que se llev a cabo repetidamente en la o aplicacin para simplicar la programacin: extendimos las cadenas ms all de o o a a su nal normal para no preocuparnos por dar un carcter a la derecha que en a realidad no existe. Esto lo hicimos para rellenar cadenas con blancos y para elegir la carrera correspondiente.

4. Ejercicios

180

Ejercicios
4.1.- Tenemos los siguientes atributos declarados en una clase:
p r i v a t e i n t num = 0 , l i n = 0; p r i v a t e S t r i n g c a d e n a = "" ;

y el siguiente mtodo en el que queremos leer los valores para estos tres e atributos:
public void leeDatos ( Scanner cons ) { System . o u t . p r i n t l n ( "Dame los datos a leer :\n" + "en el siguiente orden : (int) (int) ( String )" ) ; num = c o n s . n e x t I n t ( ) ; cons . nextLine ( ) ; l i n = cons . n e x t I n t ( ) ; cons . nextLine ( ) ; cadena = cons . n e x t L i n e ( ) ; cons . nextLine ( ) ; }

D exactamente cmo tienen que teclearse los datos (o aparecer en un archivo o de texto) para que se asignen bien los valores. 4.2.- Cules son los enunciados que acomodan una cadena y un entero que apaa recen como sigue:
1785 E s t a e s una c a d e n a

4.3.- Supongamos que tenemos dos cadenas:


S t r i n g cad1 = " Otra vez mas S t r i n g cad2 = " OTRA VEZ MAS " ; ";

y queremos que al compararlas nos diga la aplicacin que son iguales. Cmo o o es el enunciado de comparacin de cadenas que lograra esto? o 4.4.- Tengo las claves de categora y Ciencias: AA Asociado A AB AC Asociado C TA TB Titular B TC nivel de los profesores de la Facultad de Asociado B Titular A Titular C

181

Manejo de cadenas y expresiones

Escribe los enunciados necesarios (de declaracin y funciones de cadenas) o para que, dada una de las claves vlidas, se imprima el nombre de la catea gor Debes vericar que la clave es vlida. a. a 4.5.- Escribe un mtodo que recibe como argumentos una cadena que va a replicar e y un entero el nmero de veces que debe replicar la cadenas; regresa la u cadena replicada el nmero de veces. u 4.6.- Como en el ejercicio anterior, pero con un tercer parmetro que le dice el a tamao mximo de la cadena que regresa, para lo que deber truncar la n a a ultima rplica. e 4.7.- Tenemos el siguiente diagrama de Warnier para obtener el nmnero de preu sencias de una subcadena en una cadena.
6 9 9 9 9 9Inicio 9 9 9 9 9 9 8 Obtener 9 6 9Obtener Cadena 9 8 9 9 7Buscar primera presencia 5

Obtener subcadena

nmero de u 9 presencias 9Cuenta presencia contador++ 9 9 (mientras haya) 9 9 Buscar siguiente presencia 9 9
9 9 9 9 7Final 3

Reportar contador

4.8.- Dibuja el diagra de Warnier y codica un mtodo que recibe como entrada e una cadena cad, una subcadena subcad y un entero n. El mtodo debe locae lizar la presencia nsima de sucad en cad y regresar la subcadena de subcad e que contiene n presencias de subcad y termina exactamente donde termina la nsima presencia de subcad. e 4.9.- Codica un mtodo que suma cuatro enteros que recibe como parmetros. e a Codica tambnin la llamada a este mtodo, donde los argumentos con los e e que se llama son los primeros cuatro que se usaron para invocar a la clase (en main). 4.10.- Codica un mtodo que escriba si la suma de sus argumentos, que son e enteros, es mayor que cero, igual a cero o menor que cero. 4.11.- Escribe un mtodo que reciba como parmetro un carcter y le asigne una e a a categor de la siguiente manera: a

4. Ejercicios
De l a a , e , i , o , u : v o c a l e s , , i , , : v o c a l e s a c e n t u a d a s a e o u 0 . . . 9 : d g i t o s el resto : resto

182

El mtodo tiene que estar imp[lementado con un switch. e 4.12.- Tenemos el siguiente pedazo de cdigo escrito con un while. Convirtelo a o e un do . . . while
i n t i = c o n s . n e x t I n t ( ) ; // Suponemos c o n s i n i c i a l i z a d o adecuadamente i n t k = cons . n e x t I n t ( ) ; int cont = 0; i n t suma ; w h i l e ( ++i < k ) { c o n t ++; suma += i ; }

4.13.- Escribe el cdigo necesario para que dado un entero entre 0 y 9, se escriba o con letro el enter ): cero, 1:uno, y as sucesivamente. 4.14.- Tenemos el siguiente pedazo de cdigo escrito con if anidados que escribe o con letra cualquier nmero entre 0 y 29. Convirtelo a un switch. u e
public String convierte ( int i ) { S t r i n g v a l o r = "" ; // O b t e n e r d g i t o de d e c e n a s i n t d e c e n a s = i % 30 / 1 0 ; // 0 <= d e c e n a s < 3 // O b t e n e r u n i d a d e s i n t u n i d a d = ( i % 3 0 ) % 1 0 ; // 0 <= u n i d a d <= 9 System . o u t . p r i n t l n ( "i=" + i + "\ tdecimal =" + d e c e n a s + "\ tunidad =" + unidad ) ; i f ( d e c e n a s == 0 ) { i f ( u n i d a d e s == 0 ) { v a l o r = "cero" ; } } else { i f ( d e c e n a s == 1 ) { i f ( u n i d a d == 1 ) { v a l o r = "once" ; } else { i f ( u n i d a d == 2 ) { v a l o r = "doce" ; } else { i f ( u n i d a d == 3 ) {

183

Manejo de cadenas y expresiones


v a l o r = " trece " ; } else { i f ( u n i d a d == 4 ) { v a l o r = " catorce " ; } else { i f ( u n i d a d == 5 ) { v a l o r = " quince " ; } else { i f ( u n i d a d == 0 ) { v a l o r = "diez" ; } else { v a l o r = " dieci " ; } } } } } } } else { i f ( d e c e n a s == 2 ) { i f ( u n i d a d == 0 ) { v a l o r = " veinte " ; } else { v a l o r = " veinti " ; } } } } i f ( u n i d a d >= 6 ) { i f ( u n i d a d == 6 ) { v a l o r += "seis" ; } else { i f ( u n i d a d == 7 ) { v a l o r += " siete " ; } else { i f ( u n i d a d == 8 ) { v a l o r += "ocho" ; } else { i f ( u n i d a d == 9 ) { v a l o r += " nueve " ; } } } } } i f ( d e c e n a s == 0 | | d e c e n a s == 2 ) {

4. Ejercicios
i f ( u n i d a d == 1 ) { v a l o r += "uno" ; } else { i f ( u n i d a d == 2 ) { v a l o r += "dos" ; } else { i f ( u n i d a d == 3 ) { v a l o r += "tres" ; } else { i f ( u n i d a d == 4 ) { v a l o r += " cuatro " ; } else { i f ( u n i d a d == 5 ) { v a l o r += " cinco " ; } } } } } } r e t u r n v a l o r + "***" ; }

184

Datos estructurados

La implementacin que dimos en el cap o tulo anterior para nuestra base de datos es demasiado alejada de cmo abstraemos la lista del grupo. Realmente, o cuando pensamos en una lista es una cierta coleccin de registros, donde cada o registro tiene uno o ms campos. Tratemos de acercarnos un poco ms a esta a a abstraccin. o Lo primero que tenemos que denir es qu es lo que queremos decir con una e estructura de datos, lo que hacemos a continuacin: o

Denicin 5.1 Una estructura de datos es una coleccin de datos, organizada de o o


cierta manera y que permite el acceso a sus elementos siguiendo una determinada disciplina. Las estructuras de datos pueden ser: Dinmicas o estticas, que pueden o no cambiar el nmero de sus elementos a a u durante ejecucin, respectivamente. o Homogneas o heterogneas, donde todos sus elementos son del mismo tipo e e o de varios tipos distintos, respectivamente. Lineales o no lineales, donde se puede formar a los elementos en una sola la (enumerarlos) uno despus del otro o no hay manera unica de hacer esto, e respectivamente. De acceso directo o secuencial, si es que se puede obtener a un elemento en particular extrayndolo directamente o se tiene que recorrer a toda la e estructura desde el principio para encontrar al elemento que se desea.

186 Las cadenas de caracteres son estructuras de datos lineales cada carcter a ocupa una posicin numerada del cero en adelante, estticas las cadenas, una o a vez construidas, no cambian ni de tamao ni de contenido, homogneas todos n e sus elementos son caracteres y de acceso directo mediante el mtodo charAt. e Los objetos tambin son estructuras de datos lineales podemos dar una lista de e los atributos, de donde los podemos formar uno despus del otro, estticas cada e a objeto construido tiene un tamao denido ya que est formado por variables de n a tipo primitivo o de tipo referencia y de acceso directo a travs del nombre del e campo, pero son heterogneas cada atributo puede ser de un tipo distinto. Los e dispositivos como el Scanner son estructuras de datos lineales, homogneas, de e acceso secuencial y dinmicas. a 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.2 Una lista es: o


o bien i. La lista vac aquella que no tiene ningn elemento representada a, u formalmente como [ ] o ; ii. el primer elemento de la lista, seguido de una lista representado formalmente como (a,), donde es a su vez 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, [ ]) . Una lista con tres a: elementos, a, b y c se representa como (a, (b, (c, [ ]) , o sea la lista cuyo primer elemento es a seguida de la lista cuyo primer elemento es b, seguida de la lista cuyo primero elemento es c, seguida de la lista vac Podemos usar una notacin a. o abreviada de lo que es una lista usando corchetes y omitiendo la lista vac con la a que terminan todas las listas. As (a, [ ]) se representa como [a] , mientras que , una lista con tres elementos a, b y c, en ese orden, quedar como [a, b, c] ; la a lista vac se sigue representando con [ ]. a Las listas son estructuras de datos lineales listamos en orden a sus elementos, homogneas todos sus elementos son del mismo tipo, dinmicas tenemos e a operaciones que agregan elementos o quitan elementos de las listas y de acceso secuencial para llegar al tercer elemento tenemos que ir desgranando la lista usando a los primeros elementos de las sublistas que la componen. 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 a a null, que corresponde a una referencia nula. Generalmente representamos las listas como se muestra en la gura 5.1 en la pgina opuesta, con @r representando un a campo (atributo) para guardar all una referencia y el s mbolo [ ] representando

187 que no sigue nadie (una referencia nula).

Datos estructurados

Como la denicin de la lista es recursiva, debemos siempre tener anclado o 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 (estudiante)


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 auto referida. Como lo dicta la orientacin a objetos, primero haremos una interfaz que me o describa los servicios que debe dar la clase que represente a cada uno de los registros del estudiante, a la que llamaremos ServiciosEstudiante. Requerimos los mismos servicios que antes, que arme el nombre, la carrera, etctera, pero adems, e a como ahora se trata de campos en un objeto, tambin tendremos los mtodos get e e y set correspondientes. El cdigo de esta interfaz, que no merecer ms explicacin, o a o se encuentra en el listado 5.1 en la siguiente pgina. Para la interfaz no requerimos a de dispositivos de entrada y salida, por lo que no importamos nada.

Del ingls, chain. e

5.1 La clase para cada registro (estudiante)

188
ServiciosEstudiante (1/4)

Cdigo 5.1 Interfaz para los servicios del registro de estudiantes o

1 package C o n s u l t a s L i s t a s ; 2 / 3 I n t e r f a z <code>S e r v i c i o s E s t u d i a n t e </code> d e s c r i b e l o s s e r v i c i o s 4 que d a r l a c l a s e <code>E s t u d i a n t e </code >. a 5 @ a u t h o r <a h r e f = m a i l t o : e l i s a @ c i e n c i a s . f c i e n c i a s . unam . mx> 6 E l i s a V i s o G u r o v i c h </a> 7 @version 1.0 8 /

189

Datos estructurados
ServiciosEstudiante (2/4)

Cdigo 5.1 Interfaz para los servicios del registro de estudiantes o

9 public interface ServiciosEstudiante { 10 / 11 Mtodo <code>getNombre </code >: r e g r e s a campo <code>nombre</code >. e 12 @ r e t u r n t i p o <code>S t r i n g </code >: c o n t e n i d o d e l campo . 13 / 14 p u b l i c S t r i n g getNombre ( ) ; 15 / 16 17 Mtodo <code>g e t C u e n t a </code >: r e g r e s a campo <code>c u e n t a </code >. e 18 @ r e t u r n t i p o <code>S t r i n g </code >: c o n t e n i d o d e l campo . 19 / 20 public S t r i n g getCuenta ( ) ; 21 22 / 23 Mtodo <code>g e t C o r r e o </code >: r e g r e s a campo<code>c o r r e o </code >. e 24 @ r e t u r n t i p o <code>S t r i n g </code >: c o n t e n i d o d e l campo . 25 / 26 public String getCorreo ( ) ; 27 / 28 29 Mtodo <code>g e t C a r r e r a </code >: r e g r e s a c o n t e n i d o d e l campo e 30 <code>c a r r e r a </code >. 31 @ r e t u r n t i p o <code>i n t </code >: c o n t e n i d o d e l campo . 32 / 33 public int getCarrera ( ) ; 34 35 / 36 Mtodo <code>g e t S i g u i e n t e </code >: r e g r e s a c o n t e n i d o d e l campo e 37 <code>s i g u i e n t e </code >. 38 @ r e t u r n t i p o <code>E s t u d i a n t e </code >: c o n t e n i d o d e l campo . 39 / 40 public Estudiante getSiguiente ( ) ; 41 42 / 43 Mtodo <code>setNombre </code >: a c t u a l i z a <code>nombre</code >. e 44 @param n t i p o <code>S t r i n g </code> p a r a nuevo v a l o r de nombre . 45 / 46 p u b l i c v o i d setNombre ( S t r i n g n ) ; 47 48 / 49 Mtodo <code>s e t C u e n t a </code >: a c t u a l i z a <code>c u e n t a </code >. e 50 @param c t i p o <code>S t r i n g </code> p a r a nuevo v a l o r de c u e n t a . 51 / 52 public void setCuenta ( S t r i n g c ) ;

5.1 La clase para cada registro (estudiante)

190
ServiciosEstudiante (3/4)

Cdigo 5.1 Interfaz para los servicios del registro de estudiantes o


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 93 94 95 96 97 98 99 100 101 102 103 104 105

/ Mtodo <code>s e t C o r r e o </code >: a c t u a l i z a <code>c o r r e o </code >. e @param c t i p o <code>S t r i n g </code >: nuevo v a l o r de <code>c o r r e o </code >. / public void s e t C o r r e o ( S t r i n g c ) ; / Mtodo <code>s e t C a r r e r a </code >: a c t u a l i z a <code>c a r r e r a </code >. e @param c t i p o <code>i n t </code >: p a r a e l nuevo v a l o r de <code>c a r r e r a </code >. / public void s e t C a r r e r a ( i n t c ) ; / Mtodo <code>s e t S i g u i e n t e </code >: a c t u a l i z a e l campo e <code>s i g u i e n t e </code >. @param s t i p o <code>E s t u d i a n t e </code> p a r a e l nuevo v a l o r de <code>s i g u i e n t e </code >. / 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 ) ; / <code>daNombre</code >: Arma c a d e n a de tama o f i j o con e l n <code>nombre</code> y l a r e l l e n a con b l a n c o s a l a d e r e c h a . @ r e t u r n t i p o <code>S t r i n g </code >: l a c a d e n a r e l l e n a d a . / p u b l i c S t r i n g daNombre ( ) ; / Mtodo <code>daCuenta </code >: arma una c a d e n a de tama o f i j o e n con e l campo <code>c u e n t a </code> r e l l e n a n d o con c e r o s p o r la izquierda . @ r e t u r n t i p o <code>S t r i n g </code >: l a c a d e n a con c e r o s . / p u b l i c S t r i n g daCuenta ( ) ; / Mtodo <code>d a C a r r e r a </code >: r e g r e s a e l nombre de l a c a r r e r a e con un tama o f i j o , r e l l e n a d o de b l a n c o s a l a d e r e c h a . n @ r e t u r n t i p o <code>S t r i n g </code >: c a r r e r a r e l l e n a d a con blancos . / public String daCarrera ( ) ; / Mtodo <code>d a C o r r e o </code >: r e g r e s a una c a d e n a de tama o f i j o e n con e l c o r r e o d e l e s t u d i a n t e s o l i c i t a d o , r e l l e n a d a con b l a n c o s por l a derecha . @ r e t u r n t i p o <code>S t r i n g </code >: e l c o r r e o s o l i c i t a d o . / public S t r i n g daCorreo ( ) ;

191

Datos estructurados
ServiciosEstudiante (4/4)

Cdigo 5.1 Interfaz para los servicios del registro de estudiantes o

107 / 108 Mtodo <code>a r m a R e g i s t r o </code >: arma e l r e g i s t r o p a r a m o s t r a r , e 109 j u n t a n d o t o d a s l a s c a d e n a s en e l r e g i s t r o . 110 @ r e t u r n t i p o <code>S t r i n g </code >: e l r e g i s t r o armado . 111 / 112 public String armaRegistro ( ) ; 113 }

Los mtodos que empiezan con get y set tienen el signicado acostumbrado, e trabajando directamente con el estado del objeto. Podemos ver en el listado, adems, los mtodos que editan los campos y que empiezan con da. Por ultimo, a e tenemos tambin el mtodo que arma todo el registro. e e Hemos decidido manejar un catlogo de carreras para unicar el acceso al misa mo. La clase correspondiente a este catlogo (CatalogoCarreras) se encargar de a a manejar el tamao de la clave y el tamao del nombre de carreras. Por el momenn n to supondremos que la clase es una clase que nos proporciona el sistema. Para evitarnos problemas, leeremos este catlogo de un archivo en disco en el cap a tulo en que veamos manejo de excepciones y archivos la destaparemos. La denicin o de la clase se encuentra en el Listado 5.2, donde unicamente aparecen los enca bezados y la descripcin de los mtodos. Por supuesto que unicamente aparece lo o e que es accesible desde fuera, que es lo de acceso pblico. u En el caso del catlogo de carreras se decidi no denir una interfaz pues a o todos los mtodos y atributos son estticos a la clase, por lo que no ser necesario e a a construir un objeto para manejarla.

Cdigo 5.2 Denicin de la clase CatalogoCarreras o o


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

CatalogoCarreras (1/2)

package C o n s u l t a s L i s t a s ; import j a v a . i o . ; import j a v a . u t i l . S c a n n e r ; / C l a s s <code>C a t a l o g o C a r r e r a s </code> c a r g a a memoria e l c a t a l o g o de carreras . @ a u t h o r <a h r e f = m a i l t o : e l i s a @ l a m b d a . f c i e n c i a s . unam . mx></a> @version 1.0 / public class CatalogoCarreras { / C o n s t a n t e <code>TAM CLAVE</code> p a r a d a r e l tama o de l a s n c l a v e s de c a r r e r a . / p u b l i c s t a t i c f i n a l i n t TAM CLAVE = 3 ;

5.1 La clase para cada registro (estudiante)

192
CatalogoCarreras (2/2)

Cdigo 5.2 Denicin de la clase CatalogoCarreras o o


35 36 37 38 39 40 41 42 43 44 45 46 89 90 91 92 93 94 95 96 98 99 100 101 102 103 104 105 106 121 122 123 124 125 126 127 145 146 147 148 149 150 151 152 153

/ C o n s t a n t e <code>TAM NOMBRE</code> p a r a d a r e l tama o de l o s n nombres de c a r r e r a . / p u b l i c s t a t i c f i n a l i n t TAM NOMBRE = 3 6 ; / Mtodo <code>g e t C a r r e r a s </code> r e g r e s a una c a d e n a con l a s e claves . @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >. / public static String getCarreras () { }

...

/ Mtodo <code>g e t S C a r r e r a s </code> r e g r e s a una c a d e n a con l o s e nombres . @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >. / public static String getSCarreras () { }

...

/ Mtodo <code>d a C a r r e r a </code >, dada l a c l a v e de l a c a r r e r a e r e g r e s a e l nombre . @param c u a l v a l o r de t i p o <code>i n t </code >. @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >. / public s t a t i c S t r i n g daCarrera ( i n t c u a l ){ }

...

/ Mtodo <code>m u e s t r a C a t a l o g o </code> m u e s t r a e l c a t l o g o de l a s e a carreras . / public s t a t i c void muestraCatalogo ( ) { ... } / Mtodo <code>e s C a r r e r a </code> d i c e s i l a c l a v e p r o p o r c i o n a d a e s e vlida . a @param c u a l v a l o r de t i p o <code>i n t </code> c l a v e p r o p o r c i o n a d a . @ r e t u r n v a l o r de t i p o <code>b o o l e a n </code >. / p u b l i c s t a t i c boolean e s C a r r e r a ( i n t c u a l ) {

165 } 166 }

...

193

Datos estructurados

Una vez que logramos separar del estudiante el manejo del catlogo de carreras, a y utilizando a este ultimo, procedemos a programar la clase para los registros de los estudiantes, a la que llamaremos Estudiante. Veamos por lo pronto los atributos (datos) que tiene esta clase en el listado 5.3. Lo primero que aparece son unas constantes, que en este paquete me indican el tamao con el que quiero editar a n cada uno de los campos, los enteros correspondientes a las carreras y los cdigos o correspondientes a las mismas. Todas estas constantes deben ser compartidas por todos los objetos que se construyan de esta clase, pues son las mismas p[ara todos los objetos, por lo que estn declaradas estticas. En esta ocasin ya no es necesario a a o mantener a los campos con blancos a la derecha, pues como est cada uno en su a atributo, el tamao de uno no inuye en la localizacin del otro: accedemos a ellos n o por el nombre del atributo, no por su posicin en el registro. Tambin vale la pena o e notar que no importa el orden en el que listamos los atributos o campos, pues nos vamos referir a ellos por su identicador.

Cdigo 5.3 Atributos de la clase Estudiante o


100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400 2500 2600 2700

Estudiante (1/2)

package C o n s u l t a s L i s t a s ; import j a v a . u t i l . S c a n n e r ; // Para l e e r d a t o s import u t i l e s . Cadenas ; // M todos p a r a e d i t a r campos e import u t i l e s . C a t a l o g o C a r r e r a s ; // C a t l o g o de c a r r e r a s a / C l a s e <code>E s t u d i a n t e </code> r e p r e s e n t a a un r e g i s t r o de l a b a s e de d a t o s . Base de d a t o s , b a s a d a en 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 r e a t e d : Wed Apr 22 0 8 : 4 6 : 5 3 2009 @ a u t h o r <a>E l i s a V i s o </a> @version 2.0 / p u b l i c c l a s s E s t u d i a n t e implements S e r v i c i o s E s t u d i a n t e { / Para e d i c i n d e l r e g i s t r o / o p u b l i c s t a t i c f i n a l i n t TAM NMBRE = 3 6 , // Para m o s t r a r TAM CTA = 9 , // Para m o s t r a r TAM CORREO = 2 0 , // Para m o s t r a r TAM CARRERA = C a t a l o g o C a r r e r a s .TAM NOMBRE; // Para m o s t r a r / A t r i b u t o s de l a c l a s e / p r i v a t e S t r i n g nombre ; / Nombre d e l e s t u d i a n t e . / private S tr i n g cuenta ; / N mero de c u e n t a d e l e s t u d i a n t e . / u private int carrera ; / C a r r e r a que c u r s a . / private String correo ; / C o r r e o e l e c t r n i c o . / o p r i v a t e E s t u d i a n t e s i g u i e n t e ; / R e f e r e n c i a a l r e s t o de l a l i s t a . /

5.1 La clase para cada registro (estudiante)

194
Estudiante (2/2)

Cdigo 5.3 Atributos de la clase Estudiante o


2800 2900 3000 3100 3200 3300 / C o n s t a n t e s que i d e n t i f i c a n a l o s campos / public static f i n a l int NOMBRE = 1 , CUENTA = 2 , CARRERA = 3 , CORREO = 4 ;

Mantenemos el tipo que dimos a los campos en la implementacin anterior, o excepto por la carrera que la tenemos ahora como un campo tipo int; el atributo siguiente es una referencia al siguiente de la lista (a la lista que le sigue), se reere a otro Estudiante, y lo denotamos dndole el tipo de la clase, y que, como todas las a variables del tipo de alguna clase, constituyen (contienen, sus valores corresponden a) 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 el que asigna valores determinados a los campos, si queremos uno sin parmetros tambin lo tenemos a e que programar nosotros. Por ultimo, queremos un constructor que haga una copia de un objeto dado; en trminos de computacin a esto se le llama clonar, pero e o por el momento no entraremos en esos detalles. Los constructores se encuentran en el listado 5.4.

Cdigo 5.4 Constructores para la clase Estudiante o


350 360 370 380 390 400 410 420 430 440 450 460 470 480 490 500 / Crea un e j e m p l a r nuevo de <code>E s t u d i a n t e </code> todos l o s a t r i b u t o s nulos ( s i n cadenas ) . / public Estudiante () { nombre = c o r r e o = c u e n t a = "" ; }

Estudiante (1/2)

/ Crea un e j e m p l a r nuevo de <code>E s t u d i a n t e </code >, i n i c i a n d o con l o s a r g u m e n t o s d a d o s . @param nmbre t i p o <code>S t r i n g </code >: p a r a <code>nombre</code >. @param c o r r e o t i p o <code>S t r i n g </code >: p a r a <code>c o r r e o </code >. @param c u e n t a t i p o <code>S t r i n g </code >: p a r a <code>c u e n t a </code >. @param c a r r e r a t i p o <code>i n t </code >: p a r a <code>c a r r e r a </code >. /

195

Datos estructurados
Estudiante (2/2)

Cdigo 5.4 Constructores para la clase Estudiante o


510 520 530 540 550 560 570 580 590 600 610 620 630 640 650 660 670 680 690 700 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 o r r e o , S t r i n g cuenta , i n t c a r r e r a ) { nombre = nmbre == n u l l ? "" : nmbre ; t h i s . c o r r e o = c o r r e o == n u l l ? "" : correo ; t h i s . c u e n t a = c u e n t a == n u l l ? "" : c u e n t a ; i f ( CatalogoCarreras . esCarrera ( carrera )) { this . carrera = carrera ; } // S i no l e ponemos nada queda en 0 }

/ Crea un e j e m p l a r nuevo de <code>E s t u d i a n t e </code >, c r e a n d o una c o p i a d e l que s e p a s a como p a r m e t r o , dando l u g a r d i s t i n t o en a e l heap . @param c o p i a t i p o <code>E s t u d i a n t e </code> . / public Estudiante ( Estudiante copia ) { nombre = c o p i a . nombre ; c a r r e r a = c o p i a . c a r r e r a ; cuenta = copia . cuenta ; correo = copia . correo ; }

En la l nea 400 del listado 5.4 en la pgina opuesta inicializamos a los atributos a con las cadenas vac as. Esto lo hacemos con el n de evitar que al tratar de acceder a alguna de las cadenas el programa aborte porque la referencia sea nula. El nmero de cuenta, por ser entero, se inicia en 0 y la referencia al resto de la u lista se inicia en null, lo que es correcto, pues a un estudiante aislado no le sigue ninguna lista. El constructor con parmetros l a neas 430 a 590 simplemente inicia al objeto con los valores proporcionados. Nuevamente no es necesario asignar un valor a siguiente, pues debe tener el valor de null, asignado por omisin. Unicamente veo ricaremos en el catlogo de carreras que la clave de la carrera exista l a neas 560 y 570. Por ultimo, el constructor que hace un clon del objeto que se le pasa como parmetro l a neas 610 a 700 produce un objeto nuevo al que copia, campo por campo, el objeto pasado como parmetro. a 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.5 en la o o e siguiente pgina. a

5.1 La clase para cada registro (estudiante)

196
Estudiante (1/2)

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


7200 7300 7400 7500 7600 7700 7800 7900 8000 8100 8200 8300 8400 8500 8600 8700 8800 8900 9000 9100 9200 9300 9400 9500 9600 9700 9800 9900 10000 10100 10200 10300 10400 10500 10600 10700 10800 10900 11000 11100 11200 11300 11400 11500 11600 11700 11800 11900 12000

/ Mtodo <code>g e t C u e n t a </code >: o b t i e n e campo c o r r e s p o n d i e n t e . e @ r e t u r n t i p o <code>S t r i n g </code >: campo c o r r e s p o n d i e n t e . / public S t r i n g getCuenta () { return cuenta ; } / Mtodo <code>s e t C u e n t a </code> a c t u a l i z a e l v a l o r e de <code>c u e n t a </code >. @param newCuenta t i p o <code>S t r i n g </code> p a r a nuevo v a l o r . / p u b l i c v o i d s e t C u e n t a ( S t r i n g newCuenta ) { t h i s . c u e n t a = newCuenta == n u l l ? "" : newCuenta ; } / Mtodo <code>g e t C a r r e r a </code> a c c e d e a l a t r i b u t o e <code>c a r r e r a </code >. @ r e t u r n t i p o <code>i n t </code >: v a l o r d e l a t r i b u t o . / public int getCarrera () { return c a r r e r a ; } / Mtodo <code>s e t C a r r e r a </code> a c t u a l i z a e l v a l o r de e <code>c a r r e r a </code >. V e r i f i c a que s e a v l i d o . a @param n e w C a r r e r a t i p o <code>i n t </code> p a r a nuevo valor . / public void s e t C a r r e r a ( i n t newCarrera ) { i f ( CatalogoCarreras . es Car rera ( newCarrera )) { th is . c a r r e r a = newCarrera ; } else { this . carrera = 0; } } / Mtodo <code>getNombre </code> a c c e d e a l a t r i b u t o e <code>nombre</code >. @ r e t u r n t i p o <code>S t r i n g </code >: v a l o r en <code>nombre</code >. / p u b l i c S t r i n g getNombre ( ) { r e t u r n nombre ; }

197

Datos estructurados
Estudiante (2/2)

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


12200 12300 12400 12500 12600 12700 12800 12900 13000 13100 13200 13300 13400 13500 13600 13700 13800 13900 14000 14100 14200 14300 14400 14500 14600 14700 14800 14900 15000 15100 15200 15300 15400 15500 15600 15700 15800 15900 16000 16100 16200 16300 16400 16500 16600 16700 16800 16900 17000

/ Mtodo <code>setNombre </code> a c t u a l i z a e l v a l o r de e <code>nombre</code >. @param newNombre t i p o <code>S t r i n g </code> p a r a nuevo v a l o r . / p u b l i c v o i d setNombre ( S t r i n g newNombre ) { t h i s . nombre = newNombre == n u l l ? "" : newNombre ; } / Mtodo <code>g e t C o r r e o </code> a c c e d e a l a t r i b u t o e <code>c o r r e o </code >. @ r e t u r n t i p o <code>S t r i n g </code >: v a l o r en e l atributo . / public String getCorreo () { return correo ; } / Mtodo <code>s e t C o r r e o </code >: A c t u a l i z a e l campo e <code>c o r r e o </code >. @param newCorreo t i p o <code>S t r i n g </code> p a r a nuevo v a l o r . / p u b l i c v o i d s e t C o r r e o ( S t r i n g newCorreo ) { t h i s . c o r r e o = newCorreo == n u l l ? "" : newCorreo ; } / Mtodo <code>g e t S i g u i e n t e </code> a c c e d e a l a t r i b u t o e <code>s i g u i e n t e </code >. @ r e t u r n t i p o <code>E s t u d i a n t e </code >: r e f e r e n c i a a l s i g u i e n t e en l a l i s t a . / public Estudiante getSiguiente () { return s i g u i e n t e ; } / Mtodo <code>s e t S i g u i e n t e </code >: a c t u a l i z a e l v a l o r de e <code>s i g u i e n t e </code >. @param n e w S i g u i e n t e t i p o <code>E s t u d i a n t e </code> p a r a nuevo v a l o 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 newSiguiente ) { this . s i g u i e n t e = newSiguiente ; }

5.1 La clase para cada registro (estudiante)

198

Noten que en los mtodos en los que se espera una cadena, el mtodo protege e e al atributo para que siempre tenga un valor distinto de null, ya que si le pasan un argumento que sea null lo sustituye por la cadena vac a. El unico mtodo interesante es el que establece el cdigo de la carrera, ya que e o debe vericar que la clave de la carrera sea correcta; si no es as coloca un 0 en , el campo correspondiente. Simplemente le pregunta al catlogo de carreras si la a clave es vlida l a nea 10600 del listado 5.5. Podemos tambin poner mtodos ms generales para los atributos de la clase e e a Estudiante; podemos pedir al mtodo que seleccione un campo a modicar o a e regresar. En el caso del atributo carrera que no es una cadena sino un entero, podemos darle la cadena con el entero y que el mtodo lo interprete. Los podemos e ver en el cdigo 5.6. o

Cdigo 5.6 Mtodos que actualizan y regresan un campo seleccionado o e


17200 17300 17400 17500 17600 17700 17800 17900 18000 18100 18200 18300 18400 18500 18600 18700 18800 18900 19000 19100 19200 19300 19400 19500 19600 19700 19800 19900 20000

Estudiante (1/2)

/ Mtodo <code>daCampo</code >: r e g r e s a e l campo s o l i c i t a d o . e @param c u a l v a l o r de t i p o <code>i n t </code >: s e l e c t o r d e l campo a r e g r e s a r . @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >: e l c o n t e n i d o d e l campo e d i t a d o en una c a d e n a . / p u b l i c S t r i n g daCampo ( i n t c u a l ) { switch ( c u a l ) { case NOMBRE: r e t u r n daNombre ( ) . t r i m ( ) ; case CUENTA : r e t u r n daCuenta ( ) . t r i m ( ) ; case CARRERA : return daCarrera ( ) . trim ( ) ; case CORREO : return daCorreo ( ) . trim ( ) ; default : r e t u r n " N mero de campo inv lido " ; u a } } / Mtodo <code>ponCampo</code >: a c t u a l i z a e l campo s o l i c i t a d o e con l a i n f o r m a c i n p r o p o r c i o n a d a . o @param c u a l t i p o <code>i n t </code> p a r a e l e g i r campo . @param v a l o r t i p o <code>S t r i n g </code> p a r a d a r e l nuevo v a l o r . / p u b l i c v o i d ponCampo ( i n t c u a l , S t r i n g v a l o r ) { i f ( v a l o r == n u l l ) { // Para no m a n e j a r r e f e r e n c i a s n u l a s . v a l o r = "" ; } switch ( c u a l ) { case NOMBRE: setNombre ( v a l o r ) ; break ;

199

Datos estructurados
Estudiante (2/2)

Cdigo 5.6 Mtodos que actualizan y regresan un campo seleccionado o e


20100 20200 20300 20400 20500 20600 20700 20800 20900 21000 21100 21200 21300 21400 21500 21600

case CUENTA : setCuenta ( v a l o r ) ; break ; case CARRERA : int intValor = Integer . parseInt ( valor ); i f ( CatalogoCarreras . esCarrera ( intValor )) { setCarrera ( intValor ); } else { setCarrera (0); } break ; case CORREO : setCorreo ( valor ); break ; default : System . o u t . p r i n t l n ( " N mero de campo inv lido " + c u a l ) ; u a } }

Dado que desearemos armar tablas (o listados) con los datos registrados para cada estudiante y que cada registro tiene datos de tamaos distintos a los otros n registros, vamos a tener mtodos que editen cada uno de los campos, con el prejo e da para distinguirlos de los que simplemente entregan el valor registrado en el campo. Para ello, requerimos un mtodo que rellene el campo, ya sea por la e izquierda o por la derecha con un carcter especicado. El algoritmo se muestra a en la gura 5.2 y el cdigo en el listado 5.7, Lo agregamos a una clase de manejo o de cadenas en nuestro paquete utiles que se llama Cadenas.

Figura 5.2

Algoritmo para rellenar un campo con un cierto carcter a " $ 'Inicializacin i tamao de la n ' o ' ' cadena ' ' ' " ' ' ' 'i tamao Entrega subcadena con ' n ' ' tamao caracteres n ' ' ' $ $ ' " $ ' ' ' ' & 'izquierda campo carcter a ' ' ' Rellena ' ' Agrega ' agrega el ' ' & ' ' + campo ' un campo ' & carcter 'carcter de & a ' " a 'i tamao ' n ' ' relleno ' ' 'derecha campo campo n ' (i tamao ' ' % ' del campo) ' ' ' ' ' + carcter a ' ' ' ' ' ' ' ' ' % %Incrementa i ' ' ' ' ' ' ! ' ' %
Final Entrega el campo rellenado

5.1 La clase para cada registro (estudiante)

200
utiles.Cadenas

Cdigo 5.7 Mtodo que rellena un campo con un carcter dado o e a

100 package u t i l e s ; 200 300 / 400 C l a s e <code>Cadenas </code> que r e u n e v a r i o s m todos e s t t i c o s p a r a e a 500 m a n i p u l a c i n de c a d e n a s . o 600 @ a u t h o r <a h r e f = m a i l t o : e l i s a @ v i s o > E l i s a V i s o G u r o v i c h </a> 700 @ v e r s i o n 1 . 0 800 / 900 p u b l i c c l a s s Cadenas {

...
3300 / 3400 <code>r e l l e n a C a m p o </code> c o m p l e t a con e l c a r c t e r i n d i c a d o l a a 3500 c a d e n a s o l i c i t a d a a l tama o i n d i c a d o . n 3600 3700 @param c a d e n a t i p o <code>S t r i n g </code >: c a d e n a a r e l l e n a r . 3800 @param tamanho un <code>i n t </code >: tama o a c o m p l e t a r . n 3900 @param c a r t i p o <code>c h a r </code >: sm b o l o p a r a r e l l e n a r . 4000 @param p o s i c i o n t i p o <code>c h a r </code >: i z q u i e r d a o d e r e c h a . 4100 @ r e t u r n t i p o <code>S t r i n g </code >: c a d e n a r e l l e n a d a . 4200 / 4300 p u b l i c s t a t i c S t r i n g r e l l e n a C a m p o ( S t r i n g cadena , i n t tamanho , 4400 char c a r , char p o s i c i o n ) { 4500 i f ( c a d e n a == n u l l ) { // e s t v a ca a 4600 c a d e n a = "" ; 4700 } 4800 i n t i = cadena . l e n g t h ( ) ; 4900 i f ( i > tamanho ) { 5000 c a d e n a = c a d e n a . s u b s t r i n g ( 0 , tamanho ) ; 5100 return cadena ; 5200 } else { 5300 i f ( i == tamanho ) { 5400 return cadena ; 5500 } 5600 } 5700 // Cadena e s < tamanho 5800 w h i l e ( i < tamanho ) { 5900 i f ( p o s i c i o n == i ) { 6000 cadena = c a r + cadena ; 6100 } else { 6200 cadena = cadena + c a r ; 6300 } 6400 i ++; 6500 } 6600 return cadena ; 6700 }

201

Datos estructurados

Ahora s es fcil implementar los mtodos que regresan los atributos en cadenas a e rellenadas para que queden alineadas en una tabla. Estos se encuentran en el listado 5.8.

Cdigo 5.8 Regresa los campos rellenados o


21800 21900 22000 22100 22200 22300 22400 22500 22600 22700 22800 22900 23000 23100 23200 23300 23400 23500 23600 23700 23800 23900 24000 24100 24200 24300 24400 24500 24600 24700 24800 24900 25000 25100 25200 25300

Estudiante

/ <code>daNombre</code> r e g r e s a una c a d e n a de l o n g i t u d <code>TAM NMBRE</code> r e l l e n a n d o con b l a n c o s a l a d e r e c h a . @ r e t u r n a <code>S t r i n g </code> v a l u e / p u b l i c S t r i n g daNombre ( ) { r e t u r n Cadenas . r e l l e n a C a m p o ( nombre , TAM NMBRE, , d ) ; } / <code>daCuenta </code> arma una c a d e n a de tama o f i j o n r e l l e n a n d o con c e r o s a l a i z q u i e r d a . @ r e t u r n t i p o <code>S t r i n g </code> / p u b l i c S t r i n g daCuenta ( ) { r e t u r n Cadenas . r e l l e n a C a m p o ( c u e n t a , TAM CTA, 0 , i ) ; } / <code>d a C a r r e r a </code> r e g r e s a e l nombre de l a c a r r e r a con un tama o f i j o , r e l l e n a d o de b l a n c o s a l a d e r e c h a . n @ r e t u r n v a l o r t i p o <code>S t r i n g </code> / public String daCarrera () { S t r i n g nCarre = CatalogoCarreras . daCarrera ( c a r r e r a ) ; return nCarre ; } / <code>d a C o r r e o </code> r e g r e s a una c a d e n a de tama o f i j o con e l n correo del estudiante s ol i ci t ad o . @ r e t u r n v a l o r t i p o <code>S t r i n g </code> / public S t r i n g daCorreo () { r e t u r n Cadenas . r e l l e n a C a m p o ( c o r r e o , TAM CORREO, , d ) ; }

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.9 en la siguiente pgina. o a

5.1 La clase para cada registro (estudiante)

202
Estudiante

Cdigo 5.9 Mtodos que arman y actualizan registro completo o e


25500 25600 25700 25800 25900 26000 26100 26200 26300 26400 26500 26600 26700 26800 26900 27000 27100 27200 27300 27400 27500 27600 27700 27800 27900 28000 28100 28200 28300 28400 28500 28600

/ Mtodo <code>a r m a R e g i s t r o </code >, r e g r e s a una c a d e n a con e l e contenido del r e g i s t r o editado . @ r e t u r n t i p o <code>S t r i n g </code> c o r r e s p o n d i e n t e a l registro . / public String armaRegistro () { r e t u r n daCuenta ( ) + " " + daCarrera () + " " + daNombre ( ) + " " + daCorreo ( ) ; } / Mtodo <code>p o n R e g i s t r o </code> a c t u a l i z a a t o d o e l r e g i s t r o . e @param nmbre t i p o <code>S t r i n g </code> p a r a s u s t i t u i r a l nombre . @param c a r r e r a t i p o <code>i n t </code> p a r a s u s t i t u i r la carrera . @param c u e n t a t i p o <code>S t r i n g </code> p a r a s u s t i t u i r l a cuenta . @param c o r r e o t i p o <code>S t r i n g </code> p a r a s u s t i t u i r al correo . / p u b l i c v o i d p o n R e g i s t r o ( S t r i n g nmbre , i n t c a r r e r a , S t r i n g cuenta , S t r i n g c o r r e o ) { nombre = nmbre == n u l l ? "" : nmbre . t r i m ( ) ; t h i s . c u e n t a = c u e n t a == n u l l ? "" : c u e n t a . t r i m ( ) ; this . carrera = CatalogoCarreras . esCarrera ( carrera ) ? carrera : 0; t h i s . c o r r e o = c o r r e o == n u l l ? "" : c o r r e o . t r i m ( ) ; }

Elegimos darle a estos cuatro ultimos mtodos nombres que no empiezan con e get o set, ya que Java reserva estos prejos para mtodos que trabajan con cada e campo por separado y lo regresan tal cual. 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 Queremos ver si nuestros mtodos funcionan bien, por lo que agregamos teme poralmente un mtodo main que pruebe los distintos mtodos. Para ello usaremos e e constantes y no leeremos datos de la consola. El mtodo main listado 5.10, y los e resultados obtenidos de la ejecucin se muestran a continuacin gura 5.3 en la o o pgina 204. a

203

Datos estructurados
Estudiante (1/3)

Cdigo 5.10 Prueba de la clase Estudiante o


28800 28900 29000 29100 29200 29300 29400 29500 29600 29700 29800 29900 30000 30100 30200 30300 30400 30500 30600 30700 30800 30900 31000 31100 31200 31300 31400 31500 31600 31700 31800 31900 32000 32100 32200 32300 32400 32500 32600 32700 32800 32900 33000 33100 33200 33300 33400 33500

/ Prueba de l o s m todos de l a c l a s e / 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 ) { CatalogoCarreras . muestraCatalogo ( ) ; System . o u t . p r i n t l n ( " Probando a la clase Estudiante ..." ) ; S c a n n e r c o n s = new S c a n n e r ( System . i n ) ; E s t u d i a n t e yo = new E s t u d i a n t e ( ) ; System . o u t . p r i n t l n ( "El registro yo contiene a:\n" + " Nombre :\t*" + yo . daNombre ( ) + "*\n" + " Carrera :\t*" + yo . d a C a r r e r a ( ) + "*\n" + " Cuenta :\t*" + yo . daCuenta ( ) + "*\n" + " Correo :\t*" + yo . d a C o r r e o ( ) +"*" ) ; yo . setNombre ( " Elisa Viso" ) ; yo . s e t C a r r e r a ( 2 0 1 ) ; yo . s e t C u e n t a ( " 067302682 " ) ; yo . s e t C o r r e o ( " elisa@ciencias " ) ; System . o u t . p r i n t l n ( " Despu s de la asignaci n , el registro yo " e o + " contiene a:\n" + " Nombre :\t*" + yo . daNombre ( ) + "*\n" + " Carrera :\t*" + yo . d a C a r r e r a ( ) + "*\n" + " Cuenta :\t*" + yo . daCuenta ( ) + "*\n" + " Correo :\t*" + yo . d a C o r r e o ( ) +"*\n" + "y el registro completo es :\n*" + yo . a r m a R e g i s t r o ( ) + "*" ) ; E s t u d i a n t e t u = new E s t u d i a n t e ( " Perico de los palotes " , " periquito@unam " , " 20109090909090 " , yo . g e t C a r r e r a ( ) ) ; System . o u t . p r i n t l n ( "El registro tu contiene a:\n" + " Nombre :\t*" + t u . daNombre ( ) + "*\n" + " Carrera :\t*" + t u . d a C a r r e r a ( ) + "*\n" + " Cuenta :\t*" + t u . daCuenta ( ) + "*\n" + " Correo :\t*" + t u . d a C o r r e o ( ) + "*\n" + "y el registro completo es :\n*" + t u . a r m a R e g i s t r o ( ) + "*" ) ; t u . p o n R e g i s t r o ( t u . getNombre ( ) , 1 0 1 , yo . g e t C u e n t a ( ) , tu . getCorreo ( ) ) ; t u . s e t C u e n t a ( " 35000 " ) ; System . o u t . p r i n t l n ( "yo. getCuenta ()="+yo . g e t C u e n t a ( ) ) ; System . o u t . p r i n t l n ( " Despu s de la asignaci n , el registro tu " e o + " contiene a:\n" + " Nombre :\t*" + t u . daNombre ( ) + "*\n" + " Carrera :\t*" + t u . d a C a r r e r a ( ) + "*\n" + " Cuenta :\t*" + t u . daCuenta ( ) + "*\n" + " Correo :\t*" + t u . d a C o r r e o ( ) + "*\n" + "y el registro completo es :\n*" + t u . a r m a R e g i s t r o ( ) + "*" ) ;

5.1 La clase para cada registro (estudiante)

204
Estudiante (3/3)

Cdigo 5.10 Prueba de la clase Estudiante o


33600 33700 33800 33900 34000 34100 34200 34300 34400 34500 34600 34700 34800 34900 35000 35100 35200 35300 35400

/ Armamos una peque n a l i s t a a p i e / E s t u d i a n t e m i L i s t a = tu ; t u . s e t S i g u i e n t e ( yo ) ; t u . g e t S i g u i e n t e ( ) . s e t S i g u i e n t e ( new E s t u d i a n t e ( " Papacito " , " yo@ciencias " , "0347" , 122)); tu . g e t S i g u i e n t e ( ) . g e t S i g u i e n t e ( ) . s e t S i g u i e n t e ( new E s t u d i a n t e ( " Mamacita " , " mami@ciencias " , "0348" , 1 2 7 ) ) ; / Veamos a h o r a e l c o n t e n i d o de l a l i s t a / System . o u t . p r i n t l n ( "\n **************************\ n" + "El contenido de la lista es :\n" ) ; Estudiante actual = miLista ; w h i l e ( a c t u a l != n u l l ) { System . o u t . p r i n t l n ( a c t u a l . a r m a R e g i s t r o ( ) ) ; actual = actual . getSiguiente (); } }

Figura 5.3
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

Ejecucin de prueba de la clase Estudiante o


C a t a l o g o de c a r r e r a s de l a F a c u l t a d de C i e n c i a s =============================================== 101 Actuaria 104 106 122 127 201 217 C i e n c i a s de l a Computaci n o F s i c a M a t e m t i c a s a C i e n c i a s de l a T i e r r a B i o l o ga Manejo S u s t e n t a b l e de Zonas C o s t e r a

(1/2)

========================================================= Probando a l a c l a s e E s t u d i a n t e . . . E l r e g i s t r o yo c o n t i e n e a : Nombre : C a r r e r a : Cdigo i n v a l i d o o Cuenta : 000000000 Correo : Despu s de l a a s i g n a c i n , e l r e g i s t r o yo c o n t i e n e a : e o Nombre : E l i s a V i s o C a r r e r a : B i o l o ga

205

Datos estructurados

Figura 5.3
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

Ejecucin de prueba de la clase Estudiante o


Cuenta : 067302682 Correo : e l i s a @ c i e n c i a s y e l r e g i s t r o completo es : 067302682 B i o l o ga

(2/2)

E l i s a Viso elisa@ciencias E l r e g i s t r o tu c o n t i e n e a : Nombre : P e r i c o de l o s p a l o t e s C a r r e r a : B i o l o ga Cuenta : 201090909 Correo : periquito@unam y e l r e g i s t r o completo es : 201090909 B i o l o ga P e r i c o de los palotes periquito@unam yo . g e t C u e n t a ()=067302682 Despu s de l a a s i g n a c i n , e l r e g i s t r o tu c o n t i e n e a : e o Nombre : P e r i c o de l o s p a l o t e s Carrera : Actuaria Cuenta : 000035000 Correo : periquito@unam y e l r e g i s t r o completo es : 000035000 A c t u a r i a P e r i c o de los palotes periquito@unam E l c o n t e n i d o de l a l i s t a e s : 000035000 A c t u a r i a los palotes 067302682 B i o l o ga 000000347 000000348 B i o l o ga yo@ciencias C i e n c i a s de l a T i e r r a mami@ciencias P e r i c o de periquito@unam E l i s a Viso elisa@ciencias Papacito Mamacita

En la gura anterior la numeracin de las l o neas no forma parte de lo que produce la aplicacin, sino que est puesta para facilitar la discusin. Los s o a o mbolos y marcan l neas que se desbordan en la pantalla. Podemos seguir paso a paso la creacin de objetos, recordando que los identio cadores de objetos contienen, en ejecucin, referencias (direcciones) al lugar en o el heap en el que quedan localizados los objetos, mientras que las variables que se reeren a valores primitivos (int, long, byte, short, oat, double, boolean y char) ocupan lugar directamente en la memoria de ejecucin trabajamos directamente o con el contenido de la memoria asignada. Despus de ejecutar la l e nea 29400 la memoria queda como se muestra en la gura 5.4 en la siguiente pgina. a

5.1 La clase para cada registro (estudiante)

206

Figura 5.4

Memoria hasta la l nea 29400 Heap


nombre cuenta carrera correo
(8126) (4086) (84322)

Variables
yo
(4086)

signte

(3010)

[]
(cadena vac a) (cadena vac a) (cadena vac a)
(3010) (84322) (8126)

En esta gura, los enteros entre parntesis corresponden a direcciones f e sicas en la memoria: en la variable tipo referencia yo se almacena la direccin en memoria o donde empieza el registro al que apunta, o bien el valor null si es que no tiene espacio asignado en el heap. Cabe aclarar que el programador nunca tiene acceso a las direcciones absolutas de memoria, sino unicamente a travs de las variables e se ilustra slo para que quede clara la asignacin de espacio en el heap. o o En las l neas 30000 a 30300 se modica el contenido de los campos, quedando la memoria como se muestra en la gura 5.5.

Figura 5.5

Memoria hasta la l nea 30300 Heap


nombre cuenta carrera correo
(32100) (40800) (4086)

Variables
yo
(4086)

signte

201

(3198)

[] elisa@ciencias
(3198) (40800) (32100)

067302682 Elisa Viso

En las l neas 31300 a 31600 tenemos la declaracin y construccin de otro o o objeto de la clase Estudiante, quedando la memoria como se muestra en la gura 5.6 en la pgina opuesta. a

207

Datos estructurados

Figura 5.6

Memoria hasta la l nea 31600 Variables


(4086)

Heap
nombre cuenta carrera correo
(40800) (32100)

signte

201

(3198)

[]
(3198) (32100) (40800)

yo

(4086)

elisa@ciencias 067302682 Elisa Viso

nombre cuenta carrera correo


(7610) (75300) (3280)

signte

201

(5432)

[]
(5432) (3280) (7610)

tu

(75300)

periquito@unam 20109090909090 Perico de los palotes

En las l neas 32400 a 32600 se modica el contenido (el estado) del objeto referido por tu, quedando la memoria como se muestra en la gura 5.7. Hay que notar que las direcciones de memoria en el heap para las cadenas contenidas en el objeto referido por tu no coinciden con las cadenas referidas por el objeto yo aunque la cadena sea la misma: se cre una cadena nueva y se le asign una o o nueva direccin en el heap. Esto sucede con las cadenas, ya que no pueden ser o modicadas (son objetos constantes).

Figura 5.7

Memoria hasta la l nea 32600 Heap Variables


nombre cuenta carrera correo
(40800) (32100) (4086)

signte

201

(3198)

[]
(3198) (32100) (40800)

yo

(4086)

elisa@ciencias 067302682 Elisa Viso

nombre cuenta carrera correo


(2500) (75300) (8200)

signte

101

(5470)

[]
(5470) (8200) (2500)

tu

(75300)

periquito@unam 067302682 Perico de los palotes

5.2 La lista de registros

208

A partir de la l nea 33700 probamos la construccin de listas ligadas. Despus o e de la ejecucin de la l o nea 33800, la memoria se encuentra como se muestra en la gura 5.8.

Figura 5.8

Memoria hasta la l nea 33800 Variables


(4086)

Heap
nombre cuenta carrera correo
(40800) (32100)

signte

201

(3198)

[]
(3198) (32100) (40800)

yo

(4086)

elisa@ciencias 067302682 Elisa Viso

nombre cuenta carrera correo


(2500) (75300) (8200)

signte
(4086)

101

(5470)

tu

(75300) (5470) (8200) (2500)

periquito@unam 067302682 Perico de los palotes

(75300)

miLista

En las l neas 33900 a 34500 se crean dos nuevos registros y se enlazan a la lista referida por miLista que es la misma que empieza en tu . El estado de la memoria se muestra en la gura 5.9 en la pgina opuesta. a Una vez comprobado que la clase trabaja bien eliminamos de nuestra clase al mtodo main. e

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, que deber encontrarse nuevamente en una a clase dedicada al grupo.

209

Datos estructurados

Figura 5.9

Memoria hasta la l nea 34500 Variables Heap


nombre cuenta carrera correo
(8788) (4444) (7676) (9876) (8788) (9876)

signte

127

(7676)

[] mami@ciencias 0348 Mamacita

nombre cuenta carrera correo


(8768) (6688) (6464)

signte
(4444) (5432) (6464) (8768)

122

(5432)

yo@ciencias 0347 Papacito

nombre cuenta carrera correo


(40800) (32100) (4086)

signte
(6688)

201

(3198)

yo

(4086) (3198) (32100) (40800)

elisa@ciencias 067302682 Elisa Viso

nombre cuenta carrera correo


(2500) (75300) (8200)

signte
(4086)

101

(5470)

tu

(75300) (5470) (8200) (2500)

periquito@unam 067302682 Perico de los palotes

(75300)

miLista

Antes de entrar de lleno a la implementacin de la lista de registros es conveo niente regresar al contrato que queremos establecer con la lista de los cursos. La tarjeta de responsabilidades se parece mucho a la que hicimos en el cap tulo anterior y debe tener los siguientes servicios, que son prcticamente los mismos que a en el caso de la base de datos guardada en una cadena ver gura 5.10 excepto que cambiamos algunos nombres de mtodos y el tipo del valor que regresan. e

5.2 La lista de registros

210

Figura 5.10

Tarjeta de responsabilidades para Curso.

Clase: Curso
Constructores

Responsabilidades
A partir de una base de datos inicial y a partir de cero. Mtodos relativos al estudiante e Regresa el nombre completo del estudiante solicitado Regresa la carrera del estudiante solicitado Regresa la clave de acceso del estudiante solicitado Regresa el nmero de cuenta del estudiante solicitado u Agrega a un estudiante, proporcionando los datos correspondientes. Regresa el registro bonito para imprimir

P u b l i

daNombre daCarrera daCorreo daCuenta agregaEstudiante armaRegistro localizaAlumno agregaAlumno eliminaAlumno dameLista dameCurso losQueCazanCon

Mtodos relativos a la base de datos: e Localiza al alumno en la base de datos c Agrega a un estudiante a la lista o Elimina al estudiante de la lista Lista todos los estudiantes del curso Regresa el listado del curso Lista a los estudiantes que cazan con algn criterio u espec co Con estas responsabilidades procedemos a codicar la interfaz para este modelo de base de datos en el listado 5.11.Ponemos el encabezado anterior (comentado) para los casos en que sustituimos un entero por una referencia.

Cdigo 5.11 Interfaz para los cursos implementados con listas o

ServiciosCursoLista (1/4)

1 package C o n s u l t a s L i s t a s ; 2 / 3 I n t e r f a c e S e r v i c i o s C u r s o L i s t a : l i s t a l a s r e s p o n s a b i l i d a d e s de 4 una b a s e de d a t o s p a r a cada c u r s o en l a F a c u l t a d de C i e n c i a s . 5 Cre ad a : A b r i l 2 0 1 0 . 6 @ a u t h o r <a h r e f = m a i l t o : e l i s a @ t u r i n g > E l i s a V i s o </a> 7 @version 2.0 8 / 9 public interface S e r vi c i os Cu r s oL i sta { 10 / 11 Mtodo <code>getGrupo </code >: r e g r e s a e l campo con e l n mero e u 12 de g r u p o . 13 @ r e t u r n t i p o <code>S t r i n g </code >: e l a t r i b u t o d e l g r u p o . 14 / 15 S t r i n g getGrupo ( ) ;

211

Datos estructurados
ServiciosCursoLista (2/4)

Cdigo 5.11 Interfaz para los cursos implementados con listas o


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 57 58 59

/ Mtodo <code>daNombre</code >: r e g r e s a e l nombre d e l e estudiante solicitado . @param c u a l t i p o <code>E s t u d i a n t e </code >: l a r e f e r e n c i a a l e s t u d i a n t e que s e d e s e a . @ r e t u r n t i p o <code>S t r i n g </code >: e l nombre d e l e s t u d i a n t e solicitado . / // S t r i n g daNombre ( i n t c u a l ) ; S t r i n g daNombre ( E s t u d i a n t e c u a l ) ; / Mtodo <code>d a C a r r e r a </code >: r e g r e s a e l nombre de l a c a r r e r a e del estudiante solicitado . @param c u a l t i p o <code>E s t u d i a n t e </code >: l a r e f e r e n c i a a l e s t u d i a n t e que s e d e s e a . @ r e t u r n t i p o <code>S t r i n g </code >: e l nombre de l a c a r r e r a d e l estudiante solicitado . / // S t r i n g d a C a r r e r a ( i n t c u a l ) ; String daCarrera ( Estudiante cual ) ; / Mtodo <code>d a C o r r e o </code >: : r e g r e s a e l c o r r e o d e l e estudiante solicitado . @param c u a l t i p o <code>E s t u d i a n t e </code >: l a r e f e r e n c i a a l e s t u d i a n t e que s e d e s e a . @ r e t u r n t i p o <code>S t r i n g </code >: e l c o r r e o d e l e s t u d i a n t e solicitado . / // S t r i n g d a C o r r e o ( i n t c u a l ) ; S t r i n g daCorreo ( Estudiante cual ) ; / Mtodo <code>daCuenta </code >: : r e g r e s a e l n mero de c u e n t a e u del estudiante solicitado . @param c u a l t i p o <code>E s t u d i a n t e </code >: l a r e f e r e n c i a a l e s t u d i a n t e que s e d e s e a . @ r e t u r n t i p o <code>S t r i n g </code >: e l n mero de c u e n t a d e l u estudiante solicitado . / // S t r i n g daCuenta ( i n t c u a l ) ; S t r i n g daCuenta ( E s t u d i a n t e c u a l ) ;

5.2 La lista de registros

212
ServiciosCursoLista (3/4)

Cdigo 5.11 Interfaz para los cursos implementados con listas o


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 104 105 106 107 108

/ Mtodo <code>l o c a l i z a A l u m n o </code >: l o c a l i z a a l alumno que e t i e n e a l a s u b c a d e n a d e n t r o de a l g u n o de l o s campos . @param c a d e n a t i p o <code>S t r i n g </code >: l a s u b c a d e n a a b u s c a r . @param campo t i p o <code>i n t </code >: E l campo d e n t r o d e l r e g i s t r o donde s e va a b u s c a r . @param d e s d e t i p o <code>E s t u d i a n t e </code >: b u s c a r a p a r t i r de e s t e r e g i s t r o en l a l i s t a . @ r e t u r n t i p o <code>E s t u d i a n t e </code >: l a r e f e r e n c i a a l r e g i s t r o donde s e l o c a l i z l a s u b c a d e n a en e l campo o solicitado . / // E s t u d i a n t e l o c a l i z a A l u m n o ( S t r i n g cadena , i n t campo , // i n t desde ) ; E s t u d i a n t e l o c a l i z a A l u m n o ( S t r i n g cadena , i n t campo , Estudiante desde ) ; / Mtodo <code>l o c a l i z a A l u m n o </code> que t i e n e a l a s u b c a d e n a e d e n t r o de a l g u n o de l o s campos . b u s c a a p a r t i r de un determinado r e g i s t r o . @param c a d e n a t i p o <code>S t r i n g </code >: l a s u b c a d e n a a b u s c a r . @param campo t i p o <code>i n t </code >: E l campo d e n t r o d e l r e g i s t r o donde s e va a b u s c a r . @ r e t u r n t i p o <code>E s t u d i a n t e </code >: l a r e f e r e n c i a a l r e g i s t r o donde s e l o c a l i z l a s u b c a d e n a en e l campo o solicitado . / E s t u d i a n t e l o c a l i z a A l u m n o ( S t r i n g cadena , i n t campo ) ; / Mtodo <code>agregaAlumno </code >: Agrega a un alumno a l e p r i n c i p i o de l a b a s e de d a t o s . @param n t i p o <code>S t r i n g </code >: nombre d e l alumno . @param ca t i p o <code>i n t </code >: C l a v e de l a c a r r e r a . @param c t a t i p o <code>S t r i n g </code >: n mero de c u e n t a . u @param co t i p o <code>S t r i n g </code >: c o r r e o . / v o i d agregaAlumno ( S t r i n g n , i n t ca , S t r i n g c t a , S t r i n g co ) ; / Mtodo <code>e l i m i n a A l u m n o </code >: E l i m i n a a l alumno i n d i c a d o e de l a b a s e de d a t o s . A v i s a s i l o pudo e l i m i n a r . @param c u a l t i p o <code>E s t u d i a n t e </code >: l a r e f e r e n c i a d e l alumno a e l i m i n a r . / // v o i d e l i m i n a A l u m n o ( i n t c u a l ) ; boolean e l i m i n a A l u m n o ( E s t u d i a n t e c u a l ) ;

213

Datos estructurados
ServiciosCursoLista (4/4)

Cdigo 5.11 Interfaz para los cursos implementados con listas o

110 / 111 Mtodo <code>a r m a R e g i s t r o </code >: Arma l a c a d e n a p a r a m o s t r a r e 112 el registro del estudiante indicado . 113 @param c u a l t i p o <code>E s t u d i a n t e </code >: E l e s t u d i a n t e 114 solicitado . 115 @ r e t u r n t i p o <code>S t r i n g </code >: La c a d e n a con e l r e g i s t r o 116 editado . 117 / 118 // S t r i n g a r m a R e g i s t r o ( i n t c u a l ) ; 119 String armaRegistro ( Estudiante cual ) ; 120 121 / 122 Mtodo <code>d a m e L i s t a </code >: L i s t a c o m p l e t a e d i t a d a como e 123 tabla . 124 @ r e t u r n t i p o <code>S t r i n g </code >: l a l i s t a e d i t a d a . 125 / 126 S t r i n g dameLista ( ) ; 127 / 128 129 Mtodo <code>dameCurso </code >: L i s t a c o m p l e t o e l c u r s o . e 130 @ r e t u r n t i p o <code>S t r i n g </code >: La c a d e n a con e l c u r s o 131 editado . 132 / 133 S t r i n g dameCurso ( ) ; 134 135 / 136 m todo <code>losQueCazanCon </code >: Arma una c a d e n a con l o s e 137 r e g i s t r o s de l o s e s t u d i a n t e s que cumplen con e l c r i t e r i o 138 solicitado . 139 @param s u b c a d t i p o <code>S t r i n g </code >: Subcadena a b u s c a r . 140 @param campo t i p o <code>i n t </code >: campo donde b u s c a r . 141 @ r e t u r n t i p o <code>S t r i n g </code >: Cadena con l a l i s t a de 142 registros solicitados . 143 / 144 S t r i n g losQueCazanCon ( S t r i n g subcad , i n t campo ) ; 145 } // S e r v i c i o s C u r s o L i s t a

La diferencia fundamental entre esta interfaz y la que dimos en el cap tulo anterior es que ahora identicamos a un estudiante por su referencia (direccin en o el heap) y ya no por la posicin relativa que ocupa en la lista. o Para toda lista requerimos, como mencionamos en relacin con la gura 5.1 o en la pgina 187, 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 e

5.2 La lista de registros

214

lo que tenemos que tener una cuerda (cadena, referencia, apuntador) desde el barco hacia el primer objeto, de ste al segundo y as sucesivamente. De esa e manera, si jalamos la cuerda podemos tener acceso a todos los elementos que conforman la lista. Jalar la cuerda quiere decir, en este contexto, ir tomando referencia por referencia, cada una pegada a la siguiente, como si se tratara de un collar de cuentas. Al igual que en nuestra implementacin anterior usaremos o un identicador lista que nos indique el primer elemento de la lista y consiste de la referencia (direccin en el heap) del primer elemento. Por supuesto que o esta lista estar vac la variable tendr un valor null en tanto no le agreguemos a a a ningn objeto del tipo Estudiante. Como las listas ligadas son estructuras de datos u de acceso secuencial resulta muy costoso agregar estudiantes al nal de la lista, pues tenemos que recorrer la lista para encontrar el nal. Agregaremos entonces una referencia al ultimo de la lista para facilitar algunas operaciones sobre la misma. Agregaremos tambin constantes simblicas que identiquen el atributo e o que deseamos trabajar. Por lo tanto, las unicas diferencias entre las declaraciones de nuestra implementacin anterior y sta es el tipo de la lista y la referencia al o e ultimo de la lista en el caso de las cadenas no era necesario pues ten amos la funcin length() quedando las primeras l o neas de la clase como se puede apreciar en el listado 5.12.

Cdigo 5.12 Atributos de la clase CursoListas o


100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100

CursoListas

package C o n s u l t a s L i s t a s ; import u t i l e s . Cadenas ; import u t i l e s . C a t a l o g o C a r r e r a s ; / C l a s e <code>C u r s o L i s t a s </code> maneja l a l i s t a de un g r u p o . @ a u t h o r <a h r e f = m a i l t o : e l i s a @ v i s o > E l i s a V i s o G u r o v i c h </a> @version 1.2 / p u b l i c c l a s s C u r s o L i s t a s implements S e r v i c i o s C u r s o L i s t a { public static f i n a l int TAM GPO = 4 , // Nm p o s i c i o n e s en e l g r u p o u NOMBRE = E s t u d i a n t e .NOMBRE, // I d d e l campo p a r a nombre CARRERA = E s t u d i a n t e . CARRERA, // I d d e l campo p a r a c a r r e r a CORREO = E s t u d i a n t e . CORREO, // I d d e l campo p a r a c o r r e o CUENTA = E s t u d i a n t e . CUENTA ; // I d d e l campo p a r a numero private S t r i n g grupo ; // Guarda n mero de g r u p o como c a d e n a u p r i v a t e E s t u d i a n t e l i s t a ; // R e f e r e n c i a a l p r i n c i p i o de l a l i s t a // de e s t u d i a n t e s i n s c r i t o s en e l g r u p o p r i v a t e E s t u d i a n t e u l t i m o ; // R e f e r e n c i a a l u l t i m o de l a l i s t a p r i v a t e i n t numRegs = 1; // N mero de r e g i s t r o s en l a l i s t a ; u // 1 i n d i c a que no ha s i d o i n i c i a l i z a d o

215

Datos estructurados

En el caso de esta clase es imprescindible que registremos el nmero de registros u que contiene la lista en un momento dado, pues el no hacerlo implica que cada vez que requiramos de este dato nos veremos forzados a recorrer la lista contndolos. a Antes de seguir con la implementacin de la clase, hagamos un parntesis o e para ver la forma general que tenemos de recorrer una lista. Si pensamos en una lista secuencial, un elemento despus del otro, el diagrama de Warnier que da el e algoritmo general para procesar listas se encuentra en la gura 5.11.

Figura 5.11

Algoritmo para procesar listas secuenciales $ $ ' &Colocarse al principio ' ' 'Inicializacin de la lista ' o ' ' % ' ' Inicializar acumuladores ' ' & # Proceso de lista secuencial ' Procesar registro Procesar registro actual ' (mientras haya o ' ' no se encuentre) Pasar al siguiente ' ' ' ' ' ! ' ' %Final Entregar los resultados
En la condicin de iteracin (mientras haya o no se encuentre) estamos ino o cluyendo tambin el caso de buscar elemento particular de la lista; si tenemos esta e situacin, en cuanto encontremos al elemento buscado suspenderemos el recorrido o de la lista. Regresamos a la implementacin de la clase donde, al igual que con la clase o Curso, tenemos dos constructores, uno que unicamente coloca el nmero del gru u po, y otro que inicializa la lista y coloca el nmero de grupo. Tambin tenemos u e un tercer constructor que copia (clona) el registro (no las referencias) creando los campos en otra posicin del heap. Debemos recordar que lo primero que hace todo o constructor es poner los valores de los atributos que corresponden a referencias en null y los que corresponden a valores primitivos en 0, a menos que el atributo tenga un enunciado de inicializacin. Cuando se invoca al constructor que crea un o curso con una lista vac de estudiantes tendremos que asignar 0 a numRegs, miena tras que si el constructor trabaja a partir de una lista inicial deberemos contar el nmero de registros en esa lista inicial. Modicamos ligeramente el diagrama de u Warnier del proceso de una lista secuencial para que cuente los registros presentes en la lista; extrayndolo del algoritmo general dado tenemos lo siguiente: El e unico acumulador que vamos a usar es el que cuenta los registros (contador); el proceso del registro implica simplemente contarlo; entregar los resultados es dar el valor nal del contador. El diagrama de Warnier que resulta se puede ver en la gura 5.12, que se encuentra a continuacin. o

5.2 La lista de registros

216

Figura 5.12

Cuenta de los registros de una lista $ ! $ ' 'Colocarte al ini- actual lista ' & ' ' 'Inicializacin cio de la lista ' o ' ! ' ' ' %Inicia contador cuantos 0 ' ' ' ' ' $ ! & 'Incrementa Cuenta registros ' cuantos & en una lista ' Cuenta el contador ' registro actual ' ! ' '(mientras haya) ' ' 'Pasa al siguiente actual toma el siguiente ' % ' ' ' ' ' ! ' 'Final %
Entrega el contador La codicacin del mtodo que cuenta los registros presentes en la lista de o e estudiantes se muestra a continuacin en el listado 5.13. El mtodo es privado o e porque la base de datos es la que decide cundo pedir que se cuenten los registros a y cundo no. a

Cdigo 5.13 Mtodo que cuenta los registros o e


230 240 250 260 270 280 290 300 310 320 330 340 350 360 370 380 390 400 410

CursoListas

/ Mtodo <code>c u e n t a R e g s </code >: c u e n t a e l n mero de r e g i s t r o s e u p r e s e n t e s en l a l i s t a y a c t u a l i z a l a r e f e r e n c i a a l u l t i m o . @ r e t u r n t i p o <code>i n t </code >: n mero de r e g i s t r o s en l a u lista . / private int cuentaRegs () { int contador = 0; Estudiante actual = l i s t a ; w h i l e ( a c t u a l != n u l l ) { c o n t a d o r ++; / Vemos s i e s e l u l t i m o / i f ( a c t u a l . g e t S i g u i e n t e ( ) == n u l l ) { ultimo = actual ; } actual = actual . getSiguiente (); } return contador ; }

El grupo lo rellenaremos con ceros a la izquierda para garantizar homogeneidad, usando para ello el mtodo rellenaCampo de la clase Cadenas en el paquete e utiles en nuestro subdirectorio de trabajo. Regresando a los mtodos constructores de la clase, ambos se encuentran en e el listado 5.14 en la pgina opuesta y hacen uso de los mtodos privados que a e

217 acabamos de mostrar.

Datos estructurados

Cdigo 5.14 Constructores para la clase CursoListas o


4300 4400 4500 4600 4700 4800 4900 5000 5100 5200 5300 5400 5500 5600 5700 5800 5900 6000 6100 6200 6300 6400 6500

CursoListas

/ Crea un e j e m p l a r nuevo de <code>C u r s o L i s t a s </code >. @param g r u p o a de t i p o <code>S t r i n g </code >, da e l numero d e l grupo . / public C u r s o L i s t a s ( S t r i n g grupo ) { % t h i s . g r u p o = Cadenas . r e l l e n a C a m p o ( grupo , TAM GPO, 0 , i ) ; l i s t a = null ; ultimo = null ; numRegs = 0 ; } / Crea un e j e m p l a r nuevo de <code>C u r s o L i s t a s </code >. @param g r u p o de t i p o <code>S t r i n g </code >, e l numero de g r u p o . @param l i s t a I n i c i a l de t i p o <code>E s t u d i a n t e </code >, una l i s t a i n i c i a l de e s t u d i a n t e s . / p u b l i c C u r s o L i s t a s ( S t r i n g grupo , E s t u d i a n t e l i s t a I n i c i a l ) { t h i s . g r u p o = Cadenas . r e l l e n a C a m p o ( grupo , TAM GPO, 0 , i ) ; lista = listaInicial ; 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. Procede a contar los registros en la u lista e inicia el contador de registros con el nmero de registros contado. u Dado que en la clase CursoLista tenemos tres atributos, tenemos que denir tres mtodos de acceso a los atributos. Los tres mtodos regresan simplemente el e e valor del atributo. Los podemos ver en el listado 5.15 en la siguiente pgina. a

5.2 La lista de registros

218
CursoListas (1/2)

Cdigo 5.15 Mtodos que trabajan con los atributos del objeto o e
6700 6800 6900 7000 7100 7200 7300 7400 7500

/ Mtodo <code>getGrupo </code >, r e g r e s a d i r e c t a m e n t e l a c a d e n a e a l m a c e n a d a p a r a e l numero d e l g r u p o . . @ r e t u r n t i p o <code>S t r i n g </code >, que e s e l numero de grupo . / public S t r i n g getGrupo ( ) { return grupo ; }

Cdigo 5.15 Mtodos que trabajan con los atributos del objeto o e
7700 7800 7900 8000 8100 8200 8300 8400 8500 8600 8700 8800 8900 9000 9100 9200 9300

CursoListas (1/2)

/ Mtodo <code>g e t L i s t a </code> r e g r e s a l a r e f e r e n c i a a l p r i m e r o e de l a l i s t a . @ r e t u r n t i p o <code>E s t u d i a n t e </code >. / public Estudiante getLista () { return l i s t a ; } / Mtodo <code>getNumRegs</code >: r e g r e s a e l n mero de e u r e g i s t r o s en l a b a s e de d a t o s . @ r e t u r n v a l o r de t i p o <code>i n t </code >. / p u b l i c i n t getNumRegs ( ) { r e t u r n numRegs ; }

Junto con los mtodos de acceso debemos tener mtodos que alteren o asignen e e 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 directamente. a En cambio, podemos querer cambiarle el nmero de grupo a una lista. Lo haceu mos simplemente indicando cul es el nuevo nmero de grupo. Podemos ver este a u mtodo en el listado 5.16 en la pgina opuesta. e a

219

Datos estructurados
CursoListas

Cdigo 5.16 Modica el nmero de grupo o u


9500 9600 9700 9800 9900 10000 10100 / Mtodo <code>s e t G r u p o </code >: a c t u a l i z a e l g r u p o . e @param g r t i p o <code>S t r i n g </code >: nuevo v a l o r . / public void setGrupo ( S t r i n g gr ) { g r u p o = ( g r == n u l l ) ? "" : g r ; }

La codicacin de los mtodos que regresan alguno de los campos editados de o e un registro simplemente delegan en el registro para que sea ste el que entregue e el valor de sus campos. Mostramos el cdigo correspondiente en el listado 5.17; el o algoritmo es secuencial y muy sencillo, por lo que no mostramos los diagramas de Warnier correspondientes.

5.2 La lista de registros

220
CursoListas (1/2)

Cdigo 5.17 Mtodos que entregan el contenido de un registro determinado o e


10500 10600 10700 10800 10900 11000 11100 11200 11300 11400 11500 11600 11700 11800 11900 12000 12100 12200 12300 12400 12500 12600 12700 12800 12900 13000 13100 13200 13300 13400 13500 13600 13700 13800 13900 14000 14100 14200 14300 14400 14500 14600 14700 14800 14900

/ Mtodo <code>daNombre</code> r e g r e s a e l nombre e d i t a d o con e l o s b l a n c o s r e q u e r i d o s p a r a a l c a n z a r e l tama o e s p e c i f i c a d o . n @param e s t u d i a n t e de t i p o <code>E s t u d i a n t e </code >, l a r e f e r e n c i a a l e s t u d i a n t e deseado . @ r e t u r n t i p o a <code>S t r i n g </code >, l a c a d e n a con e l nombre e d i t a d o . / p u b l i c S t r i n g daNombre ( E s t u d i a n t e e s t u d i a n t e ) { i f ( e s t u d i a n t e != n u l l ) { r e t u r n e s t u d i a n t e . daNombre ( ) ; } r e t u r n " Estudiante invalido " ; } / Mtodo <code>d a C a r r e r a </code> r e g r e s a l a c a r r e r a e d i t a d a con e l e nombre de l a misma y l o s b l a n c o s r e q u e r i d o s p a r a a l c a n z a r e l tama o e s p e c i f i c a d o . n @param e s t u d i a n t e de t i p o <code>E s t u d i a n t e </code >, l a r e f e r e n c i a a l e s t u d i a n t e deseado . @ r e t u r n t i p o a <code>S t r i n g </code >, l a c a d e n a con l a carrera editada . / public String daCarrera ( Estudiante estudiante ) { i f ( e s t u d i a n t e != n u l l ) { return e s t u d i a n t e . daCarrera ( ) ; } r e t u r n " Estudiante no valido " ; } / Mtodo <code>d a C o r r e o </code> r e g r e s a e l c o r r e o e d i t a d o con l o s e b l a n c o s r e q u e r i d o s p a r a a l c a n z a r e l tama o e s p e c i f i c a d o . n @param e s t u d i a n t e de t i p o <code>E s t u d i a n t e </code >, l a r e f e r e n c i a a l e s t u d i a n t e deseado . @ r e t u r n t i p o a <code>S t r i n g </code >, l a c a d e n a con e l correo editado . / public S t r i n g daCorreo ( Estudiante e s t u d i a n t e ) { i f ( e s t u d i a n t e != n u l l ) { return e s t u d i a n t e . daCorreo ( ) ; } r e t u r n " Estudiante no valido " ; }

221

Datos estructurados
CursoListas (2/2)

Cdigo 5.17 Mtodos que entregan el contenido de un registro determinado o e


15100 15200 15300 15400 15500 15600 15700 15800 15900 16000 16100 16200 16300 16400 16500 16600 16700 16800 16900 17000 17100 17200 17300 17400 17500 17600 17700 17800 17900

/ Mtodo <code>daCuenta </code> r e g r e s a e l nombre e d i t a d o con e l o s c e r o s r e q u e r i d o s p a r a a l c a n z a r e l tama o e s p e c i f i c a d o . n @param e s t u d i a n t e de t i p o <code>E s t u d i a n t e </code >, l a r e f e r e n c i a a l e s t u d i a n t e deseado . @ r e t u r n t i p o a <code>S t r i n g </code >, l a c a d e n a con e l numero de c u e n t a e d i t a d o . / p u b l i c S t r i n g daCuenta ( E s t u d i a n t e e s t u d i a n t e ) { i f ( e s t u d i a n t e != n u l l ) { r e t u r n e s t u d i a n t e . daCuenta ( ) ; } r e t u r n " Estudiante no valido " ; } / Mtodo <code>a r m a R e g i s t r o </code >, arma un r e n g l n p a r a e o l a t a b l a que s e va a m o s t r a r en un l i s t a d o . @param e s t u d i a n t e t i p o <code>E s t u d i a n t e </code >, e l r e g i s t r o a armar . @ r e t u r n t i p o a <code>S t r i n g </code >, una c a d e n a con e l r e g i s t r o formateado y editado . / public String armaRegistro ( Estudiante estudiante ) { i f ( e s t u d i a n t e != n u l l ) { return e s t u d i a n t e . armaRegistro ( ) ; } return n u l l ; }

Podemos empezar ya con las funciones propias de la base de datos, como agregar un estudiante a la lista. Realmente podr amos agregarlo de tal manera que se mantenga un cierto orden alfabtico o simplemente agregarlo en cualquier posicin e o de la misma. Haremos esto ultimo; el mantener la lista ordenada se har poste a riormente. Hay dos posiciones lgicas para agregar un registro. La primera de ellas, 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 decir que la nueva lista consiste de este primer registro, seguido de la vieja lista. Veamos en la gura 5.13 en la siguiente pgina qu es lo que queremos decir. a e El diagrama de Warnier para hacer esto est en la gura 5.14 en la siguiente a pgina. Hay que considerar el caso en que estamos ingresando al primer elemento a de la lista, en cuyo caso se trata tambin del ultimo en la lista. e

5.2 La lista de registros

222

Figura 5.13

Esquema del agregado de un registro al principio de la lista

Antes:
@ lista @ ultimo Inf o @ Inf o @ Inf o @ Inf o @
[]

@ nuevo

Inf o

@
[]

Despus: e
@ lista @ ultimo Inf o @ Inf o @ Inf o @ Inf o @
[]

@ nuevo

Inf o

Figura 5.14

Agregando al principio de la lista $ ! 'Lista vac Poner a ultimo a apuntar a nuevo ' a ' ' ' ' ' ' ' ' ' ' ! ' ' 'Lista vac & a Agrega registro ' al principio 'Poner a nuevo a apuntar al ' ' ' ' primer elemento de la lista ' ' ' ' ' ' 'Poner a lista a apuntar ' ' %
al nuevo elemento La programacin de este mtodo se encuentra en el listado 5.18. o e

223

Datos estructurados
CursoListas

Cdigo 5.18 Agregar un registro al principio de la lista o


18100 18200 18300 18400 18500 18600 18700 18800 18900 19000 19100 19200 19300 19400 19500

/ Mtodo <code>agregaAlumno </code >, i n s e r t a a l r e g i s t r o r e f e r i d o e p o r nuevo en l a l i s t a de r e g i s t r o s , en e l l u g a r que l e corresponde alfabticamente . e @param nuevo de t i p o <code>E s t u d i a n t e </code >, e l r e g i s t r o que se desea i n s e r t a r . / p u b l i c v o i d agregaAlumno ( E s t u d i a n t e nuevo ) { i f ( l i s t a == n u l l ) { // e l p r i m e r o e s t a m b i n e l u l t i m o e u l t i m o = nuevo ; } nuevo . s e t S i g u i e n t e ( l i s t a ) ; // A g r e g a r l o a l p r i n c i p i o l i s t a = nuevo ; // l a l i s t a a h o r a e m p i e z a en e l nuevo numRegs ++; }

Es importante el orden en que se redirigen las referencias. Si primero redirigimos a la cabeza de la lista perdemos la lista, por lo que primero hay que amarrar el inicio anterior de la lista registrndolo en la referencia que tiene a nuevo al siguiente de la lista. Podemos tener otra rma de este mismo mtodo, que es cuando el usuario proe porciona directamente los campos del registro. En ese caso simplemente armamos al vuelo el objeto de la clase Estudiante e invocamos al mtodo que acabamos de e dar. El cdigo se puede ver en el listado 5.19. o

Cdigo 5.19 Agregar un estudiante con especicacin de cada campo o o


197 198 199 200 201 202 203 204 205 206 207

CursoListas

/ Mtodo <code>agregaAlumno </code> l o que h a c e . e @param nmbre de t i p o <code>S t r i n g </code> . @param c t a de t i p o <code>i n t </code> . @param c a r r e r a de t i p o <code>i n t </code> . @param c o r r e o de t i p o <code>S t r i n g </code> . / p u b l i c v o i d agregaAlumno ( S t r i n g nmbre , i n t c a r r e r a , S t r i n g cta , String correo ) { agregaAlumno ( new E s t u d i a n t e ( nmbre , c o r r e o , c t a , c a r r e r a ) ) ; }

Si quisiramos acomodar al registro nuevo al nal de la lista y tuviramos que e e recorrer la lista para encontrar al ultimo registro en ella las cosas se complicar an

5.2 La lista de registros

224

un poco ms, aunque no demasiado. Queremos hacer lo que aparece en la gua ra 5.15 con la l nea punteada. Afortunadamente tuvimos precaucin de mantener o una referencia al ultimo elemento de la lista.

Figura 5.15

Esquema para agregar al nal de la lista


@ lista Inf o @ ultimo @ Inf o @ Inf o @ Inf o
[]

@ nuevo

@ Inf o [ ]

Debemos tomar la precaucin que si la lista est vac cuando vamos a ingresar o a a el registro, ste es el primero de la lista pero tambin el ultimo. El algoritmo se e e muestra en el diagrama de Warnier de la gura 5.16.

Figura 5.16

Algoritmo para agregar al nal de la lista $ # ' 'lista vac lista apunta a nuevo ' a ' ' ' ultimo apunta a nuevo ' ' ' ' ' ' $ ' & ' ' Agregar registro 'Poner al registro apuntado por & ' al nal de la lista ' 'lista vac a ' ' ' Poner ultimo a apuntar a nuevo ' ' ' a ultimo a apuntar ' ' % ' ' ' a nuevo ' ' %

La programacin del algoritmo queda como se ve en el listado 5.20 en la pgina o a opuesta. Tambin ac el orden en que se actualizan las referencias es importante, e a ya que si primero modicamos la referencia ultimo ya no tendr amos un acceso directo al ultimo registro actual de la lista.

225

Datos estructurados
CursoListas

Cdigo 5.20 Agregar un registro al nal de la lista o


20900 21000 21100 21200 21300 21400 21500 21600 21700 21800 21900 22000 22100 22200 22300 22400

/ Mtodo <code>a g r e g a A l F i n a l </code> a g r e g a a un r e g i s t r o a l f i n a l e de l a l i s t a . @param nuevo de t i p o <code>E s t u d i a n t e </code >, e l r e g i s t r o a agregar t a l cual . / p u b l i c v o i d a g r e g a A l F i n a l ( E s t u d i a n t e nuevo ) { i f ( l i s t a == n u l l ) { l i s t a = nuevo ; u l t i m o = nuevo ; } else { u l t i m o . s e t S i g u i e n t e ( nuevo ) ; u l t i m o = nuevo ; } numRegs++; }

Tambin en este mtodo conviene tener otra rma para cuando el usuario e e proporciona directamente los campos del registro. Al igual que en el caso anterior simplemente armamos al vuelo el objeto de la clase Estudiante e invocamos al mtodo que acabamos de dar. El cdigo se puede ver en el listado 5.21. e o

Cdigo 5.21 Mtodo que recibe los campos para agregar a un estudiante o e
22600 22700 22800 22900 23000 23100 23200 23300 23400 23500 23600 23700 23800 23900 24000

CursoListas

/ Mtodo <code>a g r e g a A l F i n a l </code> c o n s t r u y e un r e g i s t r o y l o e a g r e g a a l f i n a l de l a l i s t a . @param nmbre t i p o <code>S t r i n g </code >, e l nombre d e l estudiante . @param c a r r e r a t i p o <code>i n t </code >, l a c l a v e de l a c a r r e r a . @param c t a t i p o <code>S t r i n g </code >, e l numero de c u e n t a d e l estudiante . @param c o r r e o t i p o <code>S t r i n g </code >, e l c o r r e o d e l estudiante . / p u b l i c v o i d a g r e g a A l F i n a l ( S t r i n g nmbre , i n t c a r r e r a , S t r i n g cta , String correo ) { a g r e g a A l F i n a l ( new E s t u d i a n t e ( nmbre , c o r r e o , c t a , c a r r e r a ) ) ; }

Sigamos implementando aquellas funciones que responden al patrn de proceso o que dimos para recorrer una lista. Por ejemplo, el mtodo que lista todos los e registros responde exactamente a este patrn. Para facilitar la interaccin con el o o usuario, el mtodo construye una cadena con los registros editados y es lo que e entrega. El algoritmo lo podemos ver en la gura 5.17 en la siguiente pgina. Lo a

5.2 La lista de registros

226

primero que hacemos al entrar a un mtodo de este tipo es vericar si la lista e est vac Si no lo est procederemos a recorrerla. a a. a

Figura 5.17

Imprimiendo todos los registros

# $ $ ' ' 'lista vac Reportar error ' ' ' a ' ' ' ' ' ' Salir con No hay & ' ' 'Inicializacin ' # o ' ' ' ' ' ' ' ' 'lista vac Colocarse al principio de la lista ' ' ' ' a ' % ' ' Acumulador de cadena & Imprime lista $ ' ' 'Procesa el registro ' ' ' &Agrega el registro actual ' ' ' ' actual ' editado a cadena ' (mientras haya) ' ' ' ' %Pasa al registro siguiente ' ' ' ' ' ' ! ' ' %Final Entrega la cadena

La programacin se encuentra en el listado 5.22. o

227

Datos estructurados
CursoListas

Cdigo 5.22 Imprimiendo toda la lista o


24200 24300 24400 24500 24600 24700 24800 24900 25000 25100 25200 25300 25400 25500 25600 25700 25800 25900 26000 26100

/ Mtodo <code>d a m e L i s t a </code> r e g r e s a una c a d e n a que e r e p r e s e n t a a una t a b l a con l o s d i s t i n t o s r e g i s t r o s . @ r e t u r n t i p o <code>S t r i n g </code >, que e s l a t a b l a con cada r e g i s t r o en un r e n g l n y a l i n e a d o . o / public S t r i n g dameLista () { i f ( l i s t a == n u l l ) { // V e r i f i c a r l i s t a v a ca r e t u r n "No hay estudiantes registrados en el grupo " ; } // I n i c i a l i z a c i n o S t r i n g s L i s t a = "" ; // P r e p a r a a c u m u l a d o r e s E s t u d i a n t e a c t u a l = l i s t a ; // C o l o c a r s e p r i n c i p i o de l i s t a w h i l e ( a c t u a l != n u l l ) { // R e p e t i r m i e n t r a s haya en l a l i s t a s L i s t a = s L i s t a + "\n" // P r o c e s a r r e g i s t r o + actual . armaRegistro ( ) ; a c t u a l = a c t u a l . g e t S i g u i e n t e ( ) ; // P a s a r a l s i g u i e n t e } return s L i s t a ; // R e g r e s a r v a l o r b u s c a d o }

Una vez que tenemos el mtodo que nos construye la lista del grupo podemos e pedir el acta, que ser un listado con el nmero de grupo y el nmero de a u u estudiantes inscritos. Lo unico que tenemos que hacer es agregar estos dos ultimos datos a la lista que nos entrega el mtodo dameLista. El cdigo se puede ver en el e o listado 5.23.

Cdigo 5.23 Mtodo que da el acta del grupo o e


263 264 265 266 267 268 269 270 271 272

CursoListas

/ Mtodo <code>dameCurso </code> e n t r e g a g r u p o b i e n e d i t a d o e en forma de t a b l a , p r e c e d i d o p o r e l numero d e l g r u p o . @ r e t u r n t i p o a <code>S t r i n g </code> l a t a b l a c o m p l e t a . / p u b l i c S t r i n g dameCurso ( ) { r e t u r n " Grupo : " + Cadenas . r e l l e n a C a m p o ( grupo , TAM GPO, 0 , i ) + "\tNum. de alumnos : " + numRegs + "\n\n" + d a m e L i s t a ( ) ; }

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 (impri-

5.2 La lista de registros

228

me) o no. El algoritmo se encuentra en la gura 5.18 obviamos en el algoritmo la vericacin de si la lista est vac o a a.

Figura 5.18

Impresin de registros seleccionados o ! $ 'Inicializacin o Toma el primero de la lista ' ' ' ' $ $ ' ' ' ' 'El campo en el registro &Acepta el ' ' ' ' actual caza con la sub' ' ' ' ' ' % registro ' ' cadena ' ' ' ' ' Procesa el ' ' ' ' & & Construye lista ' $ de registros ' registro actual 'El campo en el registro ' seleccionados ' ' (mientras haya) ' actual NO caza con la & ' ' ' ' ' ' ' ' ' ' ' % ' subcadena ' ' ' ' ' ' ' ' ' %Toma el registro siguiente ' ' ' ' ' ! ' ' %Final Entrega lista construida

El unico punto no es cmo se decide si un campo cumple o no. Para ello o lo que hacemos es ver si el campo solicitado contiene a la subcadena indicada o no, cuando no se trata del campo de la carrera, que es un entero. En el caso del campo de la carrera, que en la base de datos es un entero pero en el argumento es una cadena, debemos vericar si es una clave de carrera vlida o asignar un 0 a si no lo es l neas 30600 a 31000: Procedemos a convertir la cadena en el entero correspondiente l nea 30700, vericamos que sea una clave vlida de carrera (o a 0) l neas 30800 a 31000 y procedemos a vericar con la anotada en el registro actual l nea 31100. Estamos suponiendo que nos estn pasando la clave de l a carrera en una variable de cadena.

229

Datos estructurados

La programacin de este mtodo se puede ver en el listado 5.24. o e

Cdigo 5.24 Impresin de registros seleccionados o o


27500 27600 27700 27800 27900 28000 28100 28200 28300 28400 28500 28600 28700 28800 28900 29000 29100 29200 29300 29400 29500 29600 29700 29800 29900 30000 30100 30200 30300 30400 30500 30600 30700 30800

CursoListas (1/2)

/ Mtodo <code>losQueCazanCon </code >: arma una l i s t a con l o s e r e g i s t r o s que cumplen t e n e r una s u b c a d e n a . @param cad de t i p o <code>S t r i n g </code >, l a c a d e n a a b u s c a r . @param campo de t i p o <code>i n t </code >, e l campo d e n t r o d e l r e g i s t r o a buscar . @ r e t u r n t i p o a <code>S t r i n g </code >, una l i s t a con t o d o s l o s r e g i s t r o s que cumplen . / p u b l i c S t r i n g losQueCazanCon ( S t r i n g cad , i n t campo ) { // V e r i f i c a r que l o s a r g u m e n t o s s o n c o r r e c t o s i f ( cad == n u l l ) { r e t u r n " Subcadena inv lida para buscar " ; a } i f ( campo < NOMBRE | | campo > CUENTA) { r e t u r n " Campo inv lido " ; a } // D e c l a r a c i o n e s e i n i c i a l i z a c i n o S t r i n g s L i s t a = "" ; // Para a c u m u l a r l o s r e g i s t r o s Estudiante actual = l i s t a ; // Para r e c o r r e r l a l i s t a y c o l o // c a r s e a l p r i n c i p i o de l a l i s t a i n t l u g a r = 1; // Para v e r i f i c a r s i un r e g i s t r o cumple // R e c o r r e r l i s t a s ( m i e n t r a s haya ) w h i l e ( a c t u a l != n u l l ) { l u g a r = 1; // Suponemos que no cumple // V e r i f i c a r s i cumple l a c o n d i c i n o i f ( campo != CARRERA) { // C o n v e r t i r a m i n u s c u l a s cad b u s c a d a cad = cad . t o L o w e r C a s e ( ) . t r i m ( ) ; l u g a r = a c t u a l . daCampo ( campo ) . t o L o w e r C a s e ( ) . t r i m ( ) . i n d e x O f ( cad ) ; } // t h e n e l s e { // Suponemos que e s l a c l a v e de l a c a r r e r a l u g a r = I n t e g e r . p a r s e I n t ( cad ) ; i f (! CatalogoCarreras . esCarrera ( lugar )) {

5.2 La lista de registros

230
CursoListas (2/2)

Cdigo 5.24 Imprimiendo registros seleccionados o


30900 31000 31100 31200 31300 31400 31500 31600 31700 31800 31900 32000 32100 32200 lugar = 0; } i f ( l u g a r != a c t u a l . g e t C a r r e r a ( ) ) { l u g a r = 1; } // l u g a r queda con 1 s i no s o n i g u a l e s } // e l s e i f ( l u g a r != 1) { s L i s t a = s L i s t a + a c t u a l . a r m a R e g i s t r o ( ) +"\n" ; } // P a s a r a l s i g u i e n t e actual = actual . getSiguiente (); } // t e r m i n a e l w h i l e return s L i s t a ; }

En el listado 5.24 hacemos referencia al mtodo daCampo que implementamos e ya en el listado 5.6 en la pgina 198. En su momento explicamos las caracter a sticas del mismo, por lo que ya no hay necesidad de repetirlas. Para los mtodos localizaAlumno y eliminaAlumno el patrn que debemos seguir e o al recorrer 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 general para este tipo de bsquedas se muestra en la gura 5.19. o u

Figura 5.19

Patrn de bsqueda de un registro que cumpla con . . . o u $ $ 'Inicializacin de los ' o ' ' ' ' ' acumuladores ' & ' ' 'Inicializacin ' o ' ' 'Colocarse al principio ' ' ' ' ' ' ' de la lista % ' ' ' ' ' $ $ ' ' & ' &Procesa el registro ' Busca algo ' 'Es el solicitado ' en una lista ' Revisa registros ' & %Regresar esta ' ' ' (mientras haya y referencia ' ' ' no lo encuentres) ' ' ' ' ' ' ' ! ' ' ' ' ' %Es el solicitado Toma el siguiente ' ' ' ' ' ' ' 'No encontr el registro ! % o Emite mensaje de error
Para el caso en que se busca un registro que tenga una subcadena en un campo

231

Datos estructurados

determinado, 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 y o salir del mtodo. La programacin de este mtodo queda como se muestra en e o e el listado 5.25 en la siguiente pgina. Como se puede observar, la condicin de a o parada unicamente verica que no se haya acabado la lista l nea 34400, ya que al encontrar al registro buscado se saldr de la iteracin (y del mtodo). a o e

El algoritmo que acabamos de dar es general. Queremos que empiece a buscar a partir de un registro determinado. Esto es necesario porque varios registros pueden compartir un mismo dato y no queremos forzosamente el primero que aparezca. Por lo tanto, en lugar de colocarnos al principio de la lista, le hemos dado al mtodo la referencia desde la que queremos que busque, desde. El mtodo e e se inicializa colocndose frente al registro solicitado y busca a partir de l. Podemos a e tener un mtodo que empiece siempre al principio de la lista; lo unico que tenemos e que hacer es a este mismo mtodo pasarle el principio de la lista como punto inicial. e Este ultimo mtodo queda como se muestra en el listado 5.25. e

5.2 La lista de registros

232
CursoListas (1/2)

Cdigo 5.25 Mtodo que busca un registro o e


32400 32500 32600 32700 32800 32900 33000 33100 33200 33300 33400 33500 33600 33700 33800 33900 34000 34100 34200 34300 34400 34500 34600 34700 34800 34900

/ Mtodo <code>l o c a l i z a A l u m n o </code> l o que h a c e . e @param q u i e n de t i p o <code>S t r i n g </code >, l a c a d e n a que s e e s t a buscando . @param campo de t i p o <code>i n t </code >, e l campo donde s e va a buscar . @param d e s d e de t i p o <code>E s t u d i a n t e </code >, e l r e g i s t r o a p a r t i r d e l c u a l empieza l a s u b l i s t a . @ r e t u r n t i p o <code>E s t u d i a n t e </code >, l a r e f e r e n c i a d e l r e g i s t r o que cumple l a c o n d i c i n . o / p u b l i c E s t u d i a n t e l o c a l i z a A l u m n o ( S t r i n g q u i e n , i n t campo , Estudiante desde ) { i f ( q u i e n == n u l l ) { return n u l l ; } quien = quien . trim ( ) . toLowerCase ( ) ; // r e c o r r e l i s t a Estudiante a c t u a l = desde ; i n t l u g a r = 1; // Para d e t e c t a r l o c a l i z a c i n en c a d e n a s o w h i l e ( a c t u a l != n u l l ) { l u g a r = 1; // Suponemos que no cumple // V e r i f i c a r s i cumple l a c o n d i c i n o i f ( campo != CARRERA) { l u g a r = a c t u a l . daCampo ( campo ) . t r i m ( ) . t o L o w e r C a s e ( ) . indexOf ( quien ) ;

233

Datos estructurados
CursoListas (2/2)

Cdigo 5.25 Mtodo que busca un registro o e


35000 35100 35200 35300 35400 35500 35600 35700 35800 35900 36000 36100 36200 36300 36400 36500 36600 36700 36800

} e l s e { // Suponemos que e s l a c l a v e de l a c a r r e r a lugar = Integer . parseInt ( quien ) ; i f (! CatalogoCarreras . esCarrera ( lugar )) { lugar = 0; } i f ( l u g a r != a c t u a l . g e t C a r r e r a ( ) ) { l u g a r = 1; } // l u g a r queda con 1 s i no s o n i g u a l e s } // S i s e cumple l a c o n d i c i n o i f ( l u g a r != 1) { return a c t u a l ; } actual = actual . getSiguiente (); } // w h i l e // S i l l e g a m o s ac e s p o r q u e no s e e n c o n t r a o System . o u t . p r i n t l n ( " Registro no encontrado " ) ; return n u l l ; }

Si ya tenemos este mtodo es fcil programar el que hace la bsqueda desde el e a u principio de la lista, pasndole unicamente lo que se busca e invocando al mtodo a e que acabamos de dar con la cabeza de la lista como primer registro. Este mtodo e se encuentra en el listado 5.26.

Cdigo 5.26 Buscando desde el principio de la lista o


37000 37100 37200 37300 37400 37500 37600 37700 37800 37900 38000 38100 38200 38300

CursoListas

/ Mtodo <code>l o c a l i z a A l u m n o </code> l o c a l i z a a un e <code>E s t u d i a n t e </code> a p a r t i r d e l i n i c i o de l a l i s t a . @param q u i e n de t i p o <code>S t r i n g </code >, l a s u b c a d e n a que s e busca . @param campo t i p o <code>i n t </code >, c l a v e d e l campo donde s e b u s c a . @ r e t u r n t i p o <code>E s t u d i a n t e </code >, l a r e f e r e n c i a a l r e g i s t r o que c o n t i e n e a l a s u b c a d e n a en e l campo e s p e c i f i c a d o . / p u b l i c E s t u d i a n t e l o c a l i z a A l u m n o ( S t r i n g q u i e n , i n t campo ) { // r e c o r r e l i s t a a p a r t i r d e l p r i m e r o r e t u r n l o c a l i z a A l u m n o ( q u i e n , campo , l i s t a ) ; }

5.2 La lista de registros

234

5.2.1.

Paso por valor y por referencia


Es el momento adecuado para insistir sobre lo que pasa cuando dentro de un mtodo o funcin se altera el valor de un parmetro, como sucede en la l e o a nea 34000 en el listado 5.25 en la pgina 232. En Java se dice que los parmetros pasan por a a valor. Esto quiere decir que al llamar a un mtodo o funcin se hace una copia e o del valor de los parmetros y esta copia es la que se entrega al mtodo. Si el a e parmetro es un valor primitivo, se pasa una copia de ese valor; si el parmetro a a es un objeto (una variable de referencia) se pasa una copia de la referencia la direccin en el heap, que es, precisamente, el valor de la variable. Por lo tanto, o todo lo que el mtodo haga con ese parmetro no se reejar fuera del mtodo, e a a e ya que trabaja con una copia. Cuando un parmetro se pasa por valor se dice que a es un parmetro de entrada, que se evala lo antes posible, en el primer momento a u en que aparece: en la llamada. A lo mejor esto resulta un poco confuso cuando pasamos objetos como parmea tros. Cuando se pasa un objeto como parmetro se pasa la copia de una variable, a que es una variable de referencia, contiene la direccin donde se encuentra el obo jeto en el heap. Como vamos a trabajar directamente con el objeto en el heap, ya que tenemos su referencia, las modicaciones que se hagan dentro del mtodo al e objeto referido s se van a reejar fuera del mtodo, ya que copia o no, el argumen e to contiene una direccin vlida del heap. Los cambios a los que nos referimos son o a aqullos que se reeren al valor de la referencia, o sea, ponerlos a apuntar a otro e lado. Por ejemplo, si dentro del mtodo construimos un objeto nuevo y ponemos e al parmetro a apuntar a l, como se muestra en el cdigo en el listado 5.27. a e o

Cdigo 5.27 Paso por valor de una variable de objeto o


100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400

Estudiante (1/2)

/ Mtodo <code>c o p i a </code >: c o n s t r u y e una c o p i a y d e j a a nuevo e ap un ta ndo a l a c o p i a . @param v i e j o t i p o <code>E s t u d i a n t e </code >: e l o b j e t o a c o p i a r . @param nuevo t i p o <code>E s t u d i a n t e </code >: e l o b j e t o c o p i a d o . / p u b l i c v o i d c o p i a ( E s t u d i a n t e v i e j o , E s t u d i a n t e nuevo ) { nuevo = new E s t u d i a n t e ( ) ; nuevo . s e t C a r r e r a ( v i e j o . g e t C a r r e r a ( ) ) ; nuevo . setNombre ( v i e j o . getNombre ( ) ) ; nuevo . s e t C o r r e o ( v i e j o . g e t C o r r e o ( ) ) ; nuevo . s e t C u e n t a ( v i e j o . g e t C u e n t a ( ) ) ; v i e j o . s e t C u e n t a ( " 444444444 " ) ; } ...

235

Datos estructurados
Estudiante (2/2)

Cdigo 5.27 Paso por valor de una variable de objeto o


10000 10100 10200 10300 10400 10500

/ Supongamos l a l l a m a d a d e s d e main de l a s i g u i e n t e forma : / 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 ) { E s t u d i a n t e v i e j o = new E s t u d i a n t e ( "Lola" , " lolita@ciencias " , " 99999999 " , 1 2 7 ) ; E s t u d i a n t e nuevo = new E s t u d i a n t e ( ) ; c o p i a ( v i e j o , nuevo ) ; ...

Supongamos que se ejecutan las l neas 10200 a 10400 y la llamada se realiza como se muestra en la l nea 10500. Antes de entrar al mtodo tenemos dos variables e de objeto (referencias) y la memoria del programa se encuentra como se muestra en la gura 5.20.

Figura 5.20

Paso por valor de una variable de objeto Variables Heap


nombre cuenta carrera correo
(40800) (32100) (4086)

signte

127

(3198)

[]
(3198) (32100) (40800)

viejo

(4086)

lolita@ciencias 99999999 Lola

nombre cuenta carrera correo


(8200) (75300) (5470)

signte

(2500)

[]
(2500) (5470) (8200)

nuevo

(75300)

Una vez dentro del mtodo, despus de ejecutarlo pero antes de salir del mismo e e l nea 1300, la memoria del programa se ve como se muestra en la gura 5.21.

5.2 La lista de registros

236

Figura 5.21

Paso por valor de una variable de objeto, dentro del mtodo e Variables
(35710)

Heap
nombre cuenta carrera correo
(6020) (5432)

signte

122

(8910)

[]
(8910) (5432) (6020)

nuevo
(copia)

(35710)

lolita@ciencias 99999999 Lola

(4086)

viejo
(copia) nombre cuenta carrera correo
(40800) (88888) (4086)

signte

122

(3198)

[]
(3198) (88888) (40800)

viejo
(main)

(4086)

lolita@ciencias 444444444 Lola

nombre cuenta carrera correo


(8200) (75300) (5470)

signte

(2500)

[]
(2500) (5470) (8200)

nuevo
(main)

(75300)

Debemos notar que tenemos espacio nuevo para un objeto, cuya referencia se encuentra en la variable local nuevo; como copiamos la referencia a viejo, el cambio que se hace en la l nea 1300 del campo cuenta del objeto cuya referencia se encuentra en viejo, se hace directamente sobre el objeto y el cambio permanece ms all de la ejecucin del mtodo. a a o e Al salir del mtodo desaparecen las variables locales las copias de los are gumentos incluidas aunque los objetos construidos en el heap permanecen. Por ejemplo, el registro creado en nuevo permanece, aunque ya no tiene ninguna referencia viva, por lo que se convierte en basura no hay manera de acceder a l y e las variables de objeto mantienen el valor que ten antes de entrar al mtodo. an e

237

Datos estructurados

Figura 5.22

Paso por valor de una variable de objeto, despus de salir del mtodo e e Variables
(35710)

Heap
nombre cuenta carrera correo
(6020) (5432)

signte

122

(8910)

[]
(8910) (5432)

nuevo
(4086)

(35710)

lolita@ciencias 99999999 Lola

viejo
nombre cuenta carrera correo
(40800) (32100) (4086)

(6020)

signte

122

(3198)

[]
(3198) (32100) (40800)

viejo

(4086)

lolita@ciencias 44444444 Lola

nombre cuenta carrera correo


(8200) (75300) (5470)

signte

(2500)

[]
(2500) (5470) (8200)

nuevo

(75300)

Para resumir, los cambios hechos directamente a variables pasadas como para metros dentro de los mtodos (sean referencias o valores) no se reejan al salir del e mtodo; sin embargo, si se usa el valor de una variable de referencia (la direccin e o del objeto) el objeto s puede ser modicado y las modicaciones permanecern a despus de que termine la ejecucin del mtodo. Por ejemplo, en la l e o e nea 34000 del listado 5.25 en la pgina 232, los cambios a quien se van a ver slo mientras a o permanezcamos en buscaAlumno. Una vez que salgamos, podremos observar que la cadena que se utiliz como parmetro no sufri ningn cambio. o a o u Otro mecanismo comn de pasar parmetros, presente en algunos lenguajes de u a programacin, es lo que se conoce como paso por referencia. Con este mecanismo lo o que se pasa al mtodo es la direccin (referencia) donde se encuentra la variable e o en cuestin ya sea que se trate de variables primitivas o de objetos. Si este o mecanismo estuviese presente en Java, lo que estar amos pasando, tratndose de a objetos, ser la direccin en memoria donde se encuentra la variable que contiene a o a la referencia. Java no tiene este mecanismo de pasar parmetros, pero se lo a pueden encontrar en lenguajes como C, C++ y Pascal. Cuando un parmetro se a pasa por referencia, generalmente se hace para dejar ah algn valor, por lo que u

5.2 La lista de registros

238

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, regresando al objeto como resultado del mtodo. e Supongamos que queremos regresar los valores mximo y m a nimo de las carreras en una lista del curso que tenemos. Tendr amos que declarar un objeto con dos campos enteros max y min para dejar all los resultados, como se ve en el listado 5.28 no es necesario comentar lo que hace la clase.

Cdigo 5.28 Clase para regresar dos valores de un mtodo o e


public class DosValores { p r i v a t e i n t max , min ; p u b l i c i n t getMin ( ) { r e t u r n min ; } p u b l i c i n t getMax ( ) { r e t u r n max ; } p u b l i c v o i d s e t M i n ( i n t min ) t h i s . min = min ; } p u b l i c v o i d setMax ( i n t max ) t h i s . max = max ; } }

DosValores

{ {

En la clase CursoLista podr amos tener un mtodo que recorriera el grupo y e fuera registrando los valores mximo y m a nimo, como se muestra en el listado 5.29, donde tambin ponemos lo correspondiente a la llamada desde el mtodo main. e e

Cdigo 5.29 Regreso de dos valores primitivos o

CursoLista (1/2)

public DosValores maxyMinCarreras ( ) { i f ( l i s t a == n u l l ) { System . o u t . p r i n t l n ( "No hay nadie en la lista " ) ; return n u l l ; } i n t max , min ; max = min = l i s t a . g e t C a r r e r a ( ) ; // V a l o r e s p o r o m i s i n o E s t u d i a n t e a c t u a l = l i s t a . g e t S i g u i e n t e ( ) ; // Empezar d e l 2 do

239

Datos estructurados
CursoLista (2/2)

Cdigo 5.30 Regreso de dos valores primitivos o


w h i l e ( a c t u a l != n u l l ) { int laCarre = actual . getCarrera (); i f ( l a C a r r e > max ) max = l a C a r r e ; i f ( l a C a r r e < min ) min = l a C a r r e ; actual = actual . getSiguiente (); } // w h i l e // Se acab l a l i s t a o D o s V a l o r e s r e g r e s o = new D o s V a l o r e s ( ) ; r e g r e s o . setMax ( max ) ; r e g r e s o . s e t M i n ( min ) ; return r e g r e s o ; } ... System . o u t . p r i n t l n ( " M ximo de carrera " ) ; a D o s V a l o r e s v a l o r e s = enOrden . m a x y M i n C a r r e r a s ( ) ; System . o u t . p r i n t l n ( "max=" + v a l o r e s . getMax ( ) + "\tmin=" + v a l o r e s . getMin ( ) ) ;

El mtodo regresa un objeto de la clase DosValores2 , que tiene mtodos para e e extraer los valores regresados. Se declara un objeto de la clase referida y se invoca al mtodo que nos arma el objeto con los valores deseados. El algoritmo para e seleccionar los valores es un ejemplar del algoritmo que recorre la lista. Para el proceso de cada registro hacemos lo que se muestra en el diagrama de la gura 5.23.

Figura 5.23

Obtencin del mximo y m o a nimo en una lista

actual.getCarrera() ! Obtener mximo a y m nimo max <valTemp max valTemp 'Procesar registro ' ' (mientras haya) ' ' ' '
' ' ' ' ' ' ' ' % ! ' ' %min >valTemp min valTemp
Regresar objeto con min y max

$ ' 'Inicio ' ' ' ' ' ' ' ' ' ' ' &

max min

$ 'valTemp ' ' ' &

primer valor primer valor

Se acostumbra regresar un arreglo, pero como es un tema que todav no revisamos, el a

5.2 La lista de registros

240

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. Como mantenemos una referencia al ultimo elemento de la lista, tambin e ser distinto eliminar al ultimo pues tendremos que actualizar esta referencia. a Veamos en la gura 5.24 los tres casos.

Figura 5.24

Eliminacin de un estudiante o
lista
@

Eliminacin del ultimo o Inf o


@

ultimo
@

Inf o

Inf o

[]

Inf o

[]

lista
@

Eliminacin del primero o Inf o


@

ultimo
@

Inf o

Inf o

Inf o

[]

lista
@

Eliminacin entre dos registros o Inf o


@

ultimo
@

Inf o

Inf o

Inf o

[]

En el primer caso tenemos que redirigir la referencia ultimo a que ahora apunte hacia el que era el penltimo registro; para ello tenemos que localizar al u registro anterior al ultimo; tambin debemos anular la referencia en el penltimo e u registro para que ah termine la lista.
mtodo regresa un objeto que contiene dos valores. e

241

Datos estructurados

El segundo caso es mucho ms sencillo porque unicamente hay que redirigir la a cabeza de la lista hacia el segundo registro. El tercer caso coincide con el primero pues tambin hay que localizar al ree gistro anterior, para modicar las referencias de ste a que ahora sean al registro e siguiente. En los tres casos, de alguna manera tenemos que tener la referencia del anterior a menos que unicamente haya un registro en la lista. Una vista general del algoritmo para eliminar un registro se encuentra en la gura 5.25. No ponemos en el algoritmo la vericacin de que los argumentos sean o correctos, como es el caso de que nos proporcionen una lista vac o una referencia a nula para el estudiante a eliminar. En la gura 5.26 desarrollamos con mayor detalle la parte correspondiente a cuando el registro a eliminar no es el primero.

Figura 5.25

Eliminacin de un registro en una lista (vista general) o $ $ ' ' ' ' ' 'Modica a lista para que ' ' ' ' 'Es el primero & ' apunte al que sigue ' ' ' 'Avisa que s se pudo ' ' ' ' ' ' ' % ' ' ' ' ' ' ' ' ' ' $ $ ' ' ' ' ' ' ' ' ' ' 'Localizar al que se desea eliminar ' ' ' ' ' ' ' ' ' y registrar quin es el anterior ' ' ' e Eliminar & ' ' ' ' ' ' ' un & ' ' Proceso de ' ' estudiante ' Es el ultimo ' ' ' ' 'eliminacin ' ' ' ' o ' ' ' ' ' ' ' & ' ' ' ' ' 'Es el primero 'Es el ultimo ' ' ' ' ' ' % ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'Eliminado ! ' ' ' ' Avisa que S se pudo I ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ! ' ' ' ' %Eliminado % Avisa que NO se pudo
Hay que distinguir los tres casos. En primera instancia unicamente pregunta mos si es el primero de la lista o no. Si es el primero, la eliminacin es sumamente o sencilla, ya que unicamente hay que actualizar la cabeza de la lista. Si no es el primero, ya sea que est en medio o al nal, deberemos localizar al registro que e deseamos eliminar, registrando cada vez que avanzamos en la lista, la referencia al

5.2 La lista de registros

242

registro anterior; esto se debe a que queremos modicar la referencia en el registro anterior. Una vez localizado el registro a eliminar y el anterior a ste, procedemos e a manejar el caso de que sea el ultimo.

Figura 5.26

Eliminacin de un registro en una lista (vista a detalle) o $ $ $ ' ' ' ' 'Colocarse en el que ' ' ' ' ' & ' ' ' ' 'Inicializacin ' sigue a lista ' ' o ' ' ' ' 'Registrar a lista co' ' ' ' ' ' ' ' % ' ' ' ' mo el anterior & ' ' ' Localizar al que " $ ' ' 'se desea eliminar ' 'NO es moverse al ' ' ' ' ' ' ' y registrar quin ' Revisar la lista ' siguiente ' & ' e ' ' ' ' es el anterior '(mientras haya Y ' ' ' ' ' ' " ' no lo encuentre) ' ' ' ' ' ' ' ' ' 'SI es Marcar ' % % ' ' ' encontrado ' ' ' $ ' ' & & Proceso de Es el ultimo %ultimoanterior eliminacin ' o ' ' ' ' ' ' ' ' ' ' ' ' ' ' ! ' ' 'Es el ultimo ' ' ' ' ' $ ' ' ' & ' ' 'Encontrado ' ' ' %Brincar a actual en la lista ' ' (anterior.setSiguiente(actual.getSiguiente())) ' ' ' ' ' ' ' ! ' 'Encontrado %
Como estamos trabajando con una lista ligada donde todas las referencias apuntan hacia el nal de la lista, cuando queramos trabajar con uno de los registros (objetos) de la lista tendremos que tener la referencia a ese objeto, y lo ideal es que usemos la referencia que se encuentra en el registro anterior. Si lo que recibe nuestro mtodo es la referencia del registro anterior habr posibilidad de e a modicar la referencia al actual, lo que requerimos para eliminar al registro actual o insertar un registro entre el anterior y el actual. Por estas razones codicaremos un mtodo cuyo unico objetivo es proporcionar la referencia del registro anterior e al que buscamos. Vamos a seguir para ello el algoritmo general que dimos en la

243

Datos estructurados

gura 5.19, donde lo que queremos que cumpla es que su referencia al siguiente corresponda al registro que estamos buscando. La codicacin de este mtodo se o e encuentra en el listado 5.31.

Cdigo 5.31 Mtodo que localiza al elemento anterior en una lista o e


39500 39600 39700 39800 39900 40000 40100 40200 40300 40400 40500 40600 40700 40800 40900 41000 41100 / Mtodo <code>b u s c a A n t e r i o r </code> b u s c a a l r e g i s t r o que e t o c a a n t e s d e l que s e d e s e a . Sabemos que e l que s e d e s e a no e s e l p r i m e r o de l a l i s t a y que l a l i s t a no e s t v a ca . a @param q u i e n t i p o <code>E s t u d i a n t e </code >, e l r e g i s t r o a buscar . @ r e t u r n t i p o <code>E s t u d i a n t e </code >, l a r e f e r e n c i a a l anterior . / private Estudiante buscaAnterior ( Estudiante quien ) { Estudiante 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 && a c t u a l . g e t S i g u i e n t e ( ) != q u i e n ) { actual = actual . getSiguiente (); } return a c t u a l ; }

Estamos suponiendo que el usuario ya localiz la referencia del registro que o desea. Este mtodo slo va a ser invocado desde mtodos de la clase, no por el e o e usuario. Usamos el mtodo buscaAnterior para simplicar el cdigo del mtodo elimie o e

5.2 La lista de registros naAlumno, que se encuentra en el listado 5.32.

244

Cdigo 5.32 Eliminacin de un registro en una lista o o


41300 41400 41500 41600 41700 41800 41900 42000 42100 42200 42300 42400 42500 42600 42700 42800 42900 43000 43100 43200 43300 43400

CursoListas (1/2)

/ Mtodo <code>e l i m i n a A l u m n o </code> e l i m i n a a l r e g i s t r o c uy a e r e f e r e n c i a s e p a s a como argumento . @param q u i e n de t i p o <code>E s t u d i a n t e </code >, l a r e f e r e n c i a d e l registro a eliminar . / p u b l i c boolean e l i m i n a A l u m n o ( E s t u d i a n t e q u i e n ) { / V e r i f i c a r que l a l i s t a no e s t v a ca y que l a r e f e r e n c i a e s e a c o r r e c t a / i f ( q u i e n == n u l l | | l i s t a == n u l l ) { System . o u t . p r i n t l n ( " Par metros inv lidos al " a a + " tratar de eliminar " ) ; return f a l s e ; } i f ( l i s t a == q u i e n ) { // Es l a p r i m e r a r e f e r e n c i a i f ( u l t i m o == q u i e n ) { // e s l a u n i c a r e f e r e n c i a ultimo = null ; } l i s t a = l i s t a . getSiguiente (); numRegs ; return true ; }

Cdigo 5.32 Eliminacin de un registro en una lista o o


43500 43600 43700 43800 43900 44000 44100 44200 44300 44400 44500 44600 44700 44800 44900 45000

CursoListas (2/2)

// E n c o n t r a r a l a n t e r i o r Estudiante a n t e r i o r = buscaAnteriorAQuien ( l i s t a , quien ) ; // V e r i f i c a r que s e a e l que b u s c a b a i f ( a n t e r i o r . g e t S i g u i e n t e ( ) == q u i e n ) { // l o e n c o n t r e a n t e r i o r . s e t S i g u i e n t e ( quien . getSiguiente ( ) ) ; i f ( u l t i m o == q u i e n ) { ultimo = anterior ; } numRegs ; return true ; } // No l o e n c o n t r en l a l i s t a e System . o u t . p r i n t l n ( "No se encontr a:\n" o + quien . armaRegistro ( ) ) ; return f a l s e ; }

Estamos aprovechando que podemos salir de un mtodo a la mitad del mismo e

245

Datos estructurados

para que quede un esquema ms simple de la programacin, como en el caso de a o que el registro a eliminar sea el primero. 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.

Insercin en orden alfabtico o e


En general, cuando deseamos una lista de nombres, lo comn es que la deseeu mos en orden alfabtico (o lexicogrco). Para ello, antes de agregar un registro e a debemos encontrar el lugar que le toca en la lista. Nuevamente, si el nombre es lexicogrcamente menor (va antes en el alfabeto) que el primero, lo insertamos al a principio de la lista; si es mayor que el ultimo, lo insertamos al nal de la lista; si no tenemos ninguno de estos dos casos, deberemos recorrer la lista hasta encontrar el que va antes y el que va despus, para modicar las referencias como se muestra e en la gura 5.27. En esta gura tenemos con l neas punteadas cmo deben quedar o las referencias despus de insertar un registro en el lugar que le corresponde de e acuerdo al nombre3 .

Figura 5.27

Insercin ordenada de registros o Variables


@ lista @ anterior @ nuevo
Mario Federico

Heap
@
Pedro

Tania

[]

[]

En este caso aparece unicamente la cadena del primer nombre como si estuviera directamente en el registro, aunque sabemos que, por tratarse de cadenas, lo que aparece ah es una referencia.

5.2 La lista de registros El algoritmo para lograr esta insercin aparece en la gura 5.28. o

246

Figura 5.28

Algoritmo para insertar a un estudiante en orden $ $ ' 'sNombre cadena a comparar en nuevo ' ' ' ' ' ' ' 'sLista cadena a comparar en lista ' ' ' ' ' ' ' ' ' ' $ ' ' ' ' ' ' principio ' ' ' &sNombre sLista &Agregar alde la lista ' 'Inicio ' ' % ' ' ' Salir ' ' ' ' ' ' ' ' ' ' ' ' ' ' # ' ' ' ' ' ' ' ' ' 'sNombre sLista Colocarse al principio de la lista ' ' ' % ' ' Comparar ' ' ' ' & $ Agrega 'sLista nombre que sigue al actual ' ' en orden ' ' ' $ ' ' ' Encuentra ' ' ' ' 'Agrega entre actual y ' ' & ' ' ' & lugar ' ' '(mientras haya sNombre sLista ' actual.getSiguiente() ' ' %Salir ' y siga uno ' ' ' ' ' ' ' ' menor) ' ' ' ' ' ' ' ' ! ' ' ' ' %sNombre sLista Pasar al siguiente ' ' ' ' ' ' ' $ $ ' ' ' & ' ' & ' ' 'Final ' ' %Agregar al nal ' 'Lleg al nal de la lista % % o
de la lista

Para este algoritmo, la implementacin en Java se encuentra en el listado 5.33. o

247

Datos estructurados
CursoListas

Cdigo 5.33 Insercin de un registro en orden lexicogrco o o a


47400 47500 47600 47700 47800 47900 48000 48100 48200 48300 48400 48500 48600 48700 48800 48900 49000 49100 49200 49300 49400 49500 49600 49700 49800 49900 50000 50100 50200 50300 50400 50500 50600 50700 50800 50900 51000 51100 51200 51300 51400

/ Mtodo <code>agregaEnOrden </code >: I n s e r t a un nuevo r e g i s t r o en e orden l e x i c o g r f i c o . a @param nuevo t i p o <code>E s t u d i a n t e </code >: r e g i s t r o a i n s e r t a r . / p u b l i c v o i d agregaEnOrden ( E s t u d i a n t e nuevo ) { i f ( l i s t a == n u l l ) { l i s t a = u l t i m o = nuevo ; numRegs++; return ; } S t r i n g sNuevo = nuevo . getNombre ( ) . t r i m ( ) ; S t r i n g s L i s t a = l i s t a . getNombre ( ) . t r i m ( ) ; i n t compara = sNuevo . c o m p a r e T o I g n o r e C a s e ( s L i s t a ) ; i f ( compara < 0 ) { // Le t o c a e l p r i m e r o nuevo . s e t S i g u i e n t e ( l i s t a ) ; l i s t a = nuevo ; numRegs++; return ; } / Hay a l menos uno y no l e t o c a a n t e s de l / e E s t u d i a n t e a c t u a l = l i s t a ; // C o l o c a r s e a l p r i n c i p i o de l a l i s t a compara = sNuevo . c o m p a r e T o I g n o r e C a s e ( s L i s t a ) ; // T i e n e que d a r >0 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 && compara >= 0 ) { s L i s t a = a c t u a l . g e t S i g u i e n t e ( ) . getNombre ( ) . t r i m ( ) ; compara = sNuevo . c o m p a r e T o I g n o r e C a s e ( s L i s t a ) ; i f ( compara < 0 ) { // E l nuevo e s menor nuevo . s e t S i g u i e n t e ( a c t u a l . g e t S i g u i e n t e ( ) ) ; a c t u a l . s e t S i g u i e n t e ( nuevo ) ; numRegs++; return ; } actual = actual . getSiguiente (); } // Cubre e l c a s o de que l e t o q u e a l f i n a l i f ( a c t u a l . g e t S i g u i e n t e ( ) == n u l l ) { // l e t o c a a l u l t i m o a c t u a l . s e t S i g u i e n t e ( nuevo ) ; u l t i m o = nuevo ; numRegs++; } }

5.2 La lista de registros

248

Prueba de la clase CursoListas


Ya tenemos todo lo que desebamos tener y ms, pues estamos agregando a a de tres distintas maneras. Programemos el mtodo main para esta clase que e simplemente pruebe los mtodos que tenemos. El mtodo main queda como se e e muestra en el listado 5.34.

Cdigo 5.34 Prueba de la clase CursoListas o


51600 51700 51800 51900 52000 52100 52200 52300 52400 52500 52600 52700 52800 52900 53000 53100 53200 53300 53400 53500 53600 53700 53800 53900 54000 54100 54200 54300 54400 54500 54600 54700 54800 54900 55000 55100 55200 55300 55400

CursoListas (1/2)

/ / / Prueba de l o s m todos 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 ) { C u r s o L i s t a s i c c 1 = new C u r s o L i s t a s ( "7004" ) ; // Agregamos a l g u n o s e s t u d i a n t e s a l p r i n c i p i o de l a l i s t a i c c 1 . agregaAlumno ( "Viso Elisa " , 1 2 2 , " 06730268 " , " elisa@ciencias " ) ; i c c 1 . agregaAlumno ( " Magidin Arturo " , 1 2 2 , " 089428452 " , " arturo@ams " ) ; i c c 1 . agregaAlumno ( " Torres Maria " , 1 2 7 , " 301245329 " , " torresm@ciencias " ) ; i c c 1 . agregaAlumno ( " Barajas Miguel " , 1 0 6 , "000" , "no tiene " ) ; System . o u t . p r i n t l n ( i c c 1 . dameCurso ( ) ) ; System . o u t . p r i n t l n ( i c c 1 . d a m e L i s t a ( ) ) ; // Probamos a h o r a que a g r e g u e en o r d e n C u r s o L i s t a s i c c O r d = new C u r s o L i s t a s ( "7004" ) ; Estudiante laLista = icc1 . getLista ( ) ; E s t u d i a n t e nuevo ; C u r s o L i s t a s a l R e v e s = new C u r s o L i s t a s ( "7005" ) ; l a L i s t a = iccOrd . g e t L i s t a ( ) ; w h i l e ( l a L i s t a != n u l l ) { a l R e v e s . agregaAlumno ( l a L i s t a . getNombre ( ) , laLista . getCarrera () , l a L i s t a . getCuenta ( ) , l a L i s t a . getCorreo ( ) ) ; laLista = laLista . getSiguiente (); } System . o u t . p r i n t l n ( "al reves :\n" + alReves . dameLista ( ) ) ; C u r s o L i s t a s a l F i n a l = new C u r s o L i s t a s ( "7006" ) ; a l F i n a l . agregaAlumno ( "Viso Elisa " , 1 2 2 , " 06730268 " , " elisa@ciencias " ) ; a l F i n a l . agregaAlumno ( " Magidin Arturo " , 1 2 2 , " 089428452 " , " arturo@ams " ) ; a l F i n a l . agregaAlumno ( " Torres Maria " , 1 2 7 , " 301245329 " , " torresm@ciencias " ) ; a l F i n a l . agregaAlumno ( " Barajas Miguel " , 1 0 6 , "000" , "no tiene " ) ; System . o u t . p r i n t l n ( a l F i n a l . dameCurso ( ) ) ;

249

Datos estructurados
CursoListas (2/2)

Cdigo 5.34 Prueba de la clase CursoListas o


55500 55600 55700 55800 55900 56000 56100 56200 56300 56400 56500 56600 56700 56800 56900 57000 57100 57200 57300 57400 57500 57600 57700 57800 57900 58000 58100 58200 58300 58400 58500 58600

/ E l i m i n a n d o r e g i s t r o s de l a l i s t a / E s t u d i a n t e q u i e n = a l F i n a l . l o c a l i z a A l u m n o ( "Viso" ,NOMBRE) ; i f ( q u i e n != n u l l ) { a l F i n a l . eliminaAlumno ( quien ) ; } System . o u t . p r i n t l n ( " Despu s de eliminar a Viso :\n" e + a l F i n a l . dameCurso ( ) ) ; q u i e n = a l F i n a l . l o c a l i z a A l u m n o ( " Barajas " ,NOMBRE) ; i f ( q u i e n != n u l l ) { a l F i n a l . eliminaAlumno ( quien ) ; } System . o u t . p r i n t l n ( " Despu s de eliminar a Barajas :\n" e + a l F i n a l . dameCurso ( ) ) ; C u r s o L i s t a s enOrden = new C u r s o L i s t a s ( "7006" ) ; enOrden . agregaEnOrden ( new E s t u d i a n t e ( "Viso Elisa " , " elisa@ciencias " , " 06730268 " , 122)); enOrden . agregaEnOrden ( new E s t u d i a n t e ( " Magidin Arturo " , " arturo@ams " , " 089428452 " , 122)); enOrden . agregaEnOrden ( new E s t u d i a n t e ( " Torres Maria " , " torresm@ciencias " , " 301245329 " , 127)); enOrden . agregaEnOrden ( new E s t u d i a n t e ( " Barajas Miguel " , "no tiene " , "000" , 106)); System . o u t . p r i n t l n ( enOrden . dameCurso ( ) ) ; }

La salida que se produce con la ejecucin del mtodo main de la clase se muestra o e en el listado 5.29.

Figura 5.29

Ejecucin del mtodo main de la clase CursoListas o e


Grupo : 7004 000000000 301245329 089428452 Nm . de alumnos : 4 u F s i c a no t i e n e C i e n c i a s de l a T i e r r a torresm@ciencias M a t e m t i c a s a arturo@ams

Salida (1/3)

Barajas Miguel T o r r e s Mar a Magidin Arturo

5.2 La lista de registros

250
Salida (2/3) Viso E l i s a elisa@ciencias Barajas Miguel T o r r e s Mar a Magidin Arturo Viso E l i s a

Figura 5.30

Ejecucin del mtodo main de la clase CursoListas o e


006730268 000000000 301245329 089428452 006730268 M a t e m t i c a s a F s i c a no t i e n e C i e n c i a s de l a T i e r r a torresm@ciencias M a t e m t i c a s a arturo@ams M a t e m t i c a s a elisa@ciencias

al revs : e No hay e s t u d i a n t e s r e g i s t r a d o s en e l g r u p o Grupo : 7006 Nm . de alumnos : 4 u

000000000

F s i c a

Barajas Miguel T o r r e s Mar a Magidin Arturo Viso E l i s a

no t i e n e 301245329 C i e n c i a s de l a T i e r r a torresm@ciencias 089428452 M a t e m t i c a s a arturo@ams 006730268 M a t e m t i c a s a elisa@ciencias Despu s de e l i m i n a r a V i s o : e Grupo : 7006 Nm . de alumnos : 3 u

000000000

F s i c a

Barajas Miguel T o r r e s Mar a Magidin Arturo

no t i e n e 301245329 C i e n c i a s de l a T i e r r a torresm@ciencias 089428452 M a t e m t i c a s a arturo@ams Despu s de e l i m i n a r a B a r a j a s : e Grupo : 7006 Nm . de alumnos : 2 u

C i e n c i a s de l a T i e r r a torresm@ciencias 089428452 M a t e m t i c a s a arturo@ams Grupo : 7006 Nm . de alumnos : 4 u

301245329

T o r r e s Mar a Magidin Arturo

251

Datos estructurados

Figura 5.30

Ejecucin del mtodo main de la clase CursoListas o e


000000000 089428452 F s i c a no t i e n e M a t e m t i c a s a arturo@ams 301245329 006730268 C i e n c i a s de l a T i e r r a torresm@ciencias M a t e m t i c a s a elisa@ciencias

Salida (3/3) Barajas Miguel Magidin Arturo

T o r r e s Mar a Viso E l i s a

Ayudndose de los enunciados que imprimen para localizar cada una de las a l neas que lo hacen, se puede seguir la ejecucin de la clase de manera sencilla, o por lo que ya no ilustraremos esta ejecucin. o

5.2.3.

La clase MenuCursoListas
La programacin del men para tener acceso a la base de datos del curso es o u 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 se encuentra o en el listado 5.34 en la siguiente pgina. a 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 6500-6800 en el listado 5.34 en la siguiente pgina), a procedemos a invocar al mtodo agregaAlumno de la clase CursoListas. Este mtodo e e tiene como argumento un objeto, que es creado en el momento de invocar a agrega ver l nea 7000 del listado 5.34 en la siguiente pgina. Tambin podemos pedir a e que agregue a un estudiante nuevo en el lugar que le toca alfabticamente, si ae gregamos la opcin de agregar en orden l o neas 7200 a 7800. Estos son los unicos mtodos en los que se crean objetos, y esto tiene sentido, ya que se requiere crear e objetos slo cuando se desea agregar a un estudiante. o Para el caso de los mtodos de la clase CursoListas que regresan una referencia e a un objeto de la clase Estudiante, es para lo que se declar la variable cual l o nea 5500 del listado 5.34 en la siguiente pgina. En estos casos el objeto ya existe, y a lo unico que hay que pasar o recibir son las variables que contienen la referencia.

5.2 La lista de registros

252
MenuCursoListas (1/6)

Cdigo 5.34 Men para el manejo de la lista o u


100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400 2500 2600 2700 2800 2900 3000 3100 3200 3300 3400 3500 3600 3700 3800 3900 4000 4100 4200 4300 4400 4500 4600

package C o n s u l t a s L i s t a s ; import j a v a . u t i l . S c a n n e r ; import u t i l e s . C a t a l o g o C a r r e r a s ; / c l a s s MenuCurso e s l a c l a s e de u s o p a r a l a b a s e de d a t o s d e l grupo . C r e a t e d : Lun Mar 8 0 7 : 5 2 : 2 6 2 0 1 0 . @ a u t h o r <a h r e f = m a i l t o : e l i s a @ l a m b d a . f c i e n c i a s . unam . mx> E l i s a V i s o G u r o v i c h </a> @version 2.0 / public class MenuCursoListas { p r i v a t e s t a t i c f i n a l i n t FIN = 0 , AGREGA = 1 , BUSCA = 2 , LISTA = 3 , ELIGE = 4 , QUITA = 5 , MUESTRA = 6 , AGREGAENORDEN = 7 ; p r i v a t e s t a t i c f i n a l S t r i n g elMenu = " Elige una opci n :" o + "[0] Terminar \n" + "[1] Agregar estudiante \n" + "[2] Buscar estudiante \n" + "[3] Listar todos \n" + "[4] Listar los que cazan con ...\n" + "[5] Eliminar estudiante \n" + "[6] Mostrar estudiante \n" + "[7] Agregar en orden \n" ; p r i v a t e s t a t i c f i n a l i n t MAXCAMPo = 4 ; private int opcion = 0; p r i v a t e S t r i n g s o p c i o n = "" ; p r i v a t e s t a t i c f i n a l S t r i n g sCampos = "(1) Nombre " + "(2) Carrera \n" + "(3) Correo \n" + "(4) Num. de cuenta " ; / Mtodo <code>daMenu</code >: Maneja l a i n t e r a c c i n con e l e o u s u a r i o a t r a v s de un men . e u @param c o n s t i p o <code>S c a n n e r </code >: d i s p o s i t i v o de l e c t u r a . @param miCurso t i p o <code>C u r s o L i s t a </code >: o b j e t o d e l c u r s o . @ r e t u r n t i p o <code>i n t </code >: u l t i m a o p c i n s e l e c c i o n a d a . o /

253

Datos estructurados
MenuCursoListas (2/6)

Cdigo 5.34 Men para el manejo de la lista o u


4700 4800 4900 5000 5100 5200 5300 5400 5500 5600 5700 5800 5900 6000 6100 6200 6300 6400 6500 6600 6700 6800 6900 7000 7100 7200 7300 7400 7500 7600 7700 7800 7900 8000 8100 8200 8300 8400 8500 8600 8700 8800 8900 9000 9100 9200 9300

p u b l i c i n t daMenu ( S c a n n e r cons , C u r s o L i s t a s miCurso ) { s o p c i o n = "" ; opcion = 0; System . o u t . p r i n t l n ( elMenu ) ; System . o u t . p r i n t l n ( " Elige una opcion -->\t" ) ; sopcion = cons . nextLine ( ) ; o p c i o n = " 0123456 " . i n d e x O f ( s o p c i o n . c h a r A t ( 0 ) ) ; S t r i n g quien , subcad ; Estudiante cual ; S t r i n g nombre = n u l l ; S tr i n g cuenta = null ; String correo = null ; int carrera = 0; boolean b r e s p = f a l s e ; switch ( opcion ) { case FIN : System . o u t . p r i n t l n ( " Mucho gusto en haberle servido " ) ; r e t u r n 1; case AGREGA : nombre = pideNombre ( c o n s ) ; cuenta = pideCuenta ( cons ) ; c o r r e o = pideCorreo ( cons ) ; c a r r e r a = p i d e C a r r e r a ( cons ) ; miCurso . agregaAlumno ( new E s t u d i a n t e ( nombre , c o r r e o , cuenta , c a r r e r a ) ) ; r e t u r n AGREGA ; case AGREGAENORDEN : nombre = pideNombre ( c o n s ) ; cuenta = pideCuenta ( cons ) ; c o r r e o = pideCorreo ( cons ) ; c a r r e r a = p i d e C a r r e r a ( cons ) ; miCurso . agregaEnOrden ( new E s t u d i a n t e ( nombre , c o r r e o , cuenta , c a r r e r a ) ) ; r e t u r n AGREGAENORDEN ; case BUSCA : i f ( miCurso . g e t L i s t a ( ) == n u l l ) { System . o u t . p r i n t l n ( "No hay nadie registrado todav a " ) ; r e t u r n BUSCA ; } // La l i s t a d e l g r u p o no e s t v a ca a System . o u t . p r i n t l n ( "Dame la cadena a buscar : " ) ; subcad = cons . n e x t L i n e ( ) ; i f ( s u b c a d . l e n g t h ( ) == 0 ) { System . o u t . p r i n t l n ( "Hubo un error de entrada " ) ; r e t u r n BUSCA ; } System . o u t . p r i n t l n ( " Ahora dime en cu l campo quieres " a + "que busque :" ) ;

5.2 La lista de registros

254
MenuCursoListas (3/6)

Cdigo 5.34 Men para el manejo de la lista o u


9400 9500 9600 9700 9800 9900 10000 10100 10200 10300 10400 10500 10600 10700 10800 10900 11000 11100 11200 11300 11400 11500 11600 11700 11800 11900 12000 12100 12200 12300 12400 12500 12600 12700 12800 12900 13000 13100 13200 13300 13400 13500 13600 13700 13800 13900 14000 14100 14200 14300

System . o u t . p r i n t l n ( sCampos ) ; i n t campo = c o n s . n e x t I n t ( ) ; i f ( campo <= 0 | | campo > MAXCAMPO) { System . o u t . p r i n t l n ( " Datos err neos " ) ; o r e t u r n BUSCA ; } c u a l = miCurso . l o c a l i z a A l u m n o ( subcad , campo ) ; i f ( c u a l == n u l l ) { System . o u t . p r i n t l n ( "No existe registro con esta cadena " ) ; r e t u r n BUSCA ; } do { System . o u t . p r i n t l n ( "El alumnos buscado es :\t" + miCurso . a r m a R e g i s t r o ( c u a l ) ) ; System . o u t . p r i n t l n ( " Deseas ver al siguiente ?" ) ; b r e s p = c o n s . n e x t L i n e ( ) . e q u a l s ( "Si" ) ; i f (! bresp ) r e t u r n BUSCA ; i f ( c u a l == n u l l ) { System . o u t . p r i n t l n ( "Se acab la base de datos " ) ; o r e t u r n BUSCA ; } c u a l = miCurso . l o c a l i z a A l u m n o ( subcad , campo , c u a l ) ; } w h i l e ( c u a l != n u l l ) ; System . o u t . p r i n t l n ( "No hay mas registros con esa subcadena " ) ; r e t u r n BUSCA ; case LISTA : System . o u t . p r i n t l n ( miCurso . d a m e L i s t a ( ) ) ; r e t u r n LISTA ; case ELIGE : System . o u t . p r i n t l n ( "Qu subcadena deben cazar :" ) ; e subcad = cons . n e x t L i n e ( ) ; i f ( s u b c a d . l e n g t h ( ) == 0 ) { System . o u t . p r i n t l n ( "No se dio una cadena v lida " a + " a buscar " ) ; r e t u r n ELIGE ; } System . o u t . p r i n t ( "En qu campo ?" e + "(1) nombre \n(2) cuenta \n" + "(3) correo \n(4) carrera \n" + " campo --> " ) ; S t r i n g strCampo = c o n s . n e x t L i n e ( ) . t r i m ( ) ; i f ( strCampo . l e n g t h ( ) == 0 ) { System . o u t . p r i n t l n ( "No se dio un campo v lido " a + " donde buscar " ) ; r e t u r n ELIGE ; } System . o u t . p r i n t l n ( miCurso . losQueCazanCon ( subcad , I n t e g e r . p a r s e I n t ( strCampo ) ) ) ; r e t u r n ELIGE ;

255

Datos estructurados
MenuCursoListas (4/6)

Cdigo 5.34 Men para el manejo de la lista o u


14400 14500 14600 14700 14800 14900 15000 15100 15200 15300 15400 15500 15600 15700 15800 15900 16000 16100 16200 16300 16400 16500 16600 16700 16800 16900 17000 17100 17200 17300 17400 17500 17600 17700 17800 17900 18000 18100 18200 18300 18400 18500 18600 18700 18800 18900 19000 19100 19200

case QUITA : System . o u t . p r i n t l n ( "Dame el alumno a eliminar " ) ; quien = cons . nextLine ( ) ; i f ( q u i e n . l e n g t h ( ) == 0 ) { System . o u t . p r i n t l n ( "Me diste una cadena inv lida " ) ; a r e t u r n QUITA ; } bresp = false ; do { c u a l = miCurso . l o c a l i z a A l u m n o ( q u i e n , E s t u d i a n t e .NOMBRE) ; i f ( c u a l == n u l l ) { System . o u t . p r i n t l n ( "Ya no hay m s con este campo " ) ; a r e t u r n QUITA ; } System . o u t . p r i n t ( " Eliminar a *" + miCurso . daNombre ( c u a l ) . t r i m ( ) + "*, Si , No --> " ) ; b r e s p = c o n s . n e x t L i n e ( ) . e q u a l s ( "Si" ) ; i f ( bresp ) { i f ( miCurso . e l i m i n a A l u m n o ( c u a l ) ) { System . o u t . p r i n t l n ( " Alumno eliminado del curso " ) ; r e t u r n QUITA ; } } System . o u t . p r i n t l n ( " Deseas ver el siguiente ?" ) ; b r e s p = c o n s . n e x t L i n e ( ) . e q u a l s ( "Si" ) ; } w h i l e ( b r e s p && c u a l != miCurso . g e t U l t i m o ( ) && c u a l != n u l l ) ; System . o u t . p r i n t l n ( "No se elimin a nadie " ) ; o r e t u r n QUITA ; case MUESTRA: System . o u t . p r i n t l n ( "Dame una subcadena a buscar " ) ; quien = cons . nextLine ( ) ; i f ( q u i e n . l e n g t h ( ) == 0 ) { System . o u t . p r i n t l n ( "Dato invalido " ) ; r e t u r n MUESTRA; } c u a l = miCurso . l o c a l i z a A l u m n o ( q u i e n , E s t u d i a n t e .NOMBRE) ; i f ( c u a l != n u l l ) { System . o u t . p r i n t l n ( miCurso . a r m a R e g i s t r o ( c u a l ) ) ; } else { System . o u t . p r i n t l n ( "No hay registro con esta subcadena " ) ; } r e t u r n MUESTRA; default : System . o u t . p r i n t l n ( " Opcion no existente " ) ; return 0; } // s w i t c h } // daMenu

5.2 La lista de registros

256
MenuCursoListas (5/6)

Cdigo 5.34 Men para el manejo de la lista o u


19300 19400 19500 19600 19700 19800 19900 20000 20100 20200 20300 20400 20500 20600 20700 20800 20900 21000 21100 21200 21300 21400 21500 21600 21700 21800 21900 22000 22100 22200 22300 22400 22500 22600 22700 22800 22900 23000 23100 23200 23300 23400 23500 23600 23700

/ Mtodo <code>pideNombre </code >, p i d e e l nombre d e l e s t u d i a n t e . e @param c o n s v a l o r de t i p o <code>S c a n n e r </code> p a r a l e e r d a t o s del usuario . @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >, e l d a t o p r o p o r c i o n a d o . / p r i v a t e S t r i n g pideNombre ( S c a n n e r c o n s ) { System . o u t . p r i n t l n ( "Dame el nombre :\t" ) ; S t r i n g nombre = c o n s . n e x t L i n e ( ) ; r e t u r n nombre ; } / Mtodo <code>p i d e C u e n t a </code >, p i d e e l n mero de c u e n t a d e l e u estudiante . @param c o n s t i p o <code>S c a n n e r </code> p a r a l e e r d a t o s del usuario . @ r e t u r n t i p o <code>S t r i n g </code >, e l d a t o p r o p o r c i o n a d o . / private S t r i n g pideCuenta ( Scanner cons ) { System . o u t . p r i n t l n ( "Dame el numero de cuenta :\t" ) ; S t r i n g cuenta = cons . nextLine ( ) ; return cuenta ; } / Mtodo <code>p i d e C o r r e o </code >, p i d e e l c o r r e o d e l e s t u d i a n t e . e @param c o n s t i p o <code>S c a n n e r </code> p a r a l e e r d a t o s del usuario . @ r e t u r n t i p o <code>S t r i n g </code >, e l d a t o p r o p o r c i o n a d o . / private S t r i n g pideCorreo ( Scanner cons ) { System . o u t . p r i n t l n ( "Dame el correo :\t" ) ; S t r i n g c o r r e o = cons . nextLine ( ) ; return correo ; } / Metodo <code>p i d e C a r r e r a </code >, p i d e e l c o r r e o d e l e s t u d i a n t e . @param c o n s v a l o r de t i p o <code>S c a n n e r </code> p a r a l e e r d a t o s del usuario . @ r e t u r n v a l o r de t i p o <code>i n t </code >, e l d a t o p r o p o r c i o n a d o . / private i nt p i d e C a r r e r a ( Scanner cons ) { int carrera = 0;

257

Datos estructurados
MenuCursoListas (6/6)

Cdigo 5.34 Men para el manejo de la lista o u


23800 23900 24000 24100 24200 24300 24400 24500 24600 24700 24800 24900 25000 25100 25200 25300 25400 25500 25600 25700 25800 25900 26000 26100 26200 26300 26400 26500 26600 26700 26800 26900 27000 27100 27200 27300 27400 27500 27600 27700 27800 27900 28000 28100 28200 28300 }

while ( ! CatalogoCarreras . e s C a r r e r a ( c a r r e r a )) { System . o u t . p r i n t l n ( "Las carreras registradas son:" ) ; CatalogoCarreras . muestraCatalogo ( ) ; System . o u t . p r i n t l n ( "Dame la carrera :" ) ; c a r r e r a = cons . n e x t I n t ( ) ; c o n s . n e x t L i n e ( ) ; // Para u s a r e l r e t o r n o de c a r r o i f (! CatalogoCarreras . esCarrera ( carrera )) { System . o u t . p r i n t l n ( " Carrera incorrecta ." + " Vuelve a proporcionar :" ) ; } } // w h i l e return c a r r e r a ; } / / / Prueba de l o s m todos 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 ) { S c a n n e r c o n s = new S c a n n e r ( System . i n ) ; Estudiante l i s t i t a = new E s t u d i a n t e ( " Aguilar Solis Aries Olaf ", " aaguilar " , " 400001528 " , 1 2 2 ) ; l i s t i t a . s e t S i g u i e n t e ( new E s t u d i a n t e ( "Cruz Cruz Gil No e ", " ncruz " , " 098034011 " , 1 0 7 ) ) ; l i s t i t a . getSiguiente (). s e t S i g u i e n t e ( new E s t u d i a n t e ( " Garcia Villafuerte Israel ", " igarcia " , " 098159820 " , 1 0 1 ) ) ; l i s t i t a . getSiguiente (). getSiguiente (). s e t S i g u i e n t e ( new E s t u d i a n t e ( " Hubard Escalera Alfredo " ahubard " , " 099586197 " , 2 0 1 ) ) ; l i s t i t a . getSiguiente (). getSiguiente (). getSiguiente (). g e t S i g u i e n t e ( ) . s e t S i g u i e n t e ( new E s t u d i a n t e ( " Tapia Vazquez Rogelio " , " rtapia " , " 098162075 " , 1 ) ) ; C u r s o L i s t a s miCurso = new C u r s o L i s t a s ( "7050" , l i s t i t a ) ; M e n u C u r s o L i s t a s menu = new M e n u C u r s o L i s t a s ( ) ; int resp = 0; do { r e s p = menu . daMenu ( cons , miCurso ) ; } w h i l e ( r e s p >= 0 ) ; System . o u t . p r i n t l n ( "Fue un placer servirte " ) ; cons . c l o s e ( ) ; }

",

5.2 La lista de registros

258

Esta ejecucin produce la interaccin con el usuario que se ve en la gura 5.31. o o

Figura 5.31
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 42 43 44 45

Ejecucin en pantalla de la clase MenuCursoLista o


e l i s a @ l a m b d a : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $ j a v a C o n s u l t a s L i s t a s / MenuCursoListas E l i g e una o p c i n : o [ 0 ] Terminar [ 1 ] Agregar e s t u d i a n t e [ 2 ] Buscar e s t u d i a n t e [ 3 ] L i s t a r todos [ 4 ] L i s t a r l o s que c a z a n con . . . [5] Eliminar estudiante [ 6 ] Mostrar e s t u d i a n t e [ 7 ] A g r e g a r en o r d e n E l i g e una o p c i n > o 7 Dame e l nombre : Viso E l i s a Dame e l numero de c u e n t a : 067302682 Dame e l c o r r e o : elisa@ciencias Las c a r r e r a s r e g i s t r a d a s s o n : C a t a l o g o de c a r r e r a s de l a F a c u l t a d de C i e n c i a s =============================================== 101 Actuaria 104 C i e n c i a s de l a Computaci n o 106 F s i c a 122 M a t e m t i c a s a 127 C i e n c i a s de l a T i e r r a 201 B i o l o ga 217 Manejo S u s t e n t a b l e de Zonas C o s t e r a ========================================================= Dame l a c a r r e r a : 104 E l i g e una o p c i n : o [ 0 ] Terminar [ 1 ] Agregar e s t u d i a n t e [ 2 ] Buscar e s t u d i a n t e [ 3 ] L i s t a r todos [ 4 ] L i s t a r l o s que c a z a n con . . . [5] Eliminar estudiante [ 6 ] Mostrar e s t u d i a n t e [ 7 ] A g r e g a r en o r d e n E l i g e una o p c i n o 3

(1/6)

>

259

Datos estructurados

Figura 5.31
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

Ejecucin en pantalla de la clase MenuCursoLista o


400001528 es Olaf 098034011 M a t e m t i c a s a aaguilar F s i c a ncruz 098159820 M a t e m t i c a s a e Israel igarcia 099586197 B i o l o ga lfredo ahubard 098162075 Manejo S u s t e n t a b l e de Zonas C o s t e r a elio rtapia 067302682 C i e n c i a s de l a Computaci n o elisa@ciencias Acciones a r e a l i z a r : [ 0 ] Terminar [ 1 ] Agregar e s t u d i a n t e [ 2 ] Buscar e s t u d i a n t e [ 3 ] L i s t a r todos [ 4 ] L i s t a r l o s que c a z a n con . . . [5] Eliminar estudiante [ 6 ] Mostrar e s t u d i a n t e [ 7 ] A g r e g a r en o r d e n

(2/6) A g u i l a r S o l s A r i Cruz Cruz G i l No e G a r ca V i l l a f u e r t Hubard E s c a l e r a A T a p i a V zquez Rog a Viso E l i s a

E l i g e una o p c i n > o 1 Dame e l nombre : A b rn B a t u l e V i r g i n i a Dame e l numero de c u e n t a : 070245672 Dame e l c o r r e o : abrin@servidor Las c a r r e r a s r e g i s t r a d a s s o n : C a t a l o g o de c a r r e r a s de l a F a c u l t a d de C i e n c i a s =============================================== 101 Actuaria 104 C i e n c i a s de l a Computaci n o 106 F s i c a 122 M a t e m t i c a s a 127 C i e n c i a s de l a T i e r r a 201 B i o l o ga 217 Manejo S u s t e n t a b l e de Zonas C o s t e r a ========================================================= Dame l a c a r r e r a : 122 Acciones a r e a l i z a r :

5.2 La lista de registros

260
(3/6)

Figura 5.31
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 132 133 134

Ejecucin en pantalla de la clase MenuCursoLista o


[0] [1] [2] [3] [4] [5] [6] [7] Terminar Agregar e s t u d i a n t e Buscar e s t u d i a n t e L i s t a r todos L i s t a r l o s que c a z a n con . . . Eliminar estudiante Mostrar e s t u d i a n t e A g r e g a r en o r d e n

E l i g e una o p c i n o 3 070245672 inia 400001528 es Olaf 098034011

>
A b rn B a t u l e V i r g A g u i l a r S o l s A r i Cruz Cruz G i l No e G a r ca V i l l a f u e r t Hubard E s c a l e r a A T a p i a V zquez Rog a Viso E l i s a

M a t e m t i c a s a abrin@servidor M a t e m t i c a s a aaguilar F s i c a ncruz 098159820 M a t e m t i c a s a e Israel igarcia 099586197 B i o l o ga lfredo ahubard 098162075 Manejo S u s t e n t a b l e de Zonas C o s t e r a elio rtapia 067302682 C i e n c i a s de l a Computaci n o elisa@ciencias Acciones a r e a l i z a r : [ 0 ] Terminar [ 1 ] Agregar e s t u d i a n t e [ 2 ] Buscar e s t u d i a n t e [ 3 ] L i s t a r todos [ 4 ] L i s t a r l o s que c a z a n con . . . [5] Eliminar estudiante [ 6 ] Mostrar e s t u d i a n t e [ 7 ] A g r e g a r en o r d e n E l i g e una o p c i n > o 1 Dame e l nombre : Torres Morales Mauricio Dame e l numero de c u e n t a : 081234567 Dame e l c o r r e o : mauri@ciencias

261

Datos estructurados

Figura 5.31
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 165 166 167 168 169 170 171 172 173 174 175 176 177 178

Ejecucin en pantalla de la clase MenuCursoLista o


Las c a r r e r a s r e g i s t r a d a s s o n : C a t a l o g o de c a r r e r a s de l a F a c u l t a d de C i e n c i a s =============================================== 101 Actuaria 104 C i e n c i a s de l a Computaci n o 106 F s i c a 122 M a t e m t i c a s a 127 C i e n c i a s de l a T i e r r a 201 B i o l o ga 217 Manejo S u s t e n t a b l e de Zonas C o s t e r a ========================================================= Dame l a c a r r e r a : 101 Acciones a r e a l i z a r : [ 0 ] Terminar [ 1 ] Agregar e s t u d i a n t e [ 2 ] Buscar e s t u d i a n t e [ 3 ] L i s t a r todos [ 4 ] L i s t a r l o s que c a z a n con . . . [5] Eliminar estudiante [ 6 ] Mostrar e s t u d i a n t e [ 7 ] A g r e g a r en o r d e n E l i g e una o p c i n o 3 081234567 uricio 070245672 inia 400001528 es Olaf 098034011

(4/6)

>
T o r r e s M o r a l e s Ma A b rn B a t u l e V i r g A g u i l a r S o l s A r i Cruz Cruz G i l No e G a r ca V i l l a f u e r t Hubard E s c a l e r a A T a p i a V zquez Rog a Viso E l i s a

Actuaria

mauri@ciencias M a t e m t i c a s a abrin@servidor M a t e m t i c a s a aaguilar F s i c a ncruz 098159820 M a t e m t i c a s a e Israel igarcia 099586197 B i o l o ga lfredo ahubard 098162075 Manejo S u s t e n t a b l e de Zonas C o s t e r a elio rtapia 067302682 C i e n c i a s de l a Computaci n o elisa@ciencias Acciones a r e a l i z a r : [ 0 ] Terminar

5.2 La lista de registros

262
(5/6)

Figura 5.31
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 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221

Ejecucin en pantalla de la clase MenuCursoLista o


[1] [2] [3] [4] [5] [6] [7] Agregar e s t u d i a n t e Buscar e s t u d i a n t e L i s t a r todos L i s t a r l o s que c a z a n con . . . Eliminar estudiante Mostrar e s t u d i a n t e A g r e g a r en o r d e n

E l i g e una o p c i n >> o 4 Qu s u b c a d e n a deben c a z a r : e 122 En qu campo ? e ( 1 ) nombre (2) cuenta (3) correo (4) carrera campo > 4 070245672 inia 400001528 es Olaf 098159820 e Israel 067302682 M a t e m t i c a s a abrin@servidor M a t e m t i c a s a aaguilar M a t e m t i c a s a igarcia M a t e m t i c a s a elisa@ciencias A b rn B a t u l e V i r g A g u i l a r S o l s A r i G a r ca V i l l a f u e r t Viso E l i s a

Acciones a r e a l i z a r : [ 0 ] Terminar [ 1 ] Agregar e s t u d i a n t e [ 2 ] Buscar e s t u d i a n t e [ 3 ] L i s t a r todos [ 4 ] L i s t a r l o s que c a z a n con . . . [5] Eliminar estudiante [ 6 ] Mostrar e s t u d i a n t e [ 7 ] A g r e g a r en o r d e n E l i g e una o p c i n > o 5 Dame e l alumno a e l i m i n a r G a r ca E l i m i n a r a G a r ca V i l l a f u e r t e I s r a e l , S i , No Alumno e l i m i n a d o d e l c u r s o

>

Si

263

Datos estructurados

Figura 5.31
222 223 224 225 226 227 228 229 230 231 232 233 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 259 260 261 262 263 264

Ejecucin en pantalla de la clase MenuCursoLista o


Acciones a r e a l i z a r : [ 0 ] Terminar [ 1 ] Agregar e s t u d i a n t e [ 2 ] Buscar e s t u d i a n t e [ 3 ] L i s t a r todos [ 4 ] L i s t a r l o s que c a z a n con . . . [5] Eliminar estudiante [ 6 ] Mostrar e s t u d i a n t e [ 7 ] A g r e g a r en o r d e n E l i g e una o p c i n o 3 081234567 uricio 070245672 inia 400001528 es Olaf 098034011 099586197 lfredo 098162075 elio 067302682

(6/6)

>
T o r r e s M o r a l e s Ma A b rn B a t u l e V i r g A g u i l a r S o l s A r i Cruz Cruz G i l No e Hubard E s c a l e r a A T a p i a V zquez Rog a Viso E l i s a

Actuaria mauri@ciencias M a t e m t i c a s a abrin@servidor M a t e m t i c a s a aaguilar F s i c a ncruz B i o l o ga ahubard Manejo S u s t e n t a b l e de Zonas C o s t e r a rtapia M a t e m t i c a s a elisa@ciencias

Acciones a r e a l i z a r : [ 0 ] Terminar [ 1 ] Agregar e s t u d i a n t e [ 2 ] Buscar e s t u d i a n t e [ 3 ] L i s t a r todos [ 4 ] L i s t a r l o s que c a z a n con . . . [5] Eliminar estudiante [ 6 ] Mostrar e s t u d i a n t e [ 7 ] A g r e g a r en o r d e n E l i g e una o p c i n >> o 0 Mucho g u s t o en h a b e r l e s e r v i d o Fue un p l a c e r s e r v i r t e e l i s a @ l a m b d a : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $

5.2 La lista de registros

264

En la ejecucin de la clase MenuCursoLista podemos observar que la lista inicial o se arma a pie l neas 25600 a 27300 del cdigo, en orden alfabtico. Dentro de la o e ejecucin de daMenu, se agrega en orden a Viso Elisa l o neas 13 a 33, por lo que la lista sigue ordenada, como se muestra en las l neas 48 a 58 de la ejecucin. o A continuacin se elige agregar al principio un registro 69 a 89, que de todos o modos le toca al principio de la lista, quedando la lista como se muestra en las l neas 103 a 116. Inmediatamente se agrega otro estudiante al principio de la lista -l neas 128 a 147, pero como el apellido empieza con Tapia, la lista se desarregla en cuanto al orden alfabtico, como se observa cuando, a continuacin de agregar e o el registro, se pide la lista completa en las l neas 159 a 176. Se solicita, asimismo, que se elimine a un estudiante cuyo nombre contenga la subcadena Garc l a neas 217 a 221 y despus de corroborar que s es el estudiante e deseado, lo elimina. Podemos ver que el alumno cuyo nombre tiene la subcadena Garc ya no se encuentra en la base de datos, cuando elegimos listar toda la base a en las l neas 233 a 249. Con esto damos por terminado este cap tulo. Se dejan como ejercicios las siguientes mejoras: 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. Corregir el u a algoritmo para que no se espere a llegar al nal de la lista para saber que el nombre no se encuentra. Mantener la base de datos ordenada respecto al nmero de cuenta, no al u nombre. Ordenar a los registros por carrera y dentro de cada carrera alfabticamente e por nombre. En el primer caso lo que hay que cambiar es la condicin de parada del ciclo. o Para el segundo caso se trata de cambiar las condiciones de bsqueda de lugar u cuando se agrega en orden. Por ultimo, como podemos tener varios alumnos en la misma carrera, queremos que estn ordenados alfabticamente dentro de cada e e carrera.

265

Datos estructurados

Ejercicios
5.1.- Para el siguiente relato, describe cules mtodos deben estar en la interfaz a e para la clase: Tenemos una clase que convierte cantidades del sistema mtrico e decimal al sistema de medidas ingls y viceversa. Queremos que e contemple medidas de peso, volumen y distancia. Por ejemplo, que convierta kilmetros a millas y viceversa; libros a kilos y viceversa; o etc. 5.2.- Queremos una clase que calcule la trayectoria bal stica de armas de distintos n tipos ver http://en.wikipedia.org/wiki/External ballistics. Disea una interfaz que calcule trayectorias bal sticas para al menos cuatro armas distintas. Decide si las cuatro armas requieren o no de los mismos parmetros, y si no a es as disea mtodos para cada tipo de arma. , n e 5.3.- Haz el cdigo en Java para el algoritmo que se encuentra descrito en el o siguiente diagrama de Warnier. Se trata de un mtodo que recibe como e parmetro de dnde leer y regresa como resultado la suma. Se le proporcioa o narn nmeros al mtodo y se marcar el n de los datos con un Ctl-D, que a u e a indica el n de datos.

$ $ &Iniciar acumulador ' ' 'Inicio ' ' ' %Pedir primer nmero ' ' u ' ' ' ' $ $ ' ' ' ' 'suma suma + cuadrado(nmero) u ' ' & ' ' ' &Suma nmero 'Pudo leer ' ' u ' Suma de & 'Leer el siguiente nmero u % cuadrados ' (mientras no ' se acabe ' ' ' ' la entrada) ' ' ' ' ' ' ' ' ' ' 'Pudo leer ! ' % ' r ' ' ' ' ! ' ' %Final Entregar suma
Nota: Deber codicarse un mtodo en Java. a e

5.4.- Tenemos la clase CursoListas como se construy en este cap o tulo. Supongamos que queremos a los estudiantes inscritos en carreras del rea de F a sicoMatemticas (claves cien). Dado el siguiente algoritmo (en un diagrama de a

5. Ejercicios

266 Warnier), extrae de la base de datos a todos los estudiantes inscritos en el area de Ciencias F sico-Matemticas. Puedes suponer que el mtodo se ena e cuentra en la clase CursoListas y que tiene acceso a todos los mtodos de la e misma.

$ $ ' &Colocarse al principio ' ' 'Inicio de la lista ' ' ' % ' ' Inicializar cadena ' ' ' ' ' ' ' " $ ' ' ' 'Registro actual Agrgalo al ' e ' ' ' & ' listado ' Extrae estuen AFM ' & diantes de AFM 'Procesa registro ' ' (mientras haya) ' # ' ' ' ' ' ' 'Registro actual r ' ' ' ' ' % ' ' ' en AFM ' ' ' ' ' ' ! ' ' %Final Entregar listado

5.5.- En el diagrama de Warnier que sigue se encuentra un algoritmo para cambiar el nmero de cuenta de un estudiante. Si suponemos que la lista est ordeu a nada alfabticamente por nombre del estudiante, codica en Java, dentro de e la clase CursoListas, un mtodo que cambie el nmero de cuenta de un estue u diante. El mtodo recibe la referencia del estudiante al que se desea cambiar e y el nuevo nmero de cuenta y regresa el nmero de cuenta anterior. u u

Cambia cuenta

! $ 'Inicio Obtener nuevo nmero de cuenta u ' ' ' ' ' ' ' ' # ' & ' ' ' ' ' ' ' ' ' ' %
Proceso Pedir viejo nmero de cuenta u Cambia el nmero de cuenta u

Final

!
Regresa el viejo nmero de cuenta u

5.6.- Como en el caso anterior, tenemos un curso registrado con los nombres ordenados alfabticamente por nombre del estudiante. Queremos cambiar el e nombre y, si es necesario, reubicar el registro. El algoritmo se encuentra en el diagrama de Warnier que sigue. Codica el algoritmo a un mtodo en Java, e

267

Datos estructurados

en la clase CursoListas, que reciba como parmetros la referencia al registro a a modicar y el nuevo nombre a poner. El mtodo no regresa nada. e

! $ 'Inicio Construir nuevo registro ' ' ' ' ' ' ' $ ' ' ' ' ' ' ' ' ' & &Elimina registro anterior Cambia nombre Proceso ' 'Agrega en orden registro nuevo ' ' ' ' ' % ' ' ' ' ' ' ' ' ! ' ' %
Final

5.7.- Haz un mtodo para la clase CursoListas que imprima los registros en posie cin par de la lista (0, 2, 4, . . . ). El algoritmo se muestra en el diagrama de o Warnier que se encuentra a continuacin. o

$ $ 'Agrgalo a la lista ' ' e ' & ' ' 'Registro en Incrementa contador ' ' Imprimir registros 'posicin par ' ' ' o ' %Pasa al siguiente en posiciones par ' ' ' ' ' & 'Procesa registro ' ' ' (mientras haya) ' ' $ ' ' ' ' ' ' ' ' ' ' ' & ' ' ' ' ' 'Registro en r ' ' ' ' ' ' ' 'posicin par ' ' ' % o % ' ' ' ' ' ' ' ' ' " ' ' 'Final Regresa listado ' % armado
5.8.- Tenemos una lista de enteros cuyos mtodos bsicos de acceso a sus atributos e a se dan en el listado que sigue.

$ ' ' ' ' ' ' ' ' 'Inicio ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' &

$ 'Colocarse al principio ' ' ' ' de la lista ' & Iniciar acumulador ' del listado ' ' 'Iniciar contador ' ' %
de posiciones

5. Ejercicios
package C o n s u l t a s L i s t a s ; import j a v a . u t i l . S c a n n e r ; public class Enteros { p r i v a t e i n t num ; private Enteros sigue ; public Enteros () { } public Enteros ( int k) { num = k ; } p u b l i c i n t getNum ( ) { r e t u r n num ; } p u b l i c v o i d setNum ( i n t num) { t h i s . num = num ; } public Enteros getSigue () { return s i g u e ; } public void s e t S i g u e ( Enteros s i g u e ) this . sigue = sigue ; } }

268

Agrega el mtodo main y construye a pie una lista que tenga los nmeros e u (5,2,0,7,10,3,4). La forma de construir esta lista se muestra en el diagrama de Warnier a continuacin. o

$ 'Construir nuevo con num & Construir lista ' Agregar num '(num=2,0,7,10,3,4) actual.sigue nuevo ' ' ' ' % ' ' actual nuevo ' ' ' ' ' ' ! ' ' 'Final % r
Cabe aclarar que no se puede hacer esto en una iteracin, pero podemos o usar nuevo para ir apuntando al elemento que se acaba de agregar. 5.9.- Escribe un mtodo en esta clase que liste a todos los elementos de una lista. e

$ ' ' ' 'inicio ' ' ' ' ' ' ' ' ' ' ' ' &

$ & Declarar cabeza con nmero 5 u %


actual

cabeza

269

Datos estructurados

El algoritmo es el usual para recorrer listas. Apl calo a la lista que acabas de crear en el ejercicio anterior. 5.10.- Haz un mtodo ingrese a un objeto de la clase Enteros en orden a una lista e cualquiera. 5.11.- Haz un mtodo que tome una lista cualquiera de Enteros y elemento por e elemento lo clone y construya una lista ordenada con ellos. Clonar un objeto quiere decir construir uno nuevo con los mismos valores. La lista a la que se clona debe permanecer igual. El algoritmo se muestra en el diagrama de Warnier que se encuentra a continuacin. El mtodo recibe como parmetro o e a la lista a clonar y regresa la lista clonada ordenada.

Clonar y ordenar ListaVieja ' '

$ ' ' ' ' ' ' ' ' ' ' 'Inicio ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' &

! $ 'lista vac ListaClonada null a ' ' ' ' ' ' $ & 'Clonar primer elemento ' & ' ' 'lista vac Agregarlo a ListaClonada ' a ' ' ' ' ' % %
actual

segundo de ListaVieja

$ 'Clonar el elemento actual ' ' ' ' en ListaVieja 'Procesa elemento ' ' ' & ' ' ' de ListaVieja Agregarlo en orden ' ' ' (mientras haya) ' a ListaClonada ' ' ' ' ' ' Avanzar al siguiente ' ' ' ' ' % ' ' elemento en ListaVieja ' ' ' ' ' ' ' ! ' 'Final %
Regresa ListaClonada

5.12.- Haz un diagrama de Warnier para determinar, en una lista ordenada de enteros (como la anterior) cul es el nmero que ms veces se repite. Aspectos a u a que debe considerar el algoritmo: Debe registrar el mximo nmero de duplicados. Al empezar este nmea u u ro es 1. Recorre la lista comparando el nmero actual con el anterior para ver u si son iguales. Si los son, incrementa un contador; si no lo son y los que acaba de ver son ms, sustituye el nmero que era el ms repetido y la a u a cuenta de ms repetido. a

5. Ejercicios

270 5.13.- Haz un diagrama de Warnier que encuentre el nmero mayor en una lista u de Enteros. 5.14.- Haz un diagrama de Warnier que encuentre el nmero menor en una lista u de Enteros. 5.15.- Haz un diagrama de Warnier para, dados dos enteros, el algoritmo recorra una lista de Enteros y sustituya las presencias del primer entero por el segundo entero. 5.16.- Disea una clase que tenga objetos que puedan pertenecer a una lista, con n un campo que pueda almacenar un entero y con dos referencias, una de ellas al objeto que sigue y otra al que le precede. 5.17.- Dada una lista de elementos como los descritos en el ejercicio anterior, describe el algoritmo (en un diagrama de Warnier) para dar de alta a un entero en una lista ordenada se deben actualizar ambas referencias en cada insercin a la lista. o 5.18.- Da el algoritmo para eliminar a un elemento dado de la lista nuevamente tener cuidado con las dos referencias.

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 lenguajes o e tradicionales esto se logra copiando aquellas partes del programa que nos sirven y modicndolas para que se ajusten a nuevas situaciones. Estos mecanismos son a en el mejor de los casos muy laboriosos y en el peor 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 subclase hereda los atributos y mtodos de la superclase. Que herede quiere decir e

6.2 Arreglos

272 que, por el hecho de extender a la superclase, tiene al menos todos los atributos y mtodos de la superclase. Antes de entrar de lleno al concepto de herencia, e revisaremos a una clase de objetos muy comn en las ciencias de la computacin u o que son los arreglos, estructuras de datos lineales, homogneas, estticas y de e a acceso directo. Los necesitamos para modicar un poco a la superclase en lo que respecta a las variables estticas que estn formadas por cadenas. a a

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

6.2.1.

Arreglos de una dimensin o


Hemos tratado los nombres que corresponden a las carreras como una cadena, pero en la clase para el catlogo de carreras estn implementados en arreglos. a a Tenemos una lista con los nombres de las carreras y una lista paralela con las claves de las carreras. El nombre que corresponde a la clave en la posicin i de o la lista de claves es el que se encuentra en la posicin i en la lista de nombres. o Organizamos estas listas colocando a cada nombre de carrera en una posicin, y o en otro arreglo, en la posicin correspondiente, colocamos su clave. Es conveniente o tener ordenadas las claves pues facilita como vern ms adelante localizar una a a clave determinada. Si queremos registrar las calicaciones de las tareas entregadas por un estudiante durante el semestre, sabemos que vamos a dejar el mismo nmero de tareas u y que ste queda jo al inicio del semestre (a lo ms, una por semana, o sea 16). e a Podemos pensar en las calicaciones de cada estudiante como un vector, calif0 , calif1 , . . . , calif15 donde estamos suponiendo que cada estudiante tiene lugar para, a lo ms, 16 a calicaciones (del 0 al 15). 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

273

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 dentro de la estructura. A estas colecciones les llamamos en programacin arreglos. La declaracin de o o arreglos tiene la siguiente sintaxis: Sintaxis: xdeclaracin de arregloy ::= xtipoy[ ] xidenticadory; o ntica: Sema 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 a aparezcan en la lista, y cada elemento estar creado de acuerdo a la lista. a En 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

6.2 Arreglos

274

Extendamos los ejemplos que acabamos de dar a que se inicialicen en el momento de la declaracin: o Arreglo de enteros con cinco elementos, todos ellos enteros. Corresponden a los primeros cinco 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 Estudiante con tres elementos, el primero y tercero con el constructor por omisin y el segundo con los datos que se indican. o E s t u d i a n t e paco = new E s t u d i a n t e ( "Paco" , " 095376383 " , "Compu" , 7 ) ; 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 ( ) , paco , new E s t u d i a n t e ( ) }; Agregamos la declaracin de los nombres de las carreras (String[]) y de las o claves de las mismas (int[]). Podemos ver los dos arreglos como paralelos: al nmero de clave en la posicin i le corresponde el nombre en la misma u o posicin. o static final String [ ] sCarreras = { "C digo invalido " , o " Actuar a " , " Ciencias de la Computaci n " , o "F sica" , " Matem ticas " , a " Ciencias de la Tierra" , " Biolog a " , "Manejo Sustentable de Zonas Costeras " } ; // // // // // // // // lugar lugar lugar lugar lugar lugar lugar lugar 0 1 2 3 4 5 6 7

275

Herencia

Y para las claves de las carreras hacemos un arreglo paralelo que contenga las claves correspondientes: static final int [ ] 0, 101 , 104 , 106 , 122 , 127 , 201 , 217}; carreras // l u g a r // l u g a r // l u g a r // l u g a r // l u g a r // l u g a r // l u g a r // l u g a r = { 0 1 2 3 4 5 6 7

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 inmea a u diatamente a la izquierda o encima del rectngulo corresponde a lo que ser la a a direccin de memoria en el heap (Java nunca maneja directamente direcciones de o memoria).

Figura 6.1

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


@

Heap
1000 1004 1008 1012 1016

1000 primos

2 [0]

3 [1]

5 [2]

7 [3]

11 [4]

Figura 6.2

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


@

Heap
2124 2130 2136

2124 vector

3.14 [0]

8.7 [1]

19.0
[2]

6.2 Arreglos

276

Figura 6.3

String [ ] cadenas = { S No}; , Variables Heap


3826
@

2784

2784

cadenas

3826 [0] @ 2790 4218 [1]

Si
4218

No

Figura 6.4

Declaracin con denicin de un arreglo de objetos o o

E s t u d i a n t e [ ] e s t u d i a n t e s = { new E s t u d i a n t e ( ) , new E s t u d i a n t e ( "Paco" , " 095376383 " , c l a v e P a c o , 7 ) , new E s t u d i a n t e ( ) } ;

Variables

Heap

nombre @ 1500

null

cuenta @ 1506

carrera

null
1512

1020

1020 1026 1032

@ @ @

1500 [0] 2054 [1] 1820 [2]


nombre @ 2054

estudiantes

3200

cuenta @ 2060

carrera

4100
2066

Paco
3200

095376383
4100 nombre @ 1820 cuenta @ 1826 carrera

null

null
1832

Figura 6.5

Declaracin e inicializacin de las claves de carreras o o


int[] claves = {0, 101, 104, 106, 122, 127, 201, 217};

Variables
@

Heap
1000 1004 1008 1012 1016 1020 1024 1028

1000

0 [0]

101 104 106 122 127 201 217 [1] [2] [3] [4] [5] [6] [7]

claves

277

Herencia

Figura 6.6

Declaracin con denicin del vector de nombres de carreras o o


S t r i n g [ ] s C a r r e r a s = {"C digo inv lido " , o a " Actuar a " , " Ciencias de la Computaci n " , o "F sica" , " Matem ticas " , a " Ciencias de la Tierra" , " Biolog a " , "Manejo Sustentable de Zonas Costeras " } ;

Variables
@

Heap
1000 1008 1016 1024 1032 1040 1048 1048 @ @ @ @ @ @ @ @

3000

Cdigo invlido o a 1000 3000 [0] 4002 [1] 2184 [2] 3226 [3] 4100 [4] 2828 [5] 2500 [6] 2320 [7]
2828 3226 4002

sCarreras

Actuar a
2184

Ciencias de la Computacin o F sica


4100

Matemticas a Ciencias de la Tierra


2500

Biolog a
2320

Manejo Sustentable de Zonas Costeras

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 Sintaxis: xdeclaracin de un arreglo con tamao dadoy ::= o n xtipoy xidenticadory = new xtipoy[xexpresin enteray]; o ntica: Sema Se inicializa la referencia a un arreglo de referencias en el caso de objetos, o de datos en el caso de tipos primitivos.

6.2 Arreglos

278 Si los ejemplos que dimos antes los hubiramos hecho sin la inicializacin ser e o an:
1 2 3 4 5 6

i n t [ ] p r i m o s = new i n t [ 5 ] ; f l o a t [ ] v e c t o r = new f l o a t [ 3 ] ; 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 ] ; S t r i n g [ ] c a d e n a s = new S t r i n g [ 2 ] ; S t r i n g [ ] s C a r r e r a s = new S t r i n g [ 8 ] ; i n t [ ] c l a v e s = new i n t [ 8 ] ; y los esquemas de su organizacin en memoria quedar como se muestra en las o a guras 6.7 a 6.10. 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.7

int [ ] primos = new int[5]; Variables


@

Heap
1000 1004 1008 1012 1016

1000 primos

0 [0]

0 [1]

0 [2]

0 [3]

0 [4]

Figura 6.8

oat [ ] vector = new oat[3]; Variables


@

Heap
2124 2130 2136

2124 vector

0.0 [0]

0.0 [1]

0.0 [2]

279

Herencia

Figura 6.9

EstudianteBasico [ ] estudiantes = new EstudianteBasico[3]; Variables


@

Heap
1020 1026 1032
@ @ @

1020

null [0] null [1] null [2]

estudiantes

Figura 6.10

String [ ] cadenas = new String[2]; Variables Heap


2784 2790
@ @

2784

null [0] null [1]

cadenas

Figura 6.11

Declaracin del vector de nombres de carreras o String[ ] sCarreras = new String[8];

Variables
@

Heap
[0] [1] [2] [3] [4] [5] [6] [7] @null @null @null @null @null @null @null @null
1000 1008 1016 1024 1032 1040 1048 1048

1000

sCarreras

Para las claves de las carreras, podr amos tener la declaracin sin inicializacin o o que se muestra en la gura 6.12.

6.2 Arreglos

280

Figura 6.12

Declaracin del vector con claves de carreras o i n t [ ] c l a v e s = new i n t [ 8 ] ; Variables


@

Heap
1000 1004 1008 1012 1016 1020 1024 1028

1000

0 [0]

0 [1]

0 [2]

0 [3]

0 [4]

0 [5]

0 [6]

0 [7]

claves

Como ya mencionamos, en el caso de los elementos numricos de un arreglo, e stos se inicializan en 0. e

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; pero mientras que en el caso de los objetos se utiliza el e operador punto (.), en el caso de los arreglos de una dimensin se usa el operador o r s, cuya sintaxis es: 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 a o otra operacin (en la ausencia de parntesis). Una vez obtenido el entero o e correspondiente a la xexpresin enteray que pudiera ser una constante eno tera proceder a elegir al elemento en el arreglo con ese a ndice. 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 (1), matriz (2), u

281 cubo (3), . . . ) y el tipo de sus elementos (int, oat, Estudiante, . . . ).

Herencia

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 [ 7 ] ; La secuencia de pasos que se dan durante la ejecucin se puede observar en o la gura 6.13 en la siguiente pgina. El nmero entre parntesis a la izquierda a u e de cada sub-esquema corresponde al nmero de instruccin que se termin de u o o ejecutar. 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 9: 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 conservando el contenido, sino que se cre un n o arreglo nuevo; se perdi el acceso al anterior, por lo que se convierte en basura. 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 enumerar (podr amos utilizar tambin caracteres). e

6.2 Arreglos

282

Figura 6.13

Reasignacin de arreglos o Variables

p1q

Heap

null enteros

Variables

p3q

Heap
[0] [1] [2] [3] [4]

1260 enteros

0
1260

0
1264

0
1268

0
1272

0
1276

Variables

p8q

Heap
[0] [1] [2] [3] [4]

1260 enteros

2
1260

4
1264

6
1268

8
1272

10
1276

Variables

p9q

Heap
[0] [1] [2] [3] [4]

2580 enteros

2
1260

4
1264

6
1268

8
1272

10
1276

[0]

[1]

[2]

[3]

[4]

[5]

[6]

0
2580

0
2584

0
2588

0
2592

0
2596

0
2600

0
2604

La sintaxis general del enunciado for se da en el cuadro de la gura 6.14 en la pgina opuesta. a 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 como se muestra en el listado 6.1. o a

Cdigo 6.1 Llenado de un arreglo usando el for o


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 ; }

283

Herencia

Figura 6.14

Sintaxis del enunciado for


Sintaxis: xenunciado de iteracin enumerativay ::= o for ( xenunciado de inicializaciny ; o xexpresin booleanay ; o xlista de enunciadosy ) xenunciado simple o compuestoy ntica: Sema 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 a que no se hace nada ni al inicio del enunciado ni al nal de cada iteracin. En o el caso de una xexpresin booleanay vac se interpreta como la constante o a, true, o sea para siempre o hasta que se salga de la iteracin con un break. o Hay una pequea diferencia entre las dos versiones. En el caso del for, la n variable i es local a l mientras que en el while tuvimos que declararla fuera del e bloque. En ambos casos, sin embargo, esto se hace una unica vez, que es el paso de inicializacin. En el caso del for no se puede hacer referencia a la variable i o fuera del bloque, pues no existe fuera de l, mientras que en el caso del while la e variable i existe fuera del bloque. No hay manera de usar una variable que no ha sido declarada en la expresin booleana del while. o Otra manera de hacer esto con un for, usando dos variables enumerativas, una para i y otra para i + 1, pudiera ser como sigue:
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 , j = 1 ; i < 5 ; i ++, j ++) enteros [ i ] = j 2; }

6.2 Arreglos

284 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, y la segunda (tercera, etc.) variables deben ser del mismo tipo; el formato debe ser de una unica declaracin que puede incluir o ms de una variable del mismo tipo. Si alguna de las variables est declarada a a antes y fuera del for aparecer el mensaje de que se est repitiendo la declaracin, a a o aunque esto no es exacto. Lo que s es vlido es tener varios enunciados for, cada a uno con una 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.1
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 cdia e a o go 6.2.

Cdigo 6.2 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.2
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 1) y la segunda se calcula sumndose uno al doble de lo que lo que llevaba. El a encabezado del for ser a:

285

Herencia

Cdigo 6.3 Dos variables controlando la iteracin o o


f o r ( i n t i =1, j =1; i <= 8 && j < 2 0 0 ; i ++, j = 2 j + 1 ) System . o u t . p r i n t 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 se debe notar que cuando el tercer componente del for consiste de ms de un a enunciado, stos se separan entre s por una coma, no un punto y coma. Lo que e 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 System . o u t . p r i n t 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

286

EJemplo 6.2.3

Para el caso de las claves de carreras, si suponemos las declaraciones dadas en las guras 6.6 y 6.5, podemos obtener el nombre a partir de la clave. Tenemos que buscar la clave correspondiente y usar el lugar encontrado como ndice para los nombres de las carreras. El diagrama de Warnier se encuentra a continuacin: o

Encontrar clave ' '

$ $ # ' ' Registrar el lugar ' ' ' ' ' ' ' Buscar clave 'Es la buscada Salir del ciclo & ' ' '(hasta encontrarla ' ' ' o se acaben) ' ' ' ' ! ' ' ' ' ' ' % &
Es la buscada Pasar al siguiente Mostrar el nombre de la carrera

' ' 'Encontrada ' ' ' ' ' ' ' ' ' ' ' %

! !
Mostrar que el cdigo es invlido o a

Encontrada

La programacin de la bsqueda est en el listado 6.5. o u a

287

Herencia
CatalogoCarreras

Cdigo 6.5 Localiza el nombre de una carrera dada su clave o

p r i v a t e s t a t i c S t r i n g [ ] s C a r r e r a s = {" C digo inv lido " , o a " Actuar a " , " Ciencias de la Computaci n " , o " F sica " , " Matem ticas " , a " Ciencias de la Tierra " , " Biolog a " , " Manejo Sustentable de Zonas Costeras " } ; p r i va t e s t a t i c i n t [ ] c l a v e s = {0 , 101 , 104 , 106 , 122 , 127 , 201 , 217}; public S t r i n g daCarrera ( i n t buscada ) { int i = 0; f o r ( i = 0 ; i < c l a v e s . l e n g t h ; i ++) { i f ( c l a v e s [ i ] == b u s c a d a ) { break ; } } // Estamos ac con i a pu nt and o a l e n c o n t r a d o a // o mayor o i g u a l que e l tama o d e l a r r e g l o n i f ( i < c l a v e s . l e n g t h ) { // S a l i m o s con e l b r e a k r e t u r n " Clave : " + c l a v e s [ i ]+"\ tCarrera : " + sCarreras [ i ] . trim ( ) ; } else { r e t u r n " Clave : "+b u s c a d a+"\ tCarrera : " + sCarreras [ 0 ] . trim ( ) ; } }

6.2 Arreglos

288

EJemplo 6.2.4
Podemos tener iteraciones anidadas, como lo dice la sintaxis del enunciado. 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 6 6

7 7 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.6.

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


f o r ( i n t i = 1 ; i <= 9 ; i ++) { / r e c o r r e l o s r e n g l o n e s / f o r ( j = 1 ; j <= i ; j ++) { / r e c o r r e l a s c o l u m n a s / System . o u t . p r i n t ( j + "\t" ) ; / 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 / } System . o u t . p r i n t l n ( ) ; / 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 son las a o matrices, arreglos de dos dimensiones. Para Java los arreglos de dos dimensiones son arreglos de arreglos, los de tres dimensiones son arreglos de arreglos de arreglos y as sucesivamente. La declaracin se hace poniendo tantas parejas de corchetes o como dimensiones queramos en un arreglo, como se puede ver a continuacin. o

289 int [ ] [ ] matriz ; EstudianteBasico [ ] [ ] [ ]

Herencia

// 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 en el momento de declararlo, digamos de dos dimensiones, tendr que darle entre e llaves las inicializaciones para cada uno de los renglones: Renglones:

hkkkkkikkkkkj hkkkikkkj hkkikkj

[0]

[1]

[2]

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 es vlido, pues siendo un arreglo de arreglos, cada uno de n a los arreglos en la ultima dimensin puede tener el nmero de elementos que se o u desee. Un esquema de cmo quedar en memoria se puede ver en la gura 6.15. o an

Figura 6.15

Acomodo en memoria de un arreglo de dos dimensiones int[ ][ ] matriz = {{2,2,3,3},{3,4,5},{8}}; Variables


@

Heap
@ [0]

[0]

8
[1] @ [2]
8154

1000

3260

4210

8154

[0]

[1]

[2]

matriz

1000

1008

1016

3
4210

4
4214

5
4218

[0]

[1]

[2]

[3]

2
3260

2
3264

3
3268

3
3272

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


1 2 3 4 5 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 ] [ ] ;

6.2 Arreglos

290 Esta declaracin y deniciones, al terminar de ejecutarse la l o nea 5, queda en memoria como se muestra en la gura 6.16.

Figura 6.16

Almacenado en memoria del arreglo cubos Variables


@
(4820)

Heap
@ @
4820

[0]
(null)
8600

[1] @
(null)
8604

[2] @
(null)
8608

[3] @
(null)
8612

[4] @
(null)
8616

[5] @
(null)
8620

3024

(8600) (9300) (2456)

[0] [1] [2]

@
4824

[0] @ @
9300

[1] @
9304

[2]
9308

0 0 0 0

[0]
2740

@
4828

(5700) (7200) (2740)

[1]
2744

0 0 0 [0]
5700

[0]
7200

[1]
7204

[2]
2748

[2]
7208

[0] @
(null)
2456

[1] @
(null)
2460

0 0

[1]
5704

[2]
5708

Con la declaracin que acabamos de ver tendr o amos un arreglo de tres dimensiones; en la primera dimensin tenemos tres elementos: el primer elemento es un o arreglo de 6 arreglos, aunque no sabemos todav de qu tamao es cada uno de a e n los 6 arreglos; el segundo elemento es un arreglo de tres arreglos de tres elementos; el tercer elemento es un arreglo de dos arreglos y no sabemos cuantos elementos en cada arreglo. Podemos observar en esta gura que unicamente la ultima dimensin en este o caso la tercera corresponde a variables, inicializadas con el valor 0 para enteros. Estamos suponiendo que cada referencia ocupa 4 bytes, lo mismo que cada entero, aunque esto puede no ser preciso. Lo que se desea enfatizar es cmo se almacenan o contiguos los elementos de cada dimensin, registrando direcciones en el heap que o son contiguas 9300, 9304, 9308 por ejemplo. 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 n el tamao de alguna de las dimensiones del mismo. Para eso, la clase que con rresponde a los arreglos tiene una variable length que nos regresa el tamao del n arreglo. 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. A diferencia de las cadenas String length es un atributo, por lo que no va seguido de parntesis como en las e cadenas, donde corresponde a un mtodo. e Resumiendo, los arreglos en Java tienen las siguientes caracter sticas:

291

Herencia

(i.) 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 (ii.) Es una estructura de datos esttica: una vez dado su tamao, el arreglo no a n puede achicarse o agrandarse. (iii.) Podemos tener arreglos del nmero de dimensiones y del tipo que queramos. u (iv.) Los arreglos son estructuras de datos homogneas, porque todos sus elemene tos son del mismo tipo. (v.) Los arreglos de dos dimensiones no tienen porqu tener el mismo nmero e u de elementos en cada rengln del arreglo. Esto se extiende en todas las o dimensiones 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 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.

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.7 en la siguiente pgina, en la l a nea 20 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 43 (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

6.2 Arreglos

292 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 a u de dimensiones.

Cdigo 6.7 Arreglos como parmetros y valor de regreso de una funcin o a 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

Arreglos (1/3)

package H e r e n c i a ; import u t i l e s . Cadenas ; // Para m todos de c a d e n a s e / C l a s e <code>A r r e g l o s </code >, i m p l e m e n t a v a r i o s m todos que t i e n e n e que v e r con a r r e g l o s . En t o d o s e s t o s m todos , e l m todo no c o n o c e l o que l e e s t n e e a pasando , p o r l o que t i e n e que t r a b a j a r i n d i r e c t a m e n t e . @ a u t h o r <a h r e f = m a i l t o : e l i s a @ v i s o > E l i s a V i s o G u r o v i c h </a> @version 1.2 / public class Arreglos { / Suma d o s a r r e g l o s de d o s d i m e n s i o n e s . Los a r r e g l o s pueden t e n e r d i s t i n t o n mero de e l e m e n t o s en c u a l q u i e r a de l a s u dimensiones . @param A t i p o <code>i n t [ ] [ ] < / code >, p r i m e r a r r e g l o . @param B t i p o <code>i n t [ ] [ ] < / code >, s e g u n d o a r r e g l o . @ r e t u r n t i p o <code>i n t [ ] [ ] < / code >, un a r r e g l o con l a suma . / p u b l i c i n t [ ] [ ] suma ( i n t [ ] [ ] A , i n t [ ] [ ] B) { i n t min1 = Math . min (A . l e n g t h , B . l e n g t h ) ; // m nimo tama o n i n t max1 = Math . max (A . l e n g t h , B . l e n g t h ) ; // m ximo tama o a n i n t [ ] [ ] laSuma = new i n t [ max1 ] [ ] ; / 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 f o r ( i n t i =0; i < min1 ; i ++) laSuma [ i ] = suma (A [ i ] , B [ i ] ) ; // S i s e acab a n t e s B o f o r ( i n t i = min1 ; i < max1 ; i ++) { i f (A . l e n g t h > B . l e n g t h ) { laSuma [ i ] = suma (A [ i ] , new i n t [ A [ i ] . l e n g t h ] ) ; } else { laSuma [ i ] = suma ( new i n t [ A [ i ] . l e n g t h ] , B [ i ] ) ; } } r e t u r n laSuma ; } // f i n suma ( i n t [ ] [ ] , i n t [ ] [ ] ) / Suma d o s a r r e g l o s de una d i m e n s i n o

293

Herencia
Arreglos (2/3)

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


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 79 80 81 82 83 84 85 86 87 @param Dos a r r e g l o s de una d i m e n s i n o @ r e t u r n Un a r r e g l o con l a suma / p u b l i c i n t [ ] suma ( i n t [ ] A , i n t [ ] B) { i n t tMin = Math . min (A . l e n g t h , B . l e n g t h ) ; i n t tMax = Math . max (A . l e n g t h , B . l e n g t h ) ; i n t [ ] r e s u l t = new i n t [ tMax ] ; f o r ( i n t i = 0 ; i < tMin ; i ++) // Tamao en com n n u r e s u l t [ i ] = A[ i ] + B[ i ] ; f o r ( i n t i = tMin ; i < tMax ; i ++) // l o s que s o b r a n r e s u l t [ i ] = ( i < A. length )? A[ i ] : B[ i ] ; i f (A . l e n g t h > B . l e n g t h ) { f o r ( i n t i = tMin ; i < tMax ; i ++) r e s u l t [ i ] = A[ i ] ; } else { f o r ( i n t i = tMin ; i < tMax ; i ++) r e s u l t [ i ] = B[ i ] ; } return r e s u l t ; } // f i n suma ( i n t [ ] , i n t [ ] )

/ / / Prueba de l o s m todos de l a c l a s e / 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 ) { / Los d o s a r r e g l o s a sumar / i n t [ ] [ ] uno = { { 3 , 4 , 5 } , { 2 , 8 , 7 , 5 } , { 3 , 4 , 7 } } ; i n t [ ] [ ] dos = { { 8 , 4 , 2 , 1 } , { 8 , 7 , 2 , 3 } , { 4 , 3 , 5 , 1 } } ; / A r r e g l o p a r a g u a r d a r l a suma / i n t [ ] [ ] miSuma ; / 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 o s m a t r i c e s / o System . o u t . p r i n t l n ( "Las matrices a sumar son:" ) ; System . o u t . p r i n t l n ( " Matriz uno" ) ; f o r ( i n t i = 0 ; i < uno . l e n g t h ; i ++) { f o r ( i n t j = 0 ; j < uno [ i ] . l e n g t h ; j ++) { System . o u t . p r i n t ( Cadenas . f o r m a t o ( uno [ i ] [ j ] , ,4)+ "\t" ) ; } System . o u t . p r i n t l n ( ) ; } System . o u t . p r i n t l n ( " Matriz dos" ) ; f o r ( i n t i = 0 ; i < d o s . l e n g t h ; i ++) { f o r ( i n t j = 0 ; j < d o s [ i ] . l e n g t h ; j ++) { System . o u t . p r i n t ( Cadenas . f o r m a t o ( d o s [ i ] [ j ] , ,4)+ "\t" ) ; } System . o u t . p r i n t l n ( ) ; }

6.2 Arreglos

294
Arreglos (3/3)

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


66 67 68 69 70 71 72 73 74 75 76

miSuma = p r u e b i t a . suma ( uno , d o s ) ; / I m p r e s i n de l o s r e s u l t a d o s / o System . o u t . p r i n t l n ( "Suma de las dos matrices :\n" + " ==================================== " ) ; 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 ++) { System . o u t . p r i n t ( Cadenas . f o r m a t o ( miSuma [ i ] [ j ] , ,4)+ "\t" ) ; } // end o f f o r ( i n t j =0; j <miSuma [ i ] . l e n g t h ; j ++) System . o u t . p r i n t l n ( ) ; } // end o f f o r ( i n t i =0; i <miSuma . l e n g t h ; i ++) }

El mtodo suma en la l e nea 20 regresa un arreglo de enteros de dos dimensiones, mientras que el mtodo con el mismo nombre suma de la l e nea 43 regresa un arreglo de enteros de una dimensin. Java distingue a ambos mtodos porque el tipo de o e sus parmetros es distinto: uno tiene matrices mientras el otro tiene vectores, a ambas estructuras de enteros. 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.7 podemos ver las invocaciones a los mtodos que se llaman sue ma. La primer invocacin es en la l o nea 66 y se le pasan como argumentos los identicadores uno y dos, que estn declarados como arreglos de enteros de dos a dimensiones, lo que coincide con el tipo de los parmetros. a La otra invocacin est en la l o a nea 26, dentro del mtodo cuya rma es e suma(int[ ][ ],int[ ][ ]). En este caso le estamos pasando al mtodo dos arreglos de e enteros, 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 estamos 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. El resultado de la invocacin de esta clase se puede ver en la Figura 6.17 en la o pgina opuesta. a 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 23 se construye la primera dimensin pero la segunda no; en la l o nea 46 tambin se construye el e arreglo. Esto se debe a que en el primer caso el i-simo rengln aparece del lado e o

295

Herencia

izquierdo de una asignacin (l o nea 26) y lo mismo sucede con cada uno de los elementos en la l nea 48 para los elementos del arreglo result. Sin embargo, en la l nea 23 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 66, 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.

Figura 6.17

Ejecucin de la clase Arreglos o


Las m a t r i c e s a sumar s o n : M a t r i z uno 3 4 5 2 8 7 5 3 4 7 Matriz dos 8 4 2 1 8 7 2 3 4 3 5 1 Suma de l a s d o s m a t r i c e s : ==================================== 11 8 7 1 10 15 9 8 7 7 12 1

Por ultimo, para redondear el uso de arreglos, presentamos una clase que tie ne algunos mtodos estticos relativos a arreglos. Esta clase se encuentra en el e a listado 6.8.

6.2 Arreglos

296
Arreglos2 (1/4)

Cdigo 6.8 Ms trabajo con arreglos o a


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

package H e r e n c i a ; import u t i l e s . Cadenas ; / C l a s e <code>A r r e g l o s 2 </code >, i m p l e m e n t a v a r i o s m todos que t i e n e n e que v e r con a r r e g l o s . En t o d o s e s t o s m todos , e l m todo no c o n o c e l o que l e e s t n e e a pasando , p o r l o que t i e n e que t r a b a j a r i n d i r e c t a m e n t e . / public class Arreglos2 { / Mtodo <code>suma</code >, suma l o s e l e m e n t o s de una m a t r i z p o r e columnas .

297

Herencia
Arreglos2 (2/4)

Cdigo 6.8 Ms trabajo con arreglos o a


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 57 58 59 60 61 62

@param m a t r i z t i p o <code>i n t </code >, l a m a t r i z de d o s dimensiones . @ r e t u r n t i p o <code>i n t [] </ code> / p u b l i c s t a t i c i n t [ ] suma ( i n t [ ] [ ] m a t r i z ) { // A v e r i g u a r e l n mero m ximo de c o l u m n a s u a i n t maxCol = 0 ; f o r ( i n t i =0; i < m a t r i z . l e n g t h ; i ++) { i f ( maxCol < m a t r i z [ i ] . l e n g t h ) { maxCol = m a t r i z [ i ] . l e n g t h ; } } // Se c o n s t r u y e e l v e c t o r p a r a l o s r e s u l t a d o s i n t [ ] r e s u l t a d o = new i n t [ maxCol ] ; // s e r e c o r r e p o r r e n g l n y en cada columna s e a g r e g a e l o // c o n t e n i d o de l a columna f o r ( i n t i = 0 ; i < m a t r i z . l e n g t h ; i ++) { // Se r e c o r r e cada r e n g l n o f o r ( i n t j = 0 ; j < m a t r i z [ i ] . l e n g t h ; j ++) { // Se r e c o r r e cada columna , d e p e n d i e n d o d e l r e n g l n o r e s u l t a d o [ j ] += m a t r i z [ i ] [ j ] ; } } return r e s u l t a d o ; } / Mtodo <code>sumaPorCols </code >, r e c o r r e l a s m a t r i c e s p o r e columnas , v e r i f i c a n d o que e x i s t a e l e l e m e n t o . @param m a t r i z t i p o <code>i n t [ ] [ ] < / code >. @ r e t u r n t i p o a <code>i n t [] </ code >, l a suma p o r c o l u m n a s . / p u b l i c s t a t i c i n t [ ] sumaPorCols ( i n t [ ] [ ] m a t r i z ) { // Para d a r r e s u l t a d o , hay que s a b e r n mero de c o l u m n a s u i n t maxCol = 0 ; f o r ( i n t i =0; i < m a t r i z . l e n g t h ; i ++) { i f ( maxCol < m a t r i z [ i ] . l e n g t h ) { maxCol = m a t r i z [ i ] . l e n g t h ; } } // Se c o n s t r u y e e l v e c t o r p a r a l o s r e s u l t a d o s i n t [ ] r e s u l t a d o = new i n t [ maxCol ] ; // Se r e c o r r e p o r c o l u m n a s f o r ( i n t j = 0 ; j < maxCol ; j ++) { f o r ( i n t i = 0 ; i < m a t r i z . l e n g t h ; i ++) { i f ( j < matriz [ i ] . length ) { r e s u l t a d o [ j ] += m a t r i z [ i ] [ j ] ; } } }

6.2 Arreglos

298
Arreglos2 (3/4)

Cdigo 6.8 Ms trabajo con arreglos o a


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 104 105 106 107 108 109 110 111 112 return r e s u l t a d o ; }

/ Mtodo <code>sumaToda</code> suma a t o d o s l o s e l e m e n t o s de l a e m a t r i z . No s a b e c u a n t o s r e n g l o n e s hay n i e l numero de e l e m e n t o s en cada columna . @param m a t r i z v a l o r de t i p o <code>i n t </code> p a r a @ r e t u r n v a l o r de t i p o <code>i n t </code >. / p u b l i c s t a t i c i n t sumaToda ( i n t [ ] [ ] m a t r i z ) { i n t suma = 0 ; f o r ( i n t i = 0 ; i < m a t r i z . l e n g t h ; i ++) { f o r ( i n t j = 0 ; j < m a t r i z [ i ] . l e n g t h ; j ++) { suma += m a t r i z [ i ] [ j ] ; } } r e t u r n suma ; } / Mtodo <code>maximo</code> e n c u e n t r a e l v a l o r maximo en un e v e c t o r . No s a b e e l n mero de e l e m e n t o s en e l v e c t o r y como u pueden s e r t o d o s v a l o r e s n e g a t i v o s i n i c i a l i z a con e l p r i m e r elemento del vector . @param v e c t o r v a l o r de t i p o <code>i n t </code> p a r a @ r e t u r n v a l o r de t i p o <code>i n t </code >. / p u b l i c s t a t i c i n t maximo ( i n t [ ] v e c t o r ) { i n t maximo = v e c t o r [ 0 ] ; f o r ( i n t i = 1 ; i < v e c t o r . l e n g t h ; i ++) { i f ( maximo < v e c t o r [ i ] ) { maximo = v e c t o r [ i ] ; } } r e t u r n maximo ; } / Prueba de l o s m todos / 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 ) { int [ ] [ ] laMatriz = {{2 ,3 ,1} ,{3} ,{5 ,2 ,8 ,4} ,{6 ,5 ,4 ,3 ,2 ,1} ,{3}}; i n t [ ] sumaCols = suma ( l a M a t r i z ) ; System . o u t . p r i n t l n ( "Suma de columnas de matriz \n" + "con renglones de distintos tama~ os \n" n + "La matriz contiene :" ) ; f o r ( i n t i = 0 ; i < l a M a t r i z . l e n g t h ; i ++) { f o r ( i n t j = 0 ; j < l a M a t r i z [ i ] . l e n g t h ; j ++) { System . o u t . p r i n t ( Cadenas . r e l l e n a ( I n t e g e r . t o S t r i n g ( l a M a t r i z [ i ] [ j ] ) , i , , 4 ) +"\t" ) ; } System . o u t . p r i n t l n ( ) ;

299

Herencia
Arreglos2 (4/4)

Cdigo 6.8 Ms trabajo con arreglos o a


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 }

} System . o u t . p r i n t l n ( " =========================================\ n" + "La suma es:" ) ; f o r ( i n t j = 0 ; j < sumaCols . l e n g t h ; j ++) { System . o u t . p r i n t ( Cadenas . r e l l e n a ( I n t e g e r . t o S t r i n g ( sumaCols [ j ] ) , i , , 4 ) + "\t" ) ; } System . o u t . p r i n t l n ( ) ; System . o u t . p r i n t l n ( " Ahora la suma por columnas " ) ; sumaCols = sumaPorCols ( l a M a t r i z ) ; f o r ( i n t j = 0 ; j < sumaCols . l e n g t h ; j ++) { System . o u t . p r i n t ( Cadenas . r e l l e n a ( I n t e g e r . t o S t r i n g ( sumaCols [ j ] ) , i , , 4 ) + "\t" ) ; } System . o u t . p r i n t l n ( ) ; System . o u t . p r i n t l n ( " ================================== " + "Una matriz generada aleatoriamente :" ) ; i n t [ ] [ ] numeros = new i n t [ 5 ] [ 4 ] ; f o r ( i n t i = 0 ; i < 5 ; i ++) { f o r ( i n t j = 0 ; j < 4 ; j ++) { // A s i g n a v a l o r e s a l e a t o r i o s a numeros [ i ] [ j ] numeros [ i ] [ j ] = Math . r o u n d ( ( f l o a t ) ( Math . random ( ) 1 0 0 0 0 ) ) % 1 0 1 7 ; System . o u t . p r i n t ( Cadenas . r e l l e n a ( I n t e g e r . t o S t r i n g ( numeros [ i ] [ j ] ) , i , , 6 ) ) ; } System . o u t . p r i n t l n ( ) ; } // P r i m e r o c a l c u l a un v e c t o r de m ximos p o r r e n g l n a o i n t [ ] deLosMax = {maximo ( numeros [ 0 ] ) , // I n i c i a l i z a maximo ( numeros [ 1 ] ) , maximo ( numeros [ 2 ] ) , maximo ( numeros [ 3 ] ) , maximo ( numeros [ 4 ] ) } ; // Ahora c a l c u l a e l m ximo d e l v e c t o r de m ximos a a System . o u t . p r i n t l n ( "El maximisimo es :\t" + maximo ( deLosMax ) ) ;

En la gura 6.18 en la siguiente pgina se encuentra lo que se imprime desa de main en la clase Arreglos2, para ejemplicar el uso de arreglos pasados como

6.3 Aspectos principales de la herencia parmetros. a

300

Figura 6.18

Salida de Arreglos2
Suma de c o l u m n a s de m a t r i z con r e n g l o n e s de d i s t i n t o s tama os n La m a t r i z c o n t i e n e : 2 3 1 3 5 2 8 4 6 5 4 3 2 1 3 ========================================= La suma e s : 19 10 13 7 2 1 Ahora l a suma p o r c o l u m n a s 19 10 13 7 2 1 ================================== Una m a t r i z g e n e r a d a a l e a t o r i a m e n t e : 957 544 227 728 212 155 112 85 576 790 383 887 270 557 484 52 262 226 92 829 E l maximisimo e s : 957

6.3 Aspectos principales de la herencia


Como mencionamos antes, uno de los objetivos principales de la herencia es el poder reutilizar clases ya denidas. Generalmente nos encontramos que algo que ya est hecho casi nos sirve, excepto que le falta algn dato o algn formato de a u u salida. En el segundo cap tulo mencionamos que la herencia conforma un arbol jerrquico que, en el caso de Java que presenta herencia simple una clase o interfaz a slo puede heredar de una sola clase o interfaz superior en la jerarqu tiene o a como ra a la clase Object ver la denicin en la pgina de Java. Toda clase z o a de Java hereda directa o indirectamente de Object, pero como esto es comn a u toda clase se omite esta relacin. Se explicita unicamente la herencia directa y se o asume la l nea de herencia de la clase a la que se extiende. Si regresamos 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 compartir la an misma informacin bsica. Quitemos la clave de usuario porque esa informacin o a o

301

Herencia

no la necesitan ms que en el centro de cmputo. Procediendo como hasta ahora, a o denimos una interfaz que liste los servicios de la clase bsica, que se muestra en a el listado 6.9 en la siguiente pgina. En l encontrarn los mtodos de acceso traa e a e dicionales y otros ms que regresan los atributos, pero editados, para formar parte a de las columnas de una tabla. No consideramos necesario mostrar nuevamente la tarjeta de responsabilidades, pues es exactamente la misma que en el caso que vimos antes.

6.3 Aspectos principales de la herencia

302
ServEstBasico (1/2)

Cdigo 6.9 Interfaz para el registro bsico de Estudiante o a

10 package H e r e n c i a ; 20 / 30 I n t e r f a c e <code>S e r v E s t B a s i c o </code> d e s c r i b e l o s s e r v i c i o s 40 que d a r l a c l a s e <code>E s t u d i a n t e </code >. a 50 / 60 p u b l i c i n t e r f a c e S e r v E s t B a s i c o { 70 / 80 Mtodo <code>getNombre </code> 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 e 90 <code>nombre</code >. 100 @ r e t u r n v a l o r t i p o <code>S t r i n g </code >. 110 / 120 p u b l i c S t r i n g getNombre ( ) ; 130 140 / 150 Mtodo <code>g e t C u e n t a </code> 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 e 160 <code>c u e n t a </code >. 170 @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >. 180 / 190 public S t r i n g getCuenta ( ) ; 200 210 / 220 Mtodo <code>g e t C a r r e r a </code> 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 e 230 <code>c a r r e r a </code >. 240 @ r e t u r n v a l o r de t i p o <code>i n t </code >. 250 / 260 public int getCarrera ( ) ; 270 280 / 290 Mtodo <code>setNombre </code> a c t u a l i z a e l v a l o r d e l a t r i b u t o e 300 <code>nombre</code >. 310 @param n t i p o <code>S t r i n g </code >: nuevo v a l o r de nombre . 320 / 330 p u b l i c v o i d setNombre ( S t r i n g n ) ; 340 350 / 360 Mtodo <code>s e t C u e n t a </code> a c t u a l i z a e l v a l o r d e l a t r i b u t o e 370 <code>c u e n t a </code >. 380 @param c t i p o <code>S t r i n g </code >: nuevo v a l o r de c u e n t a . 390 / 400 public void setCuenta ( S t r i n g c ) ; 410 420 / 430 Mtodo <code>s e t C a r r e r a </code> a c t u a l i z a e l v a l o r e

303

Herencia
ServEstBasico (2/2)

Cdigo 6.9 Interfaz para el registro bsico de Estudiante o a


140 150 160 170 180 190 200 210 220 230 240 250 260 270 280 290 300 310 320 330 340 350 360 370 380 390 400 410 420 430 440 450 460 470 480 490 500 510 520 530 540 550 560 570 580 590 600 610 620 630 }

del atributo carrera . @param c t i p o <code>i n t </code >: nuevo v a l o r de c a r r e r a . / public void s e t C a r r e r a ( i n t c ) ; / Mtodo <code>daNombre</code> r e g r e s a una c a d e n a e r e l l e n a d a con b l a n c o s a l a d e r e c h a . @ r e t u r n t i p o <code>S t r i n g </code >: nombre e d i t a d o . / p u b l i c S t r i n g daNombre ( ) ; / Mtodo <code>daCuenta </code> arma una c a d e n a de tama o e n f i j o r e l l e n a n d o con c e r o s a l a i z q u i e r d a . @ r e t u r n t i p o <code>S t r i n g </code >: c u e n t a r e l l e n a d a . / p u b l i c S t r i n g daCuenta ( ) ; / Mtodo <code>d a C a r r e r a </code> r e g r e s a e l nombre de l a e c a r r e r a con un tama o f i j o , r e l l e n a d o de b l a n c o s a l a n derecha . @ r e t u r n t i p o <code>S t r i n g </code >: c a r r e r a e d i t a d a . / public String daCarrera ( ) ; / Mtodo <code>t o S t r i n g </code> c o n v i e r t e a una c a d e n a a l o b j e t o . e @ r e t u r n t i p o <code>S t r i n g </code >: c a d e n a que d e s c r i b e e l e s t a d o del objeto . / public String toString ( ) ; / Mtodo <code>daCampo</code >: r e g r e s a e l campo s o l i c i t a d o . e @param c u a l t i p o <code>i n t </code >: s e l e c t o r d e l campo a r e g r e s a r . @ r e t u r n t i p o <code>S t r i n g </code >: e l c o n t e n i d o d e l campo e d i t a d o en una c a d e n a . / p u b l i c S t r i n g daCampo ( i n t c u a l ) ; / Mtodo <code>ponCampo</code >: a c t u a l i z a e l campo s o l i c i t a d o e con l a i n f o r m a c i n p r o p o r c i o n a d a . o @param c u a l t i p o <code>i n t </code> p a r a e l e g i r campo . @param v a l o r t i p o <code>S t r i n g </code> p a r a d a r e l nuevo v a l o r . / p u b l i c v o i d ponCampo ( i n t c u a l , S t r i n g v a l o r ) ;

6.3 Aspectos principales de la herencia

304

Entre los servicios ofrecidos por esta interfaz tenemos el mtodo toString(), e que pretende armar el registro completo para impresin. Este mtodo se hereda o e de la clase Object que, por omisin, en caso de una variable primitiva la convierte o a cadena y en caso de una referencia, convierte a esta direccin tambin a cadena. o e Como toda clase extiende a Object, y lo que pretende este mtodo es convertir al e objeto en una cadena observable, lo que hacemos es redenir el mtodo para e que en todos lados donde se invoque por ejemplo, dentro del mtodo println se e use esta nueva manera de mostrar el estado del objeto en cuestin. Notarn la o a ausencia del mtodo armaRegistro() que es al que sustituye toString(). Como el e objetivo de la herencia es, precisamente, reutilizar, elegimos redenir el mtodo e toString() de la superclase Object para producir una cadena que reeje el contenido de objetos de la clase EstudianteBasico l neas 460. La clase Object es la ra del z arbol de herencia: toda clase extiende a la clase Object.

Dados estos servicios, tenemos una clase que los ofrece, EstudianteBasico, que quedar denida como se muestra en el listado 6.10 en la pgina opuesta. Omia a timos los comentarios para tener un cdigo ms uido y porque son mtodos o a e que responden a lo especicado en la interfaz. unicamente comentaremos aquellos mtodos adicionales a los presentados en la interfaz. e

305

Herencia
EstudianteBasico (1/4)

Cdigo 6.10 Superclase EstudianteBasico o


10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 210 220 230

package H e r e n c i a ; import j a v a . u t i l . S c a n n e r ; import u t i l e s . ; / C l a s e <code>E s t u d i a n t e B a s i c o </code> r e p r e s e n t a a un r e g i s t r o de l a b a s e de d a t o s , b a s a d a en 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 @version 2.0 / p u b l i c c l a s s E s t u d i a n t e B a s i c o implements S e r v E s t B a s i c o { / Para e d i c i n d e l r e g i s t r o / o public static f i n a l int TAM NMBRE = 3 6 , // Para m o s t r a r TAM CTA = 9 , // Para m o s t r a r TAM CARRERA = C a t a l o g o C a r r e r a s .TAM NOMBRE; // Para m o s t r a r / A t r i b u t o s de l a c l a s e / p r i v a t e S t r i n g nombre ; / Nombre d e l e s t u d i a n t e . / private S tr i n g cuenta ; / N mero de c u e n t a d e l e s t u d i a n t e . / u private int carrera ; / C a r r e r a que c u r s a . / / C o n s t a n t e s que i d e n t i f i c a n a l o s campos / public static f i n a l int

6.3 Aspectos principales de la herencia

306
EstudianteBasico (2/4)

Cdigo 6.10 Superclase EstudianteBasico o


240 250 260 320 330 340 460 470 480 490 500 510 520 530 610 620 630 640 650 710 720 730 810 820 830 900 910 920 1000 1010 1020 1030 1040 1050 1060 1130 1140 1150 1230 1240 1250 NOMBRE = 1 , CUENTA = 2 , CARRERA = 3 ; public EstudianteBasico () { nombre = c u e n t a = "" ; } 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 cuenta , i n t c a r r e r a ) { nombre = nmbre == n u l l ? "" : nmbre ; t h i s . c u e n t a = c u e n t a == n u l l ? "" : c u e n t a ; i f ( CatalogoCarreras . esCarrera ( carrera )) { this . carrera = carrera ; } // S i no l e ponemos nada queda en 0 } public EstudianteBasico ( EstudianteBasico copia ) nombre = c o p i a . nombre ; carrera = copia . carrera ; cuenta = copia . cuenta ; } public S t r i n g getCuenta () { return cuenta ; }

p u b l i c v o i d s e t C u e n t a ( S t r i n g newCuenta ) { t h i s . c u e n t a = newCuenta == n u l l ? "" : newCuenta ; } public int getCarrera () { return c a r r e r a ; } public void s e t C a r r e r a ( i n t newCarrera ) { i f ( CatalogoCarreras . e sCar r er a ( newCarrera )) { th is . c a r r e r a = newCarrera ; } else { this . carrera = 0; } } p u b l i c S t r i n g getNombre ( ) { r e t u r n nombre ; } p u b l i c v o i d setNombre ( S t r i n g newNombre ) { t h i s . nombre = newNombre == n u l l ? "" : newNombre ; }

307

Herencia
EstudianteBasico (3/4)

Cdigo 6.10 Superclase EstudianteBasico o


1270 1280 1290 1300 1310 1320 1330 1340 1350 1270 1280 1290 1300 1310 1320 1330 1340 1350 1360 1370 1380 1560 1570 1580 1590 1600 1610 1620 1630 1710 1720 1730 1740 1750 1760 1770 1780 1790 1800 1810 1820 1830 1840 1850 1860

/ Mtodo <code>t o S t r i n g </code >: p a r a no p e r d e r a c c e s o a l e m todo de l a s u p e r c l a s e . e @param c t i p o <code>i n t </code >: p a r a d i s t i n g u i r m todos . e @ r e t u r n t i p o <code>S t r i n g </code >: c a d e n a de l a s u p e r c l a s e . / public String toString ( int c ) { r e t u r n super . t o S t r i n g ( ) ; } / Mtodo <code>t o S t r i n g </code >, r e d e f i n e e l mismo m todo en l a e e c l a s e O b j e c t y s u s t i t u y e a l m todo a r m a R e g i s t r o d e l c a p i t u l o e anterior . @ r e t u r n t i p o <code>S t r i n g </code >. / public String toString () { S t r i n g l i n e a = daNombre ( ) + " " + daCuenta ( ) + " " + daCarrera ( ) ; return l i n e a ; } p u b l i c S t r i n g daCampo ( i n t c u a l ) { switch ( c u a l ) { case NOMBRE: r e t u r n daNombre ( ) . t r i m ( ) ; case CUENTA : r e t u r n daCuenta ( ) . t r i m ( ) ; case CARRERA : return daCarrera ( ) . trim ( ) ; default : r e t u r n " Numero de campo invalido " ; } } p u b l i c v o i d ponCampo ( i n t c u a l , S t r i n g v a l o r ) { i f ( v a l o r == n u l l ) { // Para no m a n e j a r r e f e r e n c i a s n u l a s . v a l o r = "" ; } switch ( c u a l ) { case NOMBRE: setNombre ( v a l o r ) ; break ; case CUENTA : setCuenta ( v a l o r ) ; break ; case CARRERA : int intValor = Integer . parseInt ( valor ); i f ( CatalogoCarreras . esCarrera ( intValor )) { setCarrera ( intValor ); } else { setCarrera (0); }

6.3 Aspectos principales de la herencia

308
EstudianteBasico (4/4)

Cdigo 6.10 Superclase EstudianteBasico o


1870 1880 1890 1900 1910 1990 2000 2010 2080 2090 2100 2110 2190 2200 2210 2220 2230 2240 2250 2260 2270 2390 2400 2410 2420 2430 default : break ; } }

System . e r r . p r i n t l n ( " N mero de campo inv lido " u a + cual );

p u b l i c S t r i n g daNombre ( ) { r e t u r n Cadenas . r e l l e n a ( nombre , i , , TAM NMBRE ) ; } public String daCarrera () { S t r i n g nCarre = CatalogoCarreras . daCarrera ( c a r r e r a ) ; return nCarre ; } p u b l i c S t r i n g daCuenta ( ) { S t r i n g sCuenta = S t r i n g . valueOf ( cuenta ) ; i f ( s C u e n t a . l e n g t h ( ) < TAM CTA) { r e t u r n Cadenas . r e l l e n a ( sCuenta , d , 0 , TAM CTA ) ; } else { r e t u r n s C u e n t a . s u b s t r i n g ( 0 ,TAM CTA ) ; } } p u b l i c v o i d p o n R e g i s t r o ( S t r i n g nmbre , i n t c a r r e r a , S t r i n g c u e n t a ) { nombre = nmbre == n u l l ? "" : nmbre . t r i m ( ) ; t h i s . c u e n t a = c u e n t a == n u l l ? "" : c u e n t a . t r i m ( ) ; this . setCarrera ( carrera ); }\ v s { .75 ex }

Lo unico que debemos notar es el uso del acceso protected en los atributos de esta clase. Cuando tenemos extensin de clases, como pretendemos hacer ahora, o tiene sentido hablar de este modo de acceso que hemos mencionado poco hasta ahora. 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 e como si fuera acceso pblico para las clases que extienden, pero como si fuera u privado para las clases que no extienden a la clase actual. Los datos con acceso privado no son accesibles ms que para la clase actual. El acceso protegido funa ciona como el privado para clases que no heredan y como pblico para las clases u que heredan. 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

309

Herencia

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 todo aquello declarado como protegido, pblico o de paquete. La sintaxis en Java u se muestra a 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 (noprivados), junto con todos sus mtodos. Las e xdeclaraciones de campos y mtodosy se reere a lo que se desea agregar a la e denicin de la superclase en esta subclase. Tambin colocamos ah rmas o e de mtodos que existen en la superclase pero queremos funcionen un poco e distinto en la subclase; atributos cuyo nombre aparece en la superclase y que pueden o no cambiar de tipo; y, en general, todas aquellas declaraciones cuyo nombre o rma ya aparezca en la superclase pero queremos que tenga otro signicado en la 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.11 en la siguiente pgina. a a

6.3 Aspectos principales de la herencia

310
EstudianteCalifs (1/2)

Cdigo 6.11 Encabezado para la subclase EstudianteCalifs o

10 package H e r e n c i a ; 20 import u t i l e s . Cadenas ; 30 / 40 c l a s s <code>E s t u d i a n t e C a l i f s </code >: e x t i e n d e a t i p o 50 <code>E s t u d i a n t e B a s i c o </code> p a r a p o d e r r e g i s t r a r 60 c a l i f i c a c i o n e s . 70 /

Cdigo 6.11 Encabezado para la subclase EstudianteCalifs o

EstudianteCalifs (2/2)

80 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 E s t u d i a n t e B a s i c o { / D e c l a r a c i o n e s a d i c i o n a l e s / ... / A g r e g a ra m o s l o s m todos que t i e n e n que v e r con l a s e / c a l i f i c a c i o n e s y a q u e l l o s a t r i b u t o s que hayamos / a g r e g a d o . ... 99990 }

/ / /

No es necesario especicar que la clase implementa a ServEstBasico, pues por el hecho de que extiende a EstudianteBasico, implementa a todas las interfaces que se especiquen en EstudianteBasico y a todas aquellas interfaces de las superclases. A partir de ese momento podemos suponer que EstudianteCalifs contiene todos los campos y mtodos que tiene EstudianteBasico. Los unicos mtodos que no hereda e e impl citamente son los constructores. Si en la superclase se deni expl o citamente un constructor sin parmetros o se mantuvo el que da por omisin Java siempre a o y cuando no se haya declarado ningn constructor, ste va a ser invocado siemu e pre que se invoque algn constructor de la subclase EstudianteCalifs y antes de u que se ejecute algo ms en el constructor de la subclase, a menos que como pria mer enunciado dentro del constructor se haga una llamada expl cita a alguno de los constructores de la superclase. Debemos recordar que si la superclase slo o tiene constructores con parmetros, queda oculto e inaccesible el constructor sin a parmetros, por lo que es conveniente declarar uno para uso de las subclases o a habr un mensaje del compilador de s a mbolo no declarado. Regresaremos a esto en cuanto programemos los constructores para la subclase. Cuando un mtodo de una superclase tiene un cierto acceso public, private, e protected o de paquete, si se redene ese mtodo en una subclase el acceso no e puede restringirse. Esto es, si en la superclase el mtodo ten acceso pblico, en e a u la subclase deber mantener ese mismo acceso. Si en la superclase ten acceso de a a paquete, en la subclase puede tener acceso de paquete o pblico, pero el acceso u

311

Herencia

para el mtodo o atributo que se redene no puede ser protegido ni privado. Se e puede hacer menos restringido el acceso pero no restringirlo ms. a

6.3.1.

Sper y subclases u

Tenemos ya las herramientas necesarias para hacer las declaraciones de nuestras subclases, as que proseguiremos con la construccin de un registro de estu o diante para ser usado en un curso, para lo que debemos agregar a los atributos en la subclase EstudianteCalifs la declaracin de un arreglo para guardar las cao licaciones del estudiante. Tambin desarrollaremos el constructor de la subclase e ver listado 6.12 en la siguiente pgina. Cuando se crea un objeto cuyo tipo es a una subclase, lo primero que hace el objeto es, como ya mencionamos, invocar al constructor sin parmetros de la superclase. Si no hay tal porque hayamos a declarado constructores con parmetros unicamente el constructor de la subclase a deber llamar expl a citamente a algn constructor denido en la superclase, pues u de otra manera no compilar. Esto se hace con el identicador super seguido por a la lista de argumentos entre parntesis y tiene que ser el primer enunciado en el e constructor de la subclase.

Supongamos que el nmero de calicaciones que deseamos guardar para un u estudiante dado depende del curso que est tomando. Entonces, la creacin del e o arreglo de calicaciones se har en el constructor, como se puede ver en el listaa do 6.12 en la siguiente pgina, donde codicamos dos constructores, el que trabaja a sin parmetros y el que recibe los parmetros especicados. a a

6.3 Aspectos principales de la herencia

312
EstudianteCalifs

Cdigo 6.12 Campos y constructor de EstudianteCalifs o


...

120 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 E s t u d i a n t e B a s i c o { 130 p r o t e c t e d f l o a t [ ] c a l i f s ; / A r r e g l o p a r a l a s c a l i f i c a c i o n e s / 140 150 / 160 C o n s t r u y e un nuevo e j e m p l a r de <code>E s t u d i a n t e C a l i f s </code> 170 v a co . 180 / 190 public Es tu d i a n t eC a l i f s () { 200 / En a u t o m t i c o i n v o c a a l c o n s t r u c t o r s i n p a r m e t r o s que s e a a 210 d e c l a r y d e j a a l a r e f e r e n c i a a l a r r e g l o en n u l l / o 220 } 230 240 / 250 C o n s t r u y e un nuevo e j e m p l a r de <code>E s t u d i a n t e C a l i f s </code >. 260 I n v o c a a l c o n s t r u c t o r de l a s u p e r c l a s e con l o s p a r m e t r o s a 270 que c o r r e s p o n d e n a l a s u p e r c l a s e . 280 @param nvoNombre t i p o <code>S t r i n g </code >: v a l o r p a r a e l campo 290 <code>nombre</code >. 300 @param n v a C a r r e r a t i p o <code>i n t </code >: v a l o r p a r a e l campo 310 <code>c a r r e r a </code >. 320 @param n u m C a l i f s t i p o <code>i n t </code >: v a l o r p a r a e l n mero de u 330 c a l i f i c a c i o n e s a g u a r d a r ( tama o d e l a r r e g l o ) . n 340 / 350 p u b l i c E s t u d i a n t e C a l i f s ( S t r i n g nvoNombre , S t r i n g nvaCuenta , 360 int nvaCarrera , int numCalifs ) { 370 super ( nvoNombre , nvaCuenta , n v a C a r r e r a ) ; 380 c a l i f s = new f l o a t [ n u m C a l i f s ] ; 390 }

Una vez hecho y extendido el constructor para la subclase, se agregan toda clase de mtodos que manejan a la parte de atributos que se extendi en la sube o clase. La mayor de los mtodos nuevos unicamente manipulan al nuevo campo a e y los explicaremos a continuacin del listado 6.13 en la pgina opuesta. o a

313

Herencia
EstudianteCalifs (1/3)

Cdigo 6.13 Mtodos nuevos para la subclase EstudianteCalifs o e


410 420 430 440 450 460 470 480 490 500 510 520 530 540 550 560 570 580 590 600 610 620 630 640 650 660 670 680 690 700 710 720 730 740 750 760 770 780 790 800 810 820 830

/ Mtodo <code>g e t C a l i f s </code >, r e g r e s a e l a r r e g l o con l a s e calificaciones . @ r e t u r n t i p o <code>f l o a t [] </ code >, e l a r r e g l o . / public float [ ] g e t C a l i f s () { return c a l i f s ; } / Mtodo <code>s e t C a l i f s </code >, c o p i a l a r e f e r e n c i a a un a r r e g l o e de c a l i f i c a c i o n e s . @param c a l i f s t i p o <code>f l o a t </code >, l a r e f e r e n c i a a c o p i a r . / public void s e t C a l i f s ( f l o a t [ ] c a l i f s ) { this . c a l i f s = c a l i f s ; } / Mtodo <code>c o p i a C a l i f s </code> c o p i a l a s c a l i f i c a c i o n e s , una a e una , d e l a r r e g l o dado . @param c a l i f s t i p o <code>f l o a t </code> a r r e g l o con c a l i f i c a c i on e s a copiar . / public void c o p i a C a l i f s ( f l o a t [ ] c a l i f s ) { i f ( c a l i f s == n u l l ) { this . c a l i f s = null ; return ; } t h i s . c a l i f s = new f l o a t [ c a l i f s . l e n g t h ] ; f o r ( i n t i = 0 ; i < c a l i f s . l e n g t h ; i ++) { this . c a l i f s [ i ] = c a l i f s [ i ] ; } } / Mtodo <code>c o p i a </code> r e a l i z a una c o p i a ( c l o n a d o ) de un e a r r e g l o de f l o a t de una d i m e n s i n . o @param v e c t o r t i p o <code>f l o a t </code> a r r e g l o a c o p i a r . @ r e t u r n t i p o <code>f l o a t [] </ code >, e l a r r e g l o c l o n a d o . / public static float [ ] copia ( float [ ] vector ) { i f ( v e c t o r == n u l l ) {

6.3 Aspectos principales de la herencia

314
EstudianteCalifs (2/3)

Cdigo 6.13 Mtodos nuevos para la subclase EstudianteCalifs o e


840 850 860 870 880 890 900 910 920 930 940 950 960 970 980 990 1000 1010 1020 1030 1040 1050 1060 1070 1080 1090 1100 1110 1120 1130 1140 1150 1160 1170 1180 1190 1200 1210 1220 1230 1240 1250 1260 1270 1280 1290 1300 1310 1320 1330 return n u l l ; } f l o a t [ ] v e c t C o p i a = new f l o a t [ v e c t o r . l e n g t h ] ; f o r ( i n t i = 0 ; i < v e c t o r . l e n g t h ; i ++) { vectCopia [ i ] = vector [ i ] ; } return vectCopia ; }

/ Mtodo <code>promedio </code >, o b t i e n e e l p r o m e d i o de l a s e calificaciones . @ r e t u r n t i p o <code>f l o a t </code >, e l p r o m e d i o . / public f l o a t promedio ( ) { i f ( c a l i f s == n u l l ) return 0; 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 ; } / Mtodo <code>s e t C a l i f </code> que c o l o c a una c a l i f i c a c i n . e o @param c u a l t i p o <code>f l o a t </code >, l a c a l i f i c a c i n . o @param donde t i p o <code>i n t </code >, l a p o s i c i n . o / p u b l i c v o i d s e t C a l i f ( f l o a t c u a l , i n t donde ) { i f ( c a l i f s == n u l l | | donde < 0 | | donde > c a l i f s . l e n g t h ) return ; c a l i f s [ donde ] = c u a l < 0 ? 0 : Math . min ( c u a l , 1 0 . 0 f ) ; } / Mtodo <code>g e t C a l i f </code >, o b t i e n e una c a l i f i c a c i n . e o @param donde t i p o <code>i n t </code >, p o s i c i n de c a l i f i c a c i n . o o @ r e t u r n v a l o r t i p o <code>f l o a t </code >, v a l o r de c a l i f i c a c i n o en l a p o s i c i n dada . o / p u b l i c f l o a t g e t C a l i f ( i n t donde ) { i f ( c a l i f s != n u l l && 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 t u r n 1; } / Mtodo <code>t o S t r i n g </code >, r e d e f i n e e l m todo e e <code>t o S t r i n g </code> de l a s u p e r c l a s e . Usa l o que ya t i e n e

315

Herencia
EstudianteCalifs (3/3)

Cdigo 6.13 Mtodos nuevos para la subclase EstudianteCalifs o e


1320 1330 1340 1350 1360 1370 1380 1390 1400 1410 1420 1430

de l a s u p e r c l a s e ( super ) y l e a g r e g a l a p a r t e que f a l t a . @ r e t u r n t i p o <code>S t r i n g </code >: l a c a d e n a que r e p r e s e n t a e l estado del objeto . / public String toString () { S t r i n g l i n e a = super . t o S t r i n g ( ) ; f o r ( i n t i = 0 ; i < c a l i f s . l e n g t h ; i ++) { l i n e a = l i n e a + Cadenas . f o r m a t o ( c a l i f s [ i ] , , 7 , 2 ) ; } l i n e a = l i n e a + Cadenas . f o r m a t o ( p r o m e d i o ( ) , , 5 , 2 ) ; return l i n e a ; }

Hay varios mtodos nuevos en el listado 6.13 que acabamos de mostrar y e notarn la ausencia del mtodo armaRegistro. Como el objetivo de la herencia es, a e precisamente, reutilizar, elegimos redenir el mtodo toString() de la superclase e EstudianteBasico para producir una cadena que reeje el contenido de objetos de la clase EstudianteCalifs l neas 1370 a 1430. El signicado de esto es que una vez que describa en una cadena a un EstudianteBasico, le deber pegar lo agregado en a EstudianteCalifs. Agregamos tambin los mtodos get y set para el campo nuevo. El problema e e es que el atributo califs es un objeto un arreglo por lo que los mtodos getCalifs e y setCalifs manejan la referencia, no el objeto en s Para poder generar arreglos . frescos que se reeran a otro arreglo en el heap y no al mismo, agregamos un mtodo copiaCalifs que crea espacio nuevo y copia los valores l e neas 650 a 740. Por ultimo, agregamos mtodos que tienen acceso a elementos del arreglo, tanto e para actualizar como para obtener el valor setCalif(int, oat) y getCalif(int).

6.4 Polimorsmo
Una de las grandes ventajas que nos ofrece la herencia es poder manipular subclases a travs de las superclases, sin que sepamos concretamente de cul sube a clase 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 EstudianteCalifs extendemos adecuadamente y redenimos nuevamente el mtodo toString(). Podr e amos tener el cdigo que sigue: o
10 20 30 40 EstudianteBasico [ ] estudiantes = { new E s t u d i a n t e C a l i f s ( " Pedro ,. . . ), new EstudianteBiblio (. . . ) };

6.4 Polimorsmo

316

En estas l neas declaramos un arreglo con dos elementos del tipo EstudianteBasico, donde el primero contiene a un EstudianteCalifs 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 solicitud a
S t r i n g cadena = e s t u d i a n t e s [ 0 ] . t o S t r i n g ( ) ;

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

El operador instanceof
Supongamos que estamos en la seccin escolar de la Facultad de Ciencias, o donde tienen registros de alumnos de distintos tipos; supongamos asimismo que le piden al coordinador que por favor entregue una lista de todos los registros que tiene para la biblioteca. El coordinador deber tener un mecanismo que identique a de qu clase es cada uno de los objetos que se encuentran en la lista (arreglo, e 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


que 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 se o puede observar en el listado 6.14 en la pgina opuesta suponiendo que tenemos a los registros en un arreglo de registros.

317

Herencia

Cdigo 6.14 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 d i an t 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 ] . t o S t r i n g ( ) ; } } ...

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

Cdigo 6.15 Otra versin del mtodo toString 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 d i an t e s [ i ] instanceof E s t u d i a n t e C a l i f s ) { / 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 ! Pero no hay manera de a c c e d e r a l m todo que c o r r e s p o n d e a l a e s u p e r c l a s e . Por l o t a n t o hay que armar l a c a d e n a a imprimir a pie : / r e p o r t e += e s t u d i a n t e s [ i ] . daNombre ( ) + "\t" + (( EstudianteCalifs ). estudiantes [ i ]) . promedio ( ) ; } } ...

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

6.4 Polimorsmo

318

lo que en ejecucin se deber pedir que se interprete el registro de esa manera. o a Si no lo puede hacer, porque el registro no sea de la subclase que especicamos, habr un error de ejecucin de InvalidClassException. a o 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 sper y subclase) tendremos que hacer un casting con el nombre de la subclase para u que en tiempo de compilacin identique que estamos hablando de la subclase. o Como hemos venido haciendo, y para terminar esta seccin, programamos o una prueba de la clase EstudianteCalifs en su mtodo main, que se encuentra en el e listado 6.16 en la pgina 320. Para mostrar algunos aspectos de la herencia se crean a dos arreglos, uno de la clase Object y otro de la clase EstudianteBasico. En ambos arreglos se pueden guardar objetos de cualquiera de sus subclases. En una primera ejecucin, quisimos aplicar casting con una subclase que no es la que construimos, o lo que nos da un error de ejecucin (no puede dar error en compilacin porque o o es vlido tratar de ver un objeto con la mscara de alguna de sus subclases). La a a ejecucin se encuentra en la gura 6.19 en la pgina 320, l o a neas 20 a 40.

Cdigo 6.16 Prueba de la clase EstudianteCalifs o


1470 1480 1490 1500 1510 1520 1530 1540 1550 1560 1570 1580 1590 1600 1610 1620 1630 1640 1650 1660 1670 1680 1690

EstudianteCalifs (1/2)

/ / / Prueba de l a c l a s 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 ) { E s t u d i a n t e B a s i c o [ ] miCurso = new E s t u d i a n t e B a s i c o [ 4 ] ; Object [ ] misObjetos = new O b j e c t [ 4 ] ; miCurso [ 0 ] = new E s t u d i a n t e C a l i f s ( ) ; misObjetos [ 0 ] = new O b j e c t ( ) ; ; miCurso [ 1 ] = new E s t u d i a n t e B a s i c o ( ) ; misObjetos [ 1 ] = new E s t u d i a n t e B a s i c o ( ) ; miCurso [ 2 ] = new E s t u d i a n t e C a l i f s ( " Elisa " , " 56432 " , 1 2 7 , 5 ) ; m i s O b j e t o s [ 2 ] = new E s t u d i a n t e C a l i f s ( " Elisa " , " 56432 " , 1 2 7 , 5 ) ; miCurso [ 3 ] = new E s t u d i a n t e C a l i f s ( " Jorge " , "34" , 1 0 6 , 4 ) ; m i s O b j e t o s [ 3 ] = new E s t u d i a n t e C a l i f s ( " Jorge " , "34" , 1 0 6 , 4 ) ; // ( ( E s t u d i a n t e C a l i f s ) m i s O b j e t o s [ 0 ] ) . c o p i a C a l i f s ( c a l i f s ) ; float [ ] c a l i f s = {8.7 f , 6 . 8 f , 9 . 6 f ,8 f ,7 f }; ( ( E s t u d i a n t e C a l i f s ) miCurso [ 2 ] ) . s e t C a l i f s ( c a l i f s ) ; miCurso [ 0 ] . setNombre ( "Juan" ) ; miCurso [ 0 ] . s e t C u e n t a ( "987" ) ; ( ( E s t u d i a n t e C a l i f s ) miCurso [ 0 ] ) . c o p i a C a l i f s ( ( ( E s t u d i a n t e C a l i f s ) miCurso [ 2 ] ) . g e t C a l i f s ( ) ) ; miCurso [ 1 ] . setNombre ( "Pepe" ) ; miCurso [ 1 ] . s e t C a r r e r a ( 2 0 1 ) ;

319

Herencia
EstudianteCalifs (2/2)

Cdigo 6.16 Prueba de la clase EstudianteCalifs o

1700 f o r ( i n t i= 0 ; i < miCurso . l e n g t h ; i ++) { 1710 String clase ; 1720 i f ( miCurso [ i ] i n s t a n c e o f E s t u d i a n t e C a l i f s ) { 1730 c l a s e = " EstudianteCalifs " ; 1740 } else { 1750 c l a s e = " EstudianteBasico " ; 1760 } 1770 System . o u t . p r i n t l n ( " miCurso [" + i +"], clase : " + c l a s e +"\n" 1780 + ( miCurso [ i ] . t o S t r i n g ( ) ) ) ; 1790 i f ( i < misObjetos . length ) { 1800 i f ( misObjetos [ i ] instanceof E s t u d i a n t e C a l i f s ) { 1810 c l a s e = " EstudianteCalifs " ; 1820 } else i f ( misObjetos [ i ] instanceof EstudianteBasico ) { 1830 c l a s e = " EstudianteBasico " ; 1840 } else { 1850 c l a s e = " Object " ; 1860 } 1870 System . o u t . p r i n t l n ( " misObjetos [" + i +"], clase : " 1880 + c l a s e +"\n" 1890 + ( misObjetos [ i ] . toString ( ) ) ) ; 1900 } // i f 1910 } // f o r 1920 } // main 1930 } // c l a s s

Figura 6.19

Ejecucin con la linea 1610 o

(1/2)

10 e l i s a @ l a m b d a : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $ j a v a H e r e n c i a / E s t u d i a n t e 20 C a l i f s 30 E x c e p t i o n i n t h r e a d "main" j a v a . l a n g . C l a s s C a s t E x c e p t i o n : j a v a . l a n g . O b j e c t c a n n o t be c a s t t o H e r e n c i a . E s t u d i a n t e C a l i f s 40 a t H e r e n c i a . E s t u d i a n t e C a l i f s . main ( E s t u d i a n t e C a l i f s . j a v a : 1 5 6 ) 50 60 e l i s a @ l a m b d a : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $ j a v a H e r e n c i a / E s t u d i a n t e C a l i f s 70 // E j e c u c i n con l a l i n e a 156 comentada o 80 90 100 miCurso [ 0 ] , c l a s e : E s t u d i a n t e C a l i f s 110 Juan 987000000 C o d i g o i n v a l i d o 8.70 6.80 9.60 8.00 7.00 8.02

6.5 Clases abstractas

320
(2/2)

Figura 6.19
120 130 140 150 160 170 180 190

Ejecucin con la linea 1610 o

misObjetos [ 0 ] , c l a s e : Object j a v a . l a n g . Object@1729854 miCurso [ 1 ] , c l a s e : E s t u d i a n t e B a s i c o Pepe 000000000 A c t u a r i a misObjetos [ 1 ] , c l a s e : EstudianteBasico 000000000 C o d i g o i n v a l i d o miCurso [ 2 ] , c l a s e : E s t u d i a n t e C a l i f s Elisa 564320000 C i e n c i a s de l a Computacion 8.70 6.80 9.60 8.00 7.00 8.02

200 m i s O b j e t o s [ 2 ] , c l a s e : E s t u d i a n t e C a l i f s 210 E l i s a 564320000 C i e n c i a s de l a Computacion 0.00 0.00 0.00 0.00 0.00 0.00 220 miCurso [ 3 ] , c l a s e : E s t u d i a n t e C a l i f s 230 J o r g e 340000000 F i s i c a 0.00 0.00 0.00 0.00 0.00 240 m i s O b j e t o s [ 3 ] , c l a s e : E s t u d i a n t e C a l i f s

Figura 6.20
250 J o r g e 0.00

Ejecucin con la linea 1610 comentada o


340000000 F i s i c a 0.00 0.00 0.00 0.00

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.21 en la siguiente pgina. La superclase es a a FiguraGeometrica y reconocemos que toda gura geomtrica debe tener los siguiene tes servicios: dibujarse, moverse, borrarse, crearse.

321

Herencia

Figura 6.21

Jerarqu de clases a

Figura geomtrica e

L nea

Supercie

Volumen

recta

elipse

parbola a

cubo

icosaedro

cilindro

cuadrado

a c rculo rectngulo

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 arbol realmente todo lo que podemos decir es z 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 arbol hacia e 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.21 los e nodos ovalados representan clases abstractas, mientras que los nodos rectangulares representan clases concretas. 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

6.5 Clases abstractas

322

Sintaxis: xencabezado de clase abstractay ::= abstract class xidenticadory Semantica: Le estamos indicando al compilador dos caracter sticas: 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.17 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.17 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 ( ) { ... } ... } ...

323

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. Aun cuando la e 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 como ya hemos visto, 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 viendo. Sabemos que no importa de qu sea la lista, tiene que tener un mtodo que e e

6.6 Interfaces

324 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.18 podemos ver a la declaracin de una interfaz de este tipo. o

Cdigo 6.18 Interfaz para manejar una lista o


10 20 30 40 50 60 70 80 interface 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 ( Scanner 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.18 ninguno de los mtodos tiene su implementacin. e o Cualquier clase que implemente a esta interfaz tiene que dar la implementacin de o todos y cada uno de los mtodos dados en esta declaracin. La excepcin se da si e o o tenemos una jerarqu de clases donde algunas de las clases son abstractas, entona ces se podr listar nada ms el encabezado, precedido de la palabra abstract. Por a a ejemplo, si deseamos una Lista de EstudianteCalifs podr amos tener declaraciones como las que se ven en el listado 6.19.

Cdigo 6.19 Herencia con una interfaz o


10 20 30 40 50 60 70 80 c l a s s L i s t a C u r s o implements L i s t a { E s t u d i a n t e C a l i f s 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 lugar de cualquier objeto. Sin embargo, si deseamos que el objeto sea visto como la subclase tendremos que aplicar lo que se conoce como casting,

325

Herencia

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 e a la variable:
E s t u d i a n t e C a l i f s nuevo = ( E s t u d i a n t e C a l i f s ) 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 una o referencia a un objeto de tipo EstudianteCalifs, 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, aun cuando una misma rma aparezca en ms de una e 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 pueden extender a una o ms interfaces de la misma manera a que subclases implementan a una o ms interfaces (como lo que se da en una a interfaz es unicamente rmas y constantes, no habr conicto). En caso de que a haya declaradas constantes con el mismo nombre, pero tipos distintos o valores distintos en ms de una interfaz, el compilador exigir que se discrimine de cul a a a de las interfaces se est hablando. Como las interfaces no ejecutan cdigo, ms a o a que en posibles asignaciones a constantes, se debe hacer expl cito a cul de los a varios valores nos estamos reriendo, precediendo el valor con el nombre de la interfaz en realidad, siempre que queramos usar alguna constante de una interfaz tendremos que dar el nombre completo de la constante, que incluye el nombre de la interfaz. La sintaxis es la misma que con la extensin de clases, excepto que o permite extender a ms de una interfaz. a

Sintaxis: interface xidentif1 y extends xidentif2 y, . . . , xidentifn y Semantica: De la misma manera que con las clases, la sub-interfaz hereda todas las deniciones de los mtodos de las sper-interfaces. e u En adelante se har un uso ms intenso de herencia y seguiremos, como hasta a a ahora, insistiendo en el uso de interfaces.

6. Ejercicios

326

Ejercicios
6.1.- Da un mtodo de Java, usando el enunciado for, que haga lo siguiente: e No recibe parmetros. a Regresa la matriz como resultado. Declara una matriz en Java que tenga 5 renglones y 3 columnas. En cada posicin de la matriz debers colocar la suma del nmero de o a u rengln con el nmero de columna. o u Debe imprimir la matriz por renglones.

! $ 'Inicio Construir matriz de n m ' ' ' ' $ $ ' ' ' ' ' ' & & & Matriz de n 'Llenar rengln 'Llenar columna ' o renglones por matriz[i][j] ' ' ' m columnas '(i=0,1,. . . ,n-1) ' (j=0,1,. . . ,m-1) ' % % ' ' ' ' ' ' ! ' ' %Final Regresar matriz construida

El algoritmo se muestra en el diagrama de Warnier que sigue.

i+j

6.2.- Escribe un mtodo en Java que, usando el enunciado for, construya una e matriz triangular izquierda donde en cada entrada de la matriz coloque el nmero de rengln. El mtodo debe imprimir esta matriz en la consola. El u o e mtodo recibe como parmetro el nmero de renglones. El primer rengln e a u o tiene una columna con un 1, el segundo dos con 2, y as sucesivamente. El algoritmo se muestra en el diagrama de Warnier que sigue.

# # $ ' 'Inicio ' Construir rengln Construir columna o ' ' ' (i+1) (k) ' ' ' ' # # ' ' & Matriz triangular Llenar rengln Llenar columna o triangulo[i,j] i+1 con k renglones ' (i=0,. . . ,k) (j=0,. . . ,i) ' ' ' $ $ ' ' ' & &Imprimir columna ' ' Imprimir rengln o 'Final (j=0,. . . ,i) ' ' % % (i =0,. . . ,k-1) %Cambiar de rengln o

327

Herencia

6.3.- Construye una matriz triangular de caracteres que recibe como parmetro el a nmero de columnas en el ultimo rengln (debe ser impar), y al imprimirla u o se muestre de la siguiente forma: b c c d d d e e e e a b c d e b c c d d d e e e e

En el ejemplo anterior el nmero de columnas dado como parmetro es 9, u a pero tu mtodo debe poder hacerlo con cualquier nmero impar de columnas. e u 6.4.- Escribe un mtodo en Java que recibe como parmetros dos arreglos de ene a teros de una dimensin y regresa un arreglo de enteros de una dimensin que o o consiste de la concatenacin del segundo arreglo a continuacin del primero. o o El ultimo elemento de cada uno de los arreglos es el valor que corresponde a un Integer.MIN VALUE, que el el menor valor que puede almacenar una variable de tipo int. El arreglo nuevo no debe contener ninguna celda vac a ni debe aparecer este nmero que se usa como centinela. u 6.5.- Tenemos un arreglo de una dimensin de enteros que contiene informacin o o de la siguiente manera ste es un ejemplo: e En la posicin o 0 1 2 3 4 Repeticiones del entero i 5 2 3 0 1

Escribe un mtodo que construya un arreglo de una dimensin donde apae o rezcan los elementos denotados en la segunda columna. Por ejemplo, para el arreglo anterior, el mtodo debe construir un arreglo de enteros que tenga e lo siguiente: 0 0 0 0 0 1 1 2 2 2 4 La tabla me dice que deben aparecer cinco ceros, seguidos de dos unos, seguidos de tres doses, cero treses y por ultimo un cuatro.

6. Ejercicios

328 Se debe obtener primero el nmero total de nmeros que aparecen; construir u u el arreglo correspondiente y acomodar en cada posicin el nmero de que se o u trata. 6.6.- Describe la relacin de herencia que puede haber entre veh o culos que transportan a una o ms personas por tierra. a 6.7.- Describe el arbol de herencia que puede haber entre distintos paquetes de juegos de la Guerra de las Galaxias un kit bsico trae una nave y un piloto; a conforme aumenta el precio se le van agregando cosas al kit, como estaciones de servicio, personajes, etc.. 6.8.- Tenemos la siguiente clase para denir distintas propiedades de cajas.

Cdigo 6.20 Caja.java o


10 package H e r e n c i a ; 20 p u b l i c c l a s s C a j a { 30 p r o t e c t e d double ancho ; 40 p r o t e c t e d double a l t o ; 50 p r o t e c t e d double p r o f u n d o ; 60 p u b l i c C a j a ( double ancho , double a l t o , double p r o f u n d o ) { 70 t h i s . ancho = ancho ; 80 this . alto = alto ; 90 this . profundo = profundo ; 100 } 110 p u b l i c double volumen ( ) { 120 r e t u r n ancho a l t o p r o f u n d o ; 130 } 140 public String toString () { 150 S t r i n g r e t = " Ancho = " + ancho 160 + "\ nAlto = " + a l t o 170 + "\ nProfundo = " + p r o f u n d o 180 + "\n" ; 190 return r e t ; 200 } 210 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 ) { 220 C a j a c a j a = new C a j a ( 3 . 5 , 2 . 8 , 1 . 2 ) ; 230 System . o u t . p r i n t l n ( c a j a + " Volumen = " + c a j a . volumen ( ) ) ; 240 } 250 }

Escribe una subclase PesoCaja de Caja que dena el peso de las cajas, agregando un atributo peso que dena el peso por cent metro cuadrado de la caja.

329

Herencia

6.9.- Dene una subclase de PesoCaja que calcule el costo de enviar una caja como el m nimo entre un costo base y el costo calculado como el peso de la caja por un costo por kilogramo que pese la caja. 6.10.- Tenemos la siguiente jerarqu de clases: a Figura

Circulo

Rectangulo

Cuadrado Con los siguientes esqueletos para las clases.

Cdigo 6.21 Figura.java o


10 package H e r e n c i a ; 20 a b s t r a c t c l a s s F i g u r a { 30 p r i v a t e S t r i n g nombre ; 40 50 p u b l i c F i g u r a ( S t r i n g nombreFig ) { 60 nombre = nombreFig ; 70 } 80 90 a b s t r a c t p u b l i c double a r e a ( ) ; 100 f i n a l p u b l i c boolean menorQue ( F i g u r a l a d o D e r e c h o ) 110 120 return area () < ladoDerecho . area ( ) ; 130 } 140 f i n a l public String toString () { 150 r e t u r n nombre + " con area " + a r e a ( ) ; 160 } 170 180 }

Cdigo 6.22 Circulo.java o


10 package H e r e n c i a ; 20 p u b l i c c l a s s C i r c u l o extends F i g u r a { 30 p r i v a t e double r a d i o ; 40 s t a t i c f i n a l p r i v a t e double PI = 3 . 1 4 1 5 9 2 6 5 3 5 8 9 7 9 3 ; 50 60 }

6. Ejercicios

330

Cdigo 6.23 Rectangulo.java o


10 package H e r e n c i a ; 20 p u b l i c c l a s s R e c t a n g u l o extends F i g u r a { 30 p r i v a t e double l a r g o ; 40 p r i v a t e double ancho ; 50 60 }

Cdigo 6.24 Cuadrado.java o


10 package H e r e n c i a ; 20 p u b l i c c l a s s Cuadrado extends R e c t a n g u l o 30 40 } {

a) Proporciona los constructores para cada una de las clases que extienden a Figura. b) Escribe la implementacin para el mtodo area() que calcula el rea de o e a cada gura y que es abstracto en la superclase. c) Escribe un mtodo main en la clase Circulo que pruebe que todas tus e clases que no son abstractas funcionan bien y calculan bien el rea. a

6.11.- Tenemos la clase UsoDeFiguras que construye un arreglo de objetos de la clase abstracta Figuras, una vez completada la denicin de las clases de la o jerarqu a.

331

Herencia

Cdigo 6.25 UsoDeFiguras.java o


10 package H e r e n c i a ; 20 import j a v a . u t i l . S c a n n e r ; 30 p u b l i c c l a s s U s o D e F i g u r a s { 40 public s t a t i c Scanner cons ; 50 private static Figura leeFigura () { 60 f i n a l i n t CIRCULO = 1 , 70 RECTANGULO = 2 , 80 CUADRADO = 3 ; 90 int opcion = 0; 100 double r a d ; 110 double l a r g o ; 120 double ancho ; 130 do { 140 System . o u t . p r i n t l n ( "Dame el tipo de figura :\n" 150 + "(1) C rculo \n" 160 + "(2) Rect ngulo \n" a 170 + "(3) Cuadrado \n" 180 + "\n Opci n --> " ) ; o

6. Ejercicios

332

Cdigo 6.25 UsoDeFiguras.java o


190 opcion = cons . n e x t I n t ( ) ; 200 i f ( opcion < 1 | | opcion > 3) { 210 System . o u t . p r i n t l n ( " Opci n inv lida " ) ; o a 220 } 230 } while ( opcion < 1 | | opcion > 3 ) ; 240 switch ( opcion ) { 250 case CIRCULO : 260 System . o u t . p r i n t ( "Dame el radio del c rculo --> " ) ; 270 rad = cons . nextDouble ( ) ; 280 r e t u r n new C i r c u l o ( r a d ) ; 290 case RECTANGULO : 300 System . o u t . p r i n t ( "Dame el largo y ancho del rect ngulo ,\n" a 310 + " separados entre s por un blanco --> " ) ; 320 l a r g o = cons . nextDouble ( ) ; 330 ancho = c o n s . n e x t D o u b l e ( ) ; 340 r e t u r n new R e c t a n g u l o ( l a r g o , ancho ) ; 350 case CUADRADO: 360 System . o u t . p r i n t ( "Dame el tama~ o de cada lado --> " ) ; n 370 l a r g o = cons . nextDouble ( ) ; 380 r e t u r n new Cuadrado ( l a r g o ) ; 390 d e f a u l t : r e t u r n new C i r c u l o ( 0 ) ; 400 } 410 } 420 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 ) { 430 440 c o n s = new S c a n n e r ( System . i n ) ; 450 System . o u t . p r i n t ( "Da el n mero de figuras que deseas -->" ) ; u 460 i n t num = c o n s . n e x t I n t ( ) ; 470 F i g u r a [ ] a r r e g l o = new F i g u r a [ num ] ; 480 f o r ( i n t i = 0 ; i < num ; i ++) { 490 arreglo [ i ] = leeFigura (); 500 } 510 f o r ( i n t i = 0 ; i < num ; i ++) { 520 System . o u t . p r i n t l n ( " Arreglo [" + i + "]:" 530 + arreglo [ i ]); 540 } 550 } 560 }

Vamos a ordenar las guras de acuerdo a su area. Para ello usaremos un mtodo sencillo de ordenamiento, muy ineciente pero efectivo. Damos vae rias vueltas al arreglo y en cada vuelta: Elegimos el elemento menor del arreglo. Lo copiamos, en orden en un nuevo arreglo.

333

Herencia

Recorremos los elementos del arreglo original para ocupar el lugar del elemento copiado. Al terminar (cuando no queden elementos vlidos en el arreglo original) a regresamos el arreglo nuevo. Escribe el mtodo en Java que hace este ordenamiento (puede ser esttica). e a El diagrama de Warnier para este mtodo se encuentra a continuacin. e o

$ $ ' 'posMenor 0 ' ' ' ' ' ' ' 'menor primera gura ' ' ' ' ' ' ' & ' 'Inicio Construye un arreglo nuevo ' ' ' ' ' del mismo tamao n ' ' ' ' ' 'lugarEnNuevo 0 ' ' ' ' ' ' ' ' ' % ' ' hayEnViejo tamao del arreglo n ' ' ' ' $ # $ ' ' ' ' ' ' ' ' 'actual <menor posMenor j ' ' ' ' ' ' ' ' & Ordena & ' menor gs[j] ' 'Elige menor ' Figuras ' ' ' ' ' por rea ' a ' ' ' ' 'Procesa menor '(j=0,.., ' hayEnViejo) ' 'actual <menor ! ' & % ' ' r ' (i=1...nm. de ' u ' ' elementos) 'Copia menor a arreglo nuevo ' ' ' ' ' ' ' ' ' 'Recorre elementos a ocupar lugar ' ' ' ' ' ' ' ' ' 'lugarEnNuevo ++ ' ' ' ' ' ' ' ' ' % ' ' hayEnViejo ' ' ' ' " ' ' ' ' Entrega arreglo de %Final
guras ordenado 6.12.- Agrega la gura Triangulo a la jerarqu de clases dada y escribe el cdigo a o de Java necesario para que sea tomada en cuenta en UsoDeFiguras. 6.13.- Agrega la gura Rombo a la jerarqu de clases dada y escribe el cdigo de a o Java necesario para que sea tomada en cuenta en UsoDeFiguras. Se puede agregar como subclase de rectngulo, pero calculando el area de manera a distinta. 6.14.- Agrega la gura Trapecio a la jerarqu de clases dada y escribe el cdigo a o de Java necesario para que sea tomada en cuenta en UsoDeFiguras.

Administracin de o la memoria durante ejecucin o

7.1 La pila y el heap


Revisitaremos el tema de asignacin de espacio durante la ejecucin del proo o grama, 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 bloo ques de Java y qu sucede con ella durante ejecucin. La estructura de bloques e o sintctica (esttica) tiene la forma que se ve en la gura 7.1 en la siguiente pgina. a a a Un archivo de Java puede tener una o ms clases declaradas. Hasta ahora hemos a hablado extensamente de los campos que se declaran en la clase y cmo son aco cesibles desde cualquier mtodo de la misma clase. Tambin hemos visto el papel e e que juegan los parmetros en los mtodos de las clases y que corresponde a variaa e bles 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, al e e

7.1 La pila y el heap

336

igual que los parmetros, se tiene acceso de ah viene el nombre de locales. Adia cionalmente 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

Figura 7.1

Estructura de bloques de un programa. Clase atributos


mtodo e

mtodo e bloque bloque mtodo e bloque mtodo e bloque bloque

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 visibles dentro del bloque de instrucciones. Para los mtodos estticos, como es el caso del mtodo main de las clases, e a e esto funciona un poco distinto, ya que este tipo de mtodos no tiene acceso ms e a
Sin embargo, en el caso de una variable declarada en el encabezado de una iteracin for, si o existe alguna declaracin de una variable con el mismo nombre fuera del bloque y que la precede, o el compilador dar error de sintaxis por identicador ya declarado. a
1

337

Administracin de la memoria durante ejecucin o o

que a los atributos o mtodos estticos de la misma clase y a los mtodos o e a e atributos pblicos o de paquete de las clases a las que se tenga acceso. Si se trata u de identicadores declarados como estticos, el acceso es a travs del nombre de a e la clase o de un objeto de esa clase xclasey.xidenticadory, mientras que si se trata de un atributo o mtodo de objeto (no esttico) se tiene que invocar desde e a un objeto de esa clase. Olvidndonos un poco de los mtodos estticos (de clase) a e a podemos decir que la estructura de bloques 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 embargo, no podemos ver lo que e est declarado dentro de otro mtodo o bloque. Pero qu pasa cuando el nombre a e e de una variable de clase se repite dentro de un bloque como parmetro o variable a local? En este caso decimos que la variable 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 acceso a ella, la pueda ver. a

El rango de una variable est denido estticamente, pues est dado por la a a a estructura del programa: de ver el listado podemos decir cul es el rango de una a variable dada y denirlo como pblico (global, pero atado a un objeto); local u (declarado dentro de un mtodo) o como privado para una clase. Es el compilador e el que se encarga de resolver todo lo relacionado con el rango de las variables, siguiendo para esto 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, es decir cuale a quiera de los mtodos declarados en la clase o que sean pblicos de otras clases. e u Se lleva a cabo un 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.

7.1 La pila y el heap Supongamos que tenemos la clase del listado 7.1.

338

Cdigo 7.1 Clase que ilustra el anidamiento dinmico o a


10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2;

Cualquiera (1/2)

...

30 40 50

p u b l i c v o i d A( i n t i )

...

B( i , a ) ;

...

Cdigo 7.1 Clase que ilustra el anidamiento dinmico o a


...
60 70 80 90 100 110 p u b l i c v o i d B( i n t i , i n t j ) int a = i + j ; {

Cualquiera (2/2)

...

C();

...

} public void C( ) { int k = 2 a ;

...

120 } 130 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 ) { 140 i n t m = 10 , n = 5 ; 150 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 ( ) ; 160 o b j e t o . A(m) ; 170 o b j e t o . B(m, n ) ; 180 objeto .C( ) ; 190 } 200 }

En el esquema de la gura 7.2 en la pgina opuesta tenemos los bloques en el a orden 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

339

Administracin de la memoria durante ejecucin o o

atributos como de las variables locales, depender del anidamiento lexicogrco. a a

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( )

C( )

[3]

[2] [2]

Por ejemplo, dentro del mtodo B en el que se le asigna valor a una variable e entera a l nea 70 y que es variable local a B, el valor del atributo a no se va a ver modicado. Por ello, en la llamada de la l nea 170 al mtodo B, los valores e con los que es llamado son los originales de a y b, o sea 3 y 2. Sigue un diagrama de Warnier en la gura 7.3 que nos muestra la relacin o entre las llamadas.

Figura 7.3

Secuencia de$ llamadas en el listado 7.1. 'Construccin de objeto o ' ' ' ' $ ' ' ' '. . . ' ' $ ' ' ' ' ' & ' ' 'Llamada a A(10) Llamada a B(10,3) &. . . ' ' ' ' %Llamada a C() ' ' ' ' ... & ' %. . . Secuencia de ejecucin ' o ' ' $ ' ' ' ' 'Llamada a B(3,2) &. . . ' ' ' ' %Llamada a C() ' ' ... ' ' ' ' ' 'Llamada a C() ! % ...

7.1 La pila y el heap

340

Lo que me indican los dos diagramas anteriores, es que la ejecucin del proo grama debe proseguir de la siguiente manera: 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 llamada a mtodo), asociamos un enteo o e ro a cada llamada. Esto es con el objeto de identicar los anidamientos dinmicos a los que estn denidos por la secuencia de ejecucin del programa. Este esquea o ma muestra varios aspectos importantes que tienen que ver con la ejecucin de un o 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 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 Para poder hacer esto, la ejecucin del programa se lleva a cabo en la memoo ria de la mquina, organizada sta como una pila stack en ingls, que es una a e e

341

Administracin de la memoria durante ejecucin o o

estructura de datos con 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 co loc en la pila. o

b) Respecto a su uso:

Una pila empieza siempre vac sin elementos. a, Conforme progresa la ejecucin, se van colocando elementos en la pila y o se van quitando elementos de la pila, siguiendo siempre esta regla: los elementos se colocan siempre en el tope de la pila y cuando se remueven, se hace tambin del tope de la pila. e

Veamos un esquema de una pila en la gura 7.4.

7.1 La pila y el heap

342

Figura 7.4

Esquema de una pila (stack)

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

stack o pila El tope de la pila corresponde a un apuntador o referencia que indica cul es a el siguiente lugar en el que se van a colocar datos en la pila. Suponiendo que la primera posicin a ocupar en una pila es la 0, si el tope vale 0 quiere decir que la o pila 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 instruccin a ejecutarse. El cdigo del programa se encuentra en una seccin de o o o memoria, mientras que las variables y el resultado de la ejecucin se colocan en la o pila. Los objetos se encuentran en el heap. El algoritmo para ejecutar un programa se encuentra en la gura 7.5.

Figura 7.5

Algoritmo para ejecutar un programa. $ $ 'Toma la siguiente instruccin ' o ' ' ' ' ' ' 'Obtiene los operandos ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' $ ' ' Ejecuta instrucciones 'Incrementa el contador del programa ' ' & & 'Suma Ejecuta ' ' ' (Hasta que encuentres ' ' ' programa ' ' ' ' & ' ' ' ' la de parar) 'Ejecuta la instruccin ' ' ' o ' ' ' ' 'Resta ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' % ... % %

Antes de empezar a ejecutar un programa, el sistema operativo debe cargar en la pila de ejecucin todo lo que corresponde a lo que est accesible para la o a

343

Administracin de la memoria durante ejecucin o o

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 se puede invocar un mtodo 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 mismo e o regresar al punto desde donde se hizo la invocacin, para continuar con la ejecucin o o del programa. A la posicin en la que se encuentra la llamada se le conoce como o punto de llamada e indica el punto al que debe regresar la ejecucin del programa o 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 mencionamos, se o o lleva a cabo en la pila. Cada vez que se invoca a un mtodo el programa principal e main es una funcin invocada por el sistema operativo se tiene que montar al o mtodo en la pila, anotando muy claramente a dnde debe regresar la ejecucin e o o al terminar la rutina. Al terminar la ejecucin del mtodo, se desmonta de la o e pila al mtodo. Para montar un mtodo a la pila hay que construir lo que se e e conoce como su registro de activacin, que es una tabla en la que hay lugar para o los parmetros y las variables locales del mtodo, de tal manera que durante la a e ejecucin se encuentren siempre en la parte superior de la pila. o Al invocar un mtodo (para transferirse a ejecutarlo), el sistema debe realizar e los siguientes pasos: 1. Dejar un lugar en la pila para que el mtodo coloque ah el valor que va a e regresar, si es que regresa valor. 2. Hacer la marca en la pila, copiando ah el contenido del contador del pro grama, que corresponde al punto de regreso. 3. Buscar en la pila, en el registro de activacin global, la direccin de cdigo o o o donde se encuentra denido ese mtodo. Copiar esa direccin al contador del e o programa (es donde va a iniciar la ejecucin del mtodo invocado cuando se o e termine de montarlo en la pila). 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 a la pila, en el orden en que aparece, el registro de activacin (el o ultimo es el que queda en el tope de la pila). 7. Copiar los valores de los argumentos a los lugares para los parmetros en el a registro de activacin. o

7.1 La pila y el heap

344

8. Conforme se va ejecutando el mtodo, se van colocando en la pila las variae bles y referencias a 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 la pila) y al bloque global (lo que se encuentra en la base o fondo de la pila y hasta la primera marca en la pila). En la base de la pila 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 de o e 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 1. Localizar la marca de la pila 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 y sumarle 1 (para tomar el siguiente enunciado). 3. Si el enunciado que causa la terminacin de la rutina es un return xvalory, o colocar el xvalory en el lugar inmediatamente abajo de la marca de la pila correspondiente. 4. Quitar de la pila todo lo que se encuentra a partir de la marca, incluyndola. e Se quita algo de la pila simplemente bajando el apuntador al tope de la pila 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 de la pila. 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 se lleva o a cabo directamente sobre el texto fuente. El compilador y ligador del programa producen un programa en binario (lenguaje de mquina) que se coloca en un cierto a 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 o mquina. Es suciente para nuestros propsitos manejar el programa a nivel de a o enunciado. En los esquemas de la pila que presentamos a continuacin, lo que o corresponde a valores en la pila aparecen tal cual, lo que corresponde a direcciones en el heap se preceden con una @ y lo que corresponde a posiciones de cdigo o del programa se precede con un # manejaremos el nmero del enunciado. El u contador del programa apunta a la siguiente instruccin a ejecutarse. El tope de o la pila apunta al primer lugar vac en la pila (si el tope de la pila contiene un 0, o quiere decir que no hay nadie en la pila).

345

Administracin de la memoria durante ejecucin o o

Al iniciarse la ejecucin de una clase se cargan a la pila todos los atributos y o nombres de mtodos de la clase2 , quedando la pila como se observa en la gura 7.6. e

Figura 7.6

Estado de la pila al iniciarse la ejecucin de una clase o


10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 p u b l i c v o i d A( i n t i ) { 40 B( i , a ) ; 50 } 60 p u b l i c v o i d B( i n t i , i n t j ) { 70 int a = i + j ; 80 C(); 90 } 100 public void C( ) { 110 int k = 2 a ; 120 } 130 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 ) { 140 i n t m = 10 , n = 5 ; 150 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 ( ) ; 160 o b j e t o . A(m) ; 170 o b j e t o . B(m, n ) ; 180 objeto .C( ) ; 190 } 200 }

xvoidy

main xvoidy C xvoidy B xvoidy A xinty b xinty a

#130 #100 #60 #30 2 3


d.r. Sistema Operativo clase Cualquiera

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 la pila. 2. Marca la pila para poder montar al mtodo. e 3. Localiza la direccin de main en la pila, y ve que es la direccin de cdigo o o o 130. La pila y los contadores quedan como se ve en la gura 7.6. 4. Construye el registro de activacin para main. El registro de activacin conso o truido se puede ver en la gura 7.7 en la siguiente pgina. En el registro se a va dando lugar para cada una de las declaraciones locales. En el caso de declaraciones de objetos, se colocan en la pila las referencias a los objetos que se van a localizar en el heap.
2 No ilustraremos las clases a las que tiene acceso para ahorrar espacio y porque ser prctia a camente interminable.

7.1 La pila y el heap

346

Figura 7.7

Registro de activacin para main o

Heap
xCualquieray

objeto xinty n xinty m xString r sy args

@heap 5 10 @heap

a 3

b 2

5 y 6 Se monta el registro de activacin en la pila. La pila queda como se ve en o la gura 7.8.

Figura 7.8
xCualquieray

La pila listo para iniciar la ejecucin de main. o


@heap (a=3,b=2) *5 *10 @heap (?) d.r. Sistema Operativo main #130 #100 #60 #30 2 3 d.r. Sistema Operativo clase Cualquiera
10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 p u b l i c v o i d A( i n t i ) { 40 B( i , a ) ; 50 } 60 p u b l i c v o i d B( i n t i , i n t j ) { 70 int a = i + j ; 80 C(); 90 } 100 public void C( ) { 110 int k = 2 a ; 120 } 130 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 ) { 140 i n t m = 10 , n = 5 ; 150 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 ( ) ; 160 o b j e t o . A(m) ; 170 o b j e t o . B(m, n ) ; 180 objeto .C( ) ; 190 } 200 }

objeto xinty n xinty m xString r sy args

xvoidy

main C xvoidy B xvoidy A xinty b xinty a


xvoidy

Una vez armada la pila, se procede a ejecutar la rutina. En este momento es accesible todo lo que corresponde a las variables y mtodos pblicos de e u las clases a las que se tiene acceso, a travs de los objetos construidos, y lo e que est desde la ultima marca hasta el tope de la pila. a 7. Empieza la ejecucin en el enunciado #130, con las declaraciones locales de o main ya montadas en la pila. Las l neas de cdigo 140 y 150 corresponden a las declaraciones que ya hicio mos, as que procedemos a ejecutar la l nea 160. Para ello debemos invocar

347

Administracin de la memoria durante ejecucin o o

el mtodo A del objeto objeto. Volvamos a seguir la ejecucin, en lo que se e o reere al manejo de la pila. 1 a 3: Como el mtodo no regresa valor, no dejamos un lugar en la pie la. 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.9.

Figura 7.9

La pila durante la ejecucin de main. o


direccin de o regreso: #160 A(10)
10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 40 50 60 70 80 90 100 110 120 130 200 } 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 ) {

xCualquieray

objeto xinty n xinty m xString r sy args

@heap (?) 5 10 @heap (a=3,b=2) d.r. Sistema Operativo main

...

4 a 6 Al evaluar los argumentos, tenemos la lista (10). Montamos en la pila el registro de activacin y colocamos los valores de la lista en el espacio o reservado para los argumentos. El contenido de la pila en este momento se puede ver en la gura 7.10.

Figura 7.10

La pila durante la ejecucin de A. o


xinty

xCualquieray

objeto n xinty m xString r sy args


xinty

10 direccin de o regreso: #160 A(10) @heap (a=3,b=2) 5 10 @heap (?) d.r. Sistema Operativo main

10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 p u b l i c v o i d A( i n t i ) { 40 B( i , a ) ; 50 } 60 p u b l i c v o i d B( i n t i , i n t j ) { 70 int a = i + j ; 80 C(); 90 } 100 public void C( ) { 110 int k = 2 a ; 120 } 130 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 ) {

...

200

7.1 La pila y el heap

348

8. Continuar la ejecucin del programa en la l o nea de cdigo #40, en la que o tenemos una llamada al mtodo B(i, a), por lo que nuevamente marcamos e la pila, copiamos la direccin del PC a la marca, armamos el registro de o activacin para B y lo montamos en la pila, colocamos la direccin donde o o empieza B a ejecutarse en el PC y proseguimos la ejecucin en ese punto. o En el momento inmediato anterior a que se ejecute B, la pila se presenta como se puede observar en la gura 7.11.

Figura 7.11

La pila antes de empezar a ejecutar C (dentro de B)


xinty

a xinty j xinty i

xinty

xCualquieray

objeto n xinty m
xinty

13 3 10 direccin de o regreso: #40 B(10,3) 10 direccin de o regreso: #160 A(10) @heap (a=3,b=2) 5 10

10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 p u b l i c v o i d A( i n t i ) { 40 B( i , a ) ; 50 } 60 p u b l i c v o i d B( i n t i , i n t j ) 70 int a = i + j ; 80 C(); 90 100 110 120 130 200 }

} 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 ) {

...

Al llegar a la l nea de cdigo #80 hay una llamada desde B al mtodo C, por o e lo que nuevamente se marca la pila, se actualiza el contador del programa y se monta en la pila el registro de activacin de C(). El resultado de estas o acciones se pueden ver en la gura 7.12.

Figura 7.12

La pila antes de empezar a ejecutar C desde la l nea #80.


xinty

xinty

xinty

a j xinty i

xinty

6 direccin de o regreso: #80 C() 13 3 10 direccin de o regreso: #40 B(10,3) 10

10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2;

...

90 } 100 p u b l i c v o i d C ( ) { 110 120 130 200 } 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 ) {

...

349

Administracin de la memoria durante ejecucin o o

Figura 7.13

La pila al terminar de ejecutarse C().


xinty

a xinty j xinty i

xinty

xCualquieray

objeto

13 3 10 direccin de o regreso: #40 B(10,3) 10 direccin de o regreso: #160 A(10) @heap (a=3,b=2)

10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2;

...

60 70 80 90 100 110 120 130 200

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 ) {

...
}

Se termina la ejecucin de B(10,3) en la l o nea #90, por lo que se desmonta el registro de activacin de B(10,3) de la pila, se copia la direccin de regreso o o de la marca al PC y se quita la marca de la pila, quedando la pila como se muestra en la gura 7.14.

Figura 7.14

La pila al terminar la ejecucin de B(10,3). o


xinty

xCualquieray

objeto xinty n xinty m xString[ ]y args

10 direccin de o regreso: #160 A(10) @heap (a=3,b=2) 5 10 @heap (?) d. r. Sistema Operativo main

10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 p u b l i c v o i d A( i n t i ) { 40 B( i , a ) ; 50 }

...
130 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 ) { 140 i n t m = 10 , n = 5 ; 150 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 ( ) ; 160 o b j e t o . A(m) ; 170 o b j e t o . B(m, n ) ; 180 objeto .C( ) ; 190 } 200 }

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 de la pila. Podemos observar el estado de la pila en este momento en la gura 7.15.

7.1 La pila y el heap

350

Figura 7.15

La pila al terminar la ejecucin de A(10). o


10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2;

xCualquieray

objeto @heap (a=3,b=2) xinty n 5 xinty m 10 xString r sy args @heap (?) d.r. Sistema Operativo main xvoidy main #130 xvoidy C #100 xvoidy B #60 xvoidy A #30 xinty b 2 xinty a 3 d.r. Sistema Operativo clase Cualquiera

...

60 70 80 90 100 130 140 150 160 170

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( ) {

...

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(m, n ) ;

180 objeto .C( ) ; 190 } 200 }

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

Figura 7.16

La pila en la ejecucin de B(10,5). o


xinty

a xinty j xinty i

xCualquieray

objeto xinty n xinty m xString r sy args

15 5 10 direccin de o regreso: #170 B(10,5) @heap (a=3,b=2) 5 10 @heap (?)

10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 p u b l i c v o i d A( i n t i ) { 40 B( i , a ) ; 50 } 60 p u b l i c v o i d B( i n t i , i n t j ) 70 int a = i + j ; 80 90 100 110 120 130 200 }

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 ) {

...

351

Administracin de la memoria durante ejecucin o o

Al ejecutar al mtodo B(10,5), en la l e nea 80 se invoca al mtodo C(), por lo e que el sistema hace lo conducente con la pila, quedando ste como se muestra en e la gura 7.17.

Figura 7.17

La pila al entrar a ejecutar C().


xinty

xinty

a xinty j xinty i

xCualquieray

objeto

6 direccin de o regreso: #80 C() 15 5 10 direccin de o regreso: #170 B(10,5) @heap (a=3,b=2)

10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 p u b l i c v o i d A( i n t i ) { 40 B( i , a ) ; 50 } 60 p u b l i c v o i d B( i n t i , i n t j ) 70 int a = i + j ; 80 C(); 90 } 100 public void C( ) { 110 int k = 2 a ; 120 130 200 }

} 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 ) {

...

Termina de ejecutarse C() en la l nea 90 y la pila regresa a verse como en la gura 7.16 en la pgina anterior, excepto que el PC vale ahora 7.1. Esta situacin a o se muestra en la gura 7.18.

Figura 7.18

La pila al terminar la ejecucin de C(). o


10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 p u b l i c v o i d A( i n t i ) { 40 B( i , a ) ; 50 } 60 p u b l i c v o i d B( i n t i , i n t j ) 70 int a = i + j ; 80 C(); 90 130 200 } }... 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 ) {

xinty

a xinty j xinty i

xCualquieray

objeto

15 5 10 direccin de o regreso: #170 B(10,5) @heap (a=3,b=2)

...

Al continuar la ejecucin el programa, llega al nal del mtodo B(10,5) y sale o e de l, dejando la pila como se ve en la gura 7.19, con el PC apuntando a la e direccin de cdigo 180. o o

7.1 La pila y el heap

352

Figura 7.19

La pila al terminar la ejecucin de B(10,5). o


60 70 80 90 100 110 120 130 140 150 160 170 180 190 } 200 } 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(m, n ) ; objeto .C( ) ;

xCualquieray

objeto n xinty m xString[ ] y args


xinty

@heap (a=3,b=2) 5 10 @heap (?) d. r. Sistema Operativo main

En la l nea 180 nuevamente se hace una llamada al mtodo C(), por lo que se e marca la pila y se monta su registro de activacin. El resultado se puede ver en la o gura 7.20 en la pgina opuesta. a

Figura 7.20

La pila antes de empezar la ejecucin de C(). o


60 70 80 90 100 110 public void int a = i C(); } public void int k = 2 B( i n t i , i n t j ) + j; C() { a; {

xinty

xCualquieray

objeto xinty n xinty m xString r sy args

xvoidy

main

6 direccin de o regreso: #180 C() @heap (a=3,b=2) 5 10 @heap (?) d.r. Sistema Operativo main #130

120 } 130 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 ) { 140 i n t m = 10 , n = 5 ; 150 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 ( ) ; 160 o b j e t o . A(m) ; 170 o b j e t o . B(m, n ) ; 180 objeto .C( ) ; 190 } 200 }

Al terminarse de ejecutar C() se desmonta su registro de activacin de la pila, o se copia la direccin de regreso de la marca al PC y se quita la marca. la pila o

353

Administracin de la memoria durante ejecucin o o

queda como se muestra en la gura 7.21, con el PC apuntando a la l nea 190 del cdigo. o

Figura 7.21

La pila lista para terminar la ejecucin de main. o


10 c l a s s C u a l q u i e r a { 20 private int a = 3 , b = 2; 30 p u b l i c v o i d A( i n t i ) { 40 B( i , a ) ; 50 } 60 p u b l i c v o i d B( i n t i , i n t j ) { 70 int a = i + j ; 80 C(); 90 } 100 public void C( ) { 110 int k = 2 a ; 120 } 130 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 ) { 140 i n t m = 10 , n = 5 ; 150 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 ( ) ; 160 o b j e t o . A(m) ; 170 o b j e t o . B(m, n ) ; 180 objeto .C( ) ; 190 } 200 }

xCualquieray

objeto n xinty m xString r sy args


xinty

xvoidy

main

@heap (a=3,b=2) 5 10 @heap (?) d.r. Sistema Operativo main #130

Como la l nea 190 es la que termina main, se descarga de la pila 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 la pila 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 desde la celda inmea diatamente abajo de la ultima marca que se coloc si es que el mtodo regresa o e algn valor, ah lo va a colocar hasta la celda que se encuentre en el tope de la u pila. De esta manera, cada mtodo crea su propio ambiente de ejecucin. e o Resumiendo, la pila 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 la pila. Cada vez que se sale de un mtodo, o e se quita de la pila 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 ese ejemplar o u o o instance del mtodo. e Durante la ejecucin de un programa el sistema trabaja con dos variables, el o tope de la pila, 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

7.1 La pila y el heap

354

(o referencias), pues el tope de la pila apunta a una celda en la pila (contiene una direccin de la pila) y el contador del programa apunta a una direccin de o o memoria del programa donde se encuentra almacenado el cdigo del programa. o En la pila 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 (referencias a ellas). Apuntadores a todos los mtodos miembros de clases. e Los resultados que entregan las funciones. Los argumentos (parmetros reales) de cada mtodo. a e 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 la pila, 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 o vida de una variable: Se reere a los momentos durante la ejecucin en que una variable conserva su valor. Una variable declarada existe o mientras se encuentre en la pila. Deja de existir cuando se quita de la pila 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 la pila, pero busca unicamente en los o a registros de activacin vivos (despiertos), el global y el local, primero en el local. o Por ello, al declarar una variable local repitiendo un nombre global se crea un ejemplar fresco y nuevo, 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 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 ya que los atributos de una clase pertenecen a un ambiente global de ejecucin pero no existe ningn mecanismo para acceder a variables locales o u de mtodos que hayan intervenido en la secuencia de llamadas. Debo insistir en e

355

Administracin de la memoria durante ejecucin o o

que el bloque o registro de activacin en el que se encuentra la variable debe ser o visible desde el punto de ejecucin y unicamente se aplica a variables que hayan o sido ocultadas por una reutilizacin del nombre. Si la variable se encuentra en un o registro de activacin inaccesible, entonces el compilador emitir un mensaje de o a error.

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 o de la serie de Fibonacci:

$ '1 ' & n!  ' ' %

si n

1 1

n pn 1q! si n

$ '1 ' & F ibn  ' ' %

si n si n

2 2

F ibn1 F ibn2

Si escribimos un mtodo en Java que reeje esta denicin de factorial, por e o ejemplo, tendr amos la implementacin que se muestra en el listado 7.2. o

Cdigo 7.2 Codicacin en Java de la funcin factorial o o o


long f a c t o r i a l ( i n t n ) { i f ( n <= 1 ) { return 1; } else { r e t u r n ( n f a c t o r i a l ( n 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 varios valores para n separados e u e entre s por uno o ms blancos, donde cada uno de ellos debe ser un valor entero. a Se invoca al mtodo factorial con cada uno de estos valores que se encuentran en e el arreglo de cadenas args que se le proporciona a main. Si el entero es mayor que 1 procede el mtodo a llamarse nuevamente a s mismo, pero disminuyendo en 1 e al argumento. Si el argumento vale 1 el mtodo logra salir. Veamos la ejecucin de e o

7.2 Recursividad

356

este mtodo dentro de una clase en el listado 7.3 y observemos el comportamiento e de la pila durante su ejecucin. o

Cdigo 7.3 La funcin factorial o o


10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) { 30 i f ( n <= 1 ) { 40 return 1; 50 } 60 else { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ; 80 } 90 } 100 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 ) { 110 f o r ( i n t i = 0 ; i < a r g s . l e n g t h ; i ++) { 120 int n = Integer . parseInt ( args [ i ] ) ; 130 i f ( n <= 0 ) { 140 System . o u t . p r i n t l n ( " Factorial no puede tomar un " 150 + " argumento negativo " ) ; 160 continue ; 170 } 180 System . o u t . p r i n t l n ( n + "! es="+ f a c t o r i a l ( n ) ) ; 190 } 200 } 210 }

La pila, una vez que se carg el registro de activacin de main, se muestra en o o la gura 7.22 (usaremos la misma notacin para anotar a los elementos en la pila o que usamos en el ejemplo anterior). Numeramos las l neas del programa para poder hacer referencia a ellas en la ejecucin, que empieza en la l o nea 100. En las l neas 110 y 120 tenemos las declaraciones e inicializaciones de variables locales a main. Entramos a una iteracin que o va a usar el arreglo args que usamos en la invocacin de Factorial. En la l o nea 110, una vez vericado que el argumento es vlido, est la llamada a factorial desde a a main, donde se pide imprimir el resultado. La primera llamada de factorial desde main deja la pila como se ve en la gura 7.23 en la pgina opuesta a Se ejecuta la condicional de la l nea 30 pero como n es mayor que 1 no se hace nada. Despus se evala la condicional de la l e u nea 60 y como es verdadera se ejecuta el enunciado en las l neas 70 y 80, que es una llamada recursiva a factorial. Vuelve a entrar a ejecutar factorial y la pila se ve como en la gura 7.24 en la pgina 358. a

357

Administracin de la memoria durante ejecucin o o

Figura 7.22

Estado de la pila al iniciarse la ejecucin de una clase. o


10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) { 30 i f ( n <= 1 ) { 40 return 1; 50 } 60 else { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ; 80 } 90 } 100 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 ) {

main xlongy factorial

xvoidy

d.r. Sistema Operativo main [4] #100 #20 d.r. Sistema Operativo clase Factorial

110 f o r ( i n t i = 0 ; i < a r g s . l e n g t h ; i ++) { 120 int n = Integer . parseInt ( args [ i ] ) ; 130 i f ( n <= 0 ) { 140 System . o u t . p r i n t l n ( " Factorial no puede " 150 + " tomar un argumento negativo " ) ; 160 continue ; 170 } 180 System . o u t . p r i n t l n ( n+"!="+ f a c t o r i a l ( n ) ) ; 190 } 200 } 210 }

Figura 7.23

Estado de la pila al iniciarse la llamada de factorial(4) desde main.

xinty n xlongy val regreso xinty n xinty i xvoidy main xlongy factorial

4
direccin de o regreso #110 factorial(4)

4 0
d.r. Sistema Operativo main [4]

10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) { 30 i f ( n <= 1 ) { 40 return 1; 50 } 60 else { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ; 80 } 90 } 100 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 ) { 210 }

...

#100 #20
d.r. Sistema Operativo clase Factorial

7.2 Recursividad

358

Figura 7.24

Estado de la pila al iniciarse la llamada de factorial(3) desde factorial(4).

xinty n xlongy val regreso xinty n xlongy val regreso xinty n xinty i xvoidy main xlongy factorial

3
direccin de o regreso #70 factorial(3)

4
direccin de o regreso #110 factorial(4)

10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) 30 i f ( n <= 1 ) { 40 return 1; 50 } 60 e l s e { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ;

4 0
d.r. Sistema Operativo main [4]

#100 #20
d.r. Sistema Operativo clase Factorial

80 } 90 } 100 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 ) { 110 f o r ( i n t i = 0 ; i < a r g s . l e n g t h ; i ++) { 120 int n = Integer . parseInt ( args [ i ] ) ; 130 i f ( n <= 0 ) { 140 System . o u t . p r i n t l n ( " Factorial no puede " 150 + " tomar un argumento negativo " ) ; 160 continue ; 170 } 180 System . o u t . p r i n t l n ( n+"!="+ f a c t o r i a l ( n ) ) ; 190 } 200 } 210 }

Nuevamente se evala a falso la primera condicin y como se evala a verdadero u o u la condicin en la l o nea 60 volvemos a llamar a factorial desde la l nea 70, quedando la pila como se puede apreciar en la gura 7.25 en la pgina opuesta. a Como n sigue siendo mayor que 1 volvemos a llamar a factorial con 1 como argumento. la pila se ve como se muestra en la gura 7.26 en la pgina opuesta. a En esta llamada las condicionales de las l neas 30 y 60 ambas se evalan a u falso, por lo que se ejecuta el enunciado de la l nea 140, y se regresa el valor 1. Esto se traduce en colocar en el espacio reservado para ello cerca del tope de la pila ese valor, quitar del tope de la pila el registro de activacin de la llamada de o factorial(1) y continuar la ejecucin en la l o nea 80 para hacer la multiplicacin. la o pila se ve como se muestra en la gura 7.27 en la pgina 360. a En este punto se puede terminar de ejecutar la invocacin de factorial(2), por lo o que nuevamente se hace la multiplicacin y se coloca el resultado inmediatamente o abajo de la ultima marca en la pila; se procede a desmontar el registro de activacin o de factorial(2). la pila se muestra en la gura 7.28 en la pgina 360. a

359

Administracin de la memoria durante ejecucin o o

Figura 7.25

Estado de la pila al iniciarse la llamada de factorial(2) desde factorial(3).

xinty n xlongy val regreso xinty n xlongy val regreso xinty n xlongy val regreso

2
direccin de o regreso #70 factorial(2)
10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) 30 i f ( n <= 1 ) { 40 return 1; 50 } 60 e l s e { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ; 80 90 100 210 } {

3
direccin de o regreso #70 factorial(3)

4
direccin de o regreso #110 factorial(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 ) {

...

Figura 7.26

Estado de la pila al iniciarse la llamada de factorial(1) desde factorial(2)

xinty n xlongy val regreso xinty n xlongy val regreso xinty n xlongy val regreso xinty n

1
direccin de o regreso #70 factorial(1)

10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) 30 i f ( n <= 1 ) { 40 return 1;

2
direccin de o regreso #70 factorial(2)

3
direccin de o regreso #70 factorial(3)

4
direccin de o regreso #110 factorial(4)

50 } 60 e l s e { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ; \ 80 } 90 } 100 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 ) { 110 f o r ( i n t i = 0 ; i < a r g s . l e n g t h ; i ++) { 120 int n = Integer . parseInt ( args [ i ] ) ; 130 i f ( n <= 0 ) { 140 System . o u t . p r i n t l n ( " Factorial no puede " 150 + " tomar un argumento negativo " ) ; 160 continue ; 170 } 180 System . o u t . p r i n t l n ( n+"!="+ f a c t o r i a l ( n ) ) ; 190 } 200 } 210 }

7.2 Recursividad

360

Figura 7.27

Estado de la pila al terminarse la llamada de factorial(1).


10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) 30 i f ( n <= 1 ) { 40 return 1; 50 } 60 e l s e { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ; {

xlongy val regreso xinty n xlongy val regreso xinty n xlongy val regreso xinty n

1 2
direccin de o regreso #70 factorial(2)

3
direccin de o regreso #70 factorial(3)

4
direccin de o regreso #110 factorial(4)

80 } 90 } 100 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 ) { 110 f o r ( i n t i = 0 ; i < a r g s . l e n g t h ; i ++) { 120 int n = Integer . parseInt ( args [ i ] ) ; 130 i f ( n <= 0 ) { 140 System . o u t . p r i n t l n ( " Factorial no puede " 150 + " tomar un argumento negativo " ) ; 160 continue ; 170 } 180 System . o u t . p r i n t l n ( n+"!="+ f a c t o r i a l ( n ) ) ; 190 } 200 } 210 }

Figura 7.28

Estado de la pila al terminarse la llamada de factorial(2).


122 3
direccin de o regreso #70 factorial(3)
10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) 30 i f ( n <= 1 ) { 40 return 1; 50 } 60 e l s e { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ; {

xlongy val regreso xinty n xlongy val regreso xinty n xlongy val regreso xinty n xinty i

4
direccin de o regreso #110 factorial(4)

4 0
d.r. Sistema Operativo main [4]

80 } 90 } 100 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 ) { 110 f o r ( i n t i = 0 ; i < a r g s . l e n g t h ; i ++) { 120 int n = Integer . parseInt ( args [ i ] ) ; 130 i f ( n <= 0 ) { 140 System . o u t . p r i n t l n ( " Factorial no puede " 150 + " tomar un argumento negativo " ) ; 160 continue ; 170 } 180 System . o u t . p r i n t l n ( n+"!="+ f a c t o r i a l ( n ) ) ; 190 } 200 } 210 }

361

Administracin de la memoria durante ejecucin o o

Se sale de la llamada de factorial(3), acomodando el resultado que se obtiene de multiplicar el resultado entregado por factorial(2) y el argumento con valor 3. la pila se puede ver en la gura 7.29.

Figura 7.29

Estado de la pila al terminarse la llamada de factorial(3).


326 4
direccin de o regreso #110 factorial(4)

xlongy val regreso xinty n xlongy val regreso xinty n xinty i xvoidy main xlongy factorial

4 0
d.r. Sistema Operativo main [4]

10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) 30 i f ( n <= 1 ) { 40 return 1; 50 } 60 e l s e { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ; 80 90 100 210 }

} } 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 ) {

...

#100 #20
d.r. Sistema Operativo clase Factorial

Nuevamente, al regresar con el valor de factorial(3) en la l nea 70, lo multiplica por 4, usando para ello el valor que coloc la ejecucin de factorial(3) en la pila. o o Despus de quitar el registro de activacin y la marca de factorial(3) la pila se ve e o como se muestra en la gura 7.30.

Figura 7.30

Estado de la pila al terminarse la llamada de factorial desde main.


10 c l a s s F a c t o r i a l { 20 p u b l i c s t a t i c long f a c t o r i a l ( i n t n ) { 30 i f ( n <= 1 ) { 40 return 1; 50 } 60 e l s e { 70 r e t u r n ( n f a c t o r i a l ( n 1 ) ) ; 80 } 90 } 100 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 ) {

xlongy val regreso xinty n xinty i xvoidy main xlongy factorial

6 4  24 4 0
d.r. Sistema Operativo main [4]

#100 #20
d.r. Sistema Operativo clase Factorial

...

110 120 } 130 } 140 }

System . o u t . p r i n t l n ( n+"!="+ f a c t o r i a l ( n ) ) ;

7.2 Recursividad

362

En este momento se procede a escribir el valor de factorial(4) que fue entregado y colocado en la pila. Se termina de ejecutar el programa, y de manera similar a mtodos no recursivos, se libera la pila y el contador del programa. e Debemos insistir en que usamos la funcin factorial para mostrar la situacin o o en la pila porque es fcil de mostrar los cambios que va sufriendo la misma, 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 de la pila. o El mtodo quedar codicado como se ve en el listado 7.4. e a

Cdigo 7.4 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 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 ; } {

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 lo 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 en pasar los n discos a un segundo palo, moviendo disco por disco; cuando se mueve un disco de un poste a otro est prohia bido que quede un disco encima de otro que es menor que l. e En la gura 7.31 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.32.

363

Administracin de la memoria durante ejecucin o o

Figura 7.31

Juego de las torres de Hanoi

Figura 7.32

Estrategia recursiva para las torres de Hanoi $ $ 'Mover 1 cha del poste 1 al 3 ' ' ' & ' ' ' 'si n  2 Mover 1 cha del poste 1 al 2 ' ' ' ' ' ' %Mover 1 cha del poste 3 al 2 ' ' Mover n chas & del poste 1 al poste 2 $ ' ' ' usando el poste 3 'Mover n 1 chas del poste 1 al 3 ' ' ' & ' ' 'si n 2 Mover 1 cha del poste 1 al 2 ' ' ' ' ' ' % %
Mover n 1 chas del poste 3 al 2 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 decrementando la n en 1 hasta que tengamos que mover una sola cha; en cuanto

7.2 Recursividad

364

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.5. a

Cdigo 7.5 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 ) ; } }

Hanoi

Podemos hacer el ejercicio con 4 chas, llamando al procedimiento con Veamos en la gura 7.33 los anidamientos que se hacen. Debe ser claro que 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.34 a 7.41 cul es el trabajo realizado en las llamadas a mueveUno. a 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
mueveN(4,1,2,3).

365 hasta arriba.

Administracin de la memoria durante ejecucin o o

7.2 Recursividad

366

Figura 7.33

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 */

367

Administracin de la memoria durante ejecucin o o

Figura 7.34

Situacin de las chas antes de la llamada o

Figura 7.35

Movimientos /* 1 */ al /* 3 */
/* 1 */ /* 2 */ /* 3 */

Figura 7.36

Movimiento /* 4 */
/* 4 */

7.2 Recursividad

368

Figura 7.37

Movimientos /* 5 */ al /* 7 */
/* 7 */ /* 6 */ /* 5 */

Figura 7.38

Movimiento /* 8 */
/* 8 */

Figura 7.39

Movimientos /* 9 */ al /* 11 */
/* 10 */

/* 11 */

/* 9 */

369

Administracin de la memoria durante ejecucin o o

Figura 7.40

Movimiento /* 12 */
/* 12 */

Figura 7.41

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 a u Mergesort y el Quicksort. Con esto damos por terminado una descripcin somera sobre cmo se comporta o o la memoria durante la ejecucin de un programa, en particular la pila de ejecucin. o o Esta descripcin no pretende ser exhaustiva, sino unicamente proporcionar una o idea de cmo identica la ejecucin los puntos de entrada, de regreso y parmetros o o a a una funcin. o En el cap tulo que sigue veremos la aplicacin de recursividad a la solucin o o del problema de la base de datos que hemos estado manejando, mxime cuando a tenemos una denicin de listas dada de manera recursiva. o

7. Ejercicios

370

Ejercicios
7.1.- Para la serie de Fibonacci, denida para los nmeros naturales de la siguiente u manera: F ibonaccipnq

n2 F ibonaccipn 2q F ibonaccipn 1q Si n 2
1 Si 1

(a) Escribe el pseudocdigo para esta funcin recursiva. o o (b) Cuntas veces entra la aplicacin a la funcin Fibonacci(int n) cuando se a o o la invoca con Fibonacci(4) ? (c) Escribe el cdigo para la funcin programada iterativamente. o o (d) Cuantos pasos se llevan a cabo. contando el incremento del ndice, las sumas y las copias de un valor a otro? 7.2.- Tenemos el siguiente pedazo de cdigo: o
10 package c a p i t 7 ; 20 c l a s s E j e r c i c i o 7 2 { 30 private int caja = 25; 40 public int getCaja () { 50 return caja ; 60 } 70 public void e l E j e r c i c i o ( i n t k ) { 80 k = 50; 90 c a j a = 1 0 ; 100 } 110 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 ) { 120 E j e r c i c i o 7 2 nuevo = new E j e r c i c i o 7 2 ( ) ; 130 i n t c a j a = nuevo . g e t C a j a ( ) ; 140 nuevo . e l E j e r c i c i o ( c a j a ) ; 150 System . o u t . p r i n t l n ( "El valor de caja es: " + c a j a ) ; 160 System . o u t . p r i n t l n ( "El segundo valor de caja es: " 170 + nuevo . g e t C a j a ( ) ) ; 180 } 190 }

(a) Qu escribe la aplicacin en la l e o nea 150? (b) Qu escribe la aplicacin en las l e o neas 160 u 170?

371

Administracin de la memoria durante ejecucin o o

(c) Qu habr que hacer para que el atributo caja, desde main, tomara el e a valor 31? (Hay varias formas de lograrlo). 7.3.- Tenemos el registro del grupo en una lista ligada (puedes usar las deniciones dadas en al cap tulo 5). Programa, usando Java, recursivamente el mtodo e que lista el grupo completo. La funcin de listado est denida de la siguiente o a manera:

$ # ' textoGrupo Grupo: grupo ' 'Inicio ' ' ' ' ' $lista lista del grupo ! ' ' ' 'lista vac Regresa la cadena vac ' a ' a ' ' ' ' ' ' & ' ' Listado $ & del grupo 'Procesa lista ' 'Regresa textoGrupo ' & ' ' ' ' 'lista vac ' + primero de la lista ' ' a ' ' ' ' ' + Procesa lista con ' ' % % ' ' + el resto de la lista ' ' ' %Final

7.4.- Para esa misma lista, programa recursivamente la bsqueda de un estudianu te. La funcin de bsqueda se encuentra a continuacin: o u o

" $ 'lista vac Reporta ' a ' ' no encontrado ' ' ' ' ' $ & ! Busca estudiante 'Es el primero Reprtalo ' o en lista ' ' & ' ' 'lista vac ' a ' ' ! ' ' ' ' 'Es el primero Busca estudiante % % en resto de la lista
7.5.- Programa de manera recursiva la inversin de una cadena (Si la cadena es o abcd, su inversin es dcba). o 7.6.- Programa recursivamente cmo obtener los distintos patrones de bits con o cuatro posiciones. 7.7.- Programa recursivamente cmo mezclar dos listas de enteros, cada lista oro denada, de tal manera que el resultado sea una lista ordenada.

7. Ejercicios

372 7.8.- Haz un mtodo que calcule el mximo comn divisor (mcd) con la siguiente e a u frmula recursiva: o mcdpa, bq

b  mcdpb, a

0 % bq si a 0
si a

7.9.- Disea, implementa y prueba un mtodo recursivo que eleva un entero pon e sitivo dado a una potencia entera positiva. (Nota: tanto la base como la potencia deben ser parmetros). a 7.10.- Disea, implementa y prueba un mtodo recursivo que recibe un enten e ro positivo como parmetro y regresa una cadena representando al entea ro con comas en los lugares apropiados. Por ejemplo, si la invocacin es o formatea(1000000) deber regresar 1,000,000. (Pista: haz la recursin con dia o visin repetida y construye la cadena concatenando despus de cada llamada o e recursiva)

367

Administracin de la memoria durante ejecucin o o

Figura 7.34

Situacin de las chas antes de la llamada o

Figura 7.35

Movimientos /* 1 */ al /* 3 */
/* 1 */ /* 2 */ /* 3 */

Figura 7.36

Movimiento /* 4 */
/* 4 */

7.2 Recursividad

368

Figura 7.37

Movimientos /* 5 */ al /* 7 */
/* 7 */ /* 6 */ /* 5 */

Figura 7.38

Movimiento /* 8 */
/* 8 */

Figura 7.39

Movimientos /* 9 */ al /* 11 */
/* 10 */

/* 11 */

/* 9 */

369

Administracin de la memoria durante ejecucin o o

Figura 7.40

Movimiento /* 12 */
/* 12 */

Figura 7.41

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 a u Mergesort y el Quicksort. Con esto damos por terminado una descripcin somera sobre cmo se comporta o o la memoria durante la ejecucin de un programa, en particular la pila de ejecucin. o o Esta descripcin no pretende ser exhaustiva, sino unicamente proporcionar una o idea de cmo identica la ejecucin los puntos de entrada, de regreso y parmetros o o a a una funcin. o En el cap tulo que sigue veremos la aplicacin de recursividad a la solucin o o del problema de la base de datos que hemos estado manejando, mxime cuando a tenemos una denicin de listas dada de manera recursiva. o

7. Ejercicios

370

Ejercicios
7.1.- Para la serie de Fibonacci, denida para los nmeros naturales de la siguiente u manera: F ibonaccipnq

n2 F ibonaccipn 2q F ibonaccipn 1q Si n 2
1 Si 1

(a) Escribe el pseudocdigo para esta funcin recursiva. o o (b) Cuntas veces entra la aplicacin a la funcin Fibonacci(int n) cuando se a o o la invoca con Fibonacci(4) ? (c) Escribe el cdigo para la funcin programada iterativamente. o o (d) Cuantos pasos se llevan a cabo. contando el incremento del ndice, las sumas y las copias de un valor a otro? 7.2.- Tenemos el siguiente pedazo de cdigo: o
10 package c a p i t 7 ; 20 c l a s s E j e r c i c i o 7 2 { 30 private int caja = 25; 40 public int getCaja () { 50 return caja ; 60 } 70 public void e l E j e r c i c i o ( i n t k ) { 80 k = 50; 90 c a j a = 1 0 ; 100 } 110 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 ) { 120 E j e r c i c i o 7 2 nuevo = new E j e r c i c i o 7 2 ( ) ; 130 i n t c a j a = nuevo . g e t C a j a ( ) ; 140 nuevo . e l E j e r c i c i o ( c a j a ) ; 150 System . o u t . p r i n t l n ( "El valor de caja es: " + c a j a ) ; 160 System . o u t . p r i n t l n ( "El segundo valor de caja es: " 170 + nuevo . g e t C a j a ( ) ) ; 180 } 190 }

(a) Qu escribe la aplicacin en la l e o nea 150? (b) Qu escribe la aplicacin en las l e o neas 160 u 170?

371

Administracin de la memoria durante ejecucin o o

(c) Qu habr que hacer para que el atributo caja, desde main, tomara el e a valor 31? (Hay varias formas de lograrlo). 7.3.- Tenemos el registro del grupo en una lista ligada (puedes usar las deniciones dadas en al cap tulo 5). Programa, usando Java, recursivamente el mtodo e que lista el grupo completo. La funcin de listado est denida de la siguiente o a manera:

$ # ' textoGrupo Grupo: grupo ' 'Inicio ' ' ' ' ' $lista lista del grupo ! ' ' ' 'lista vac Regresa la cadena vac ' a ' a ' ' ' ' ' ' & ' ' Listado $ & del grupo 'Procesa lista ' 'Regresa textoGrupo ' & ' ' ' ' 'lista vac ' + primero de la lista ' ' a ' ' ' ' ' + Procesa lista con ' ' % % ' ' + el resto de la lista ' ' ' %Final

7.4.- Para esa misma lista, programa recursivamente la bsqueda de un estudianu te. La funcin de bsqueda se encuentra a continuacin: o u o

" $ 'lista vac Reporta ' a ' ' no encontrado ' ' ' ' ' $ & ! Busca estudiante 'Es el primero Reprtalo ' o en lista ' ' & ' ' 'lista vac ' a ' ' ! ' ' ' ' 'Es el primero Busca estudiante % % en resto de la lista
7.5.- Programa de manera recursiva la inversin de una cadena (Si la cadena es o abcd, su inversin es dcba). o 7.6.- Programa recursivamente cmo obtener los distintos patrones de bits con o cuatro posiciones. 7.7.- Programa recursivamente cmo mezclar dos listas de enteros, cada lista oro denada, de tal manera que el resultado sea una lista ordenada.

7. Ejercicios

372 7.8.- Haz un mtodo que calcule el mximo comn divisor (mcd) con la siguiente e a u frmula recursiva: o mcdpa, bq

b  mcdpb, a

0 % bq si a 0
si a

7.9.- Disea, implementa y prueba un mtodo recursivo que eleva un entero pon e sitivo dado a una potencia entera positiva. (Nota: tanto la base como la potencia deben ser parmetros). a 7.10.- Disea, implementa y prueba un mtodo recursivo que recibe un enten e ro positivo como parmetro y regresa una cadena representando al entea ro con comas en los lugares apropiados. Por ejemplo, si la invocacin es o formatea(1000000) deber regresar 1,000,000. (Pista: haz la recursin con dia o visin repetida y construye la cadena concatenando despus de cada llamada o e recursiva)

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 a n a grupo y reservar ese nmero de localidades en un arreglo. La superclase para u el registro con la informacin del estudiante queda exactamente igual a la que o utilizamos como EstudianteBasico. Como vamos a acomodar al curso en un arreglo la relacin de quin sigue a quin va a estar dada por la posicin en el arreglo. Por o e e o el momento ignoraremos el manejo de listas ligadas y no utilizaremos la referencia al elemento siguiente en los registros si la requerimos construiremos una subclase que la contenga. Para quien lo requiera puede consultar los listados 6.9 y 6.10 en el cap tulo sobre herencia cap tulo 6. La clase as denida puede ser usada, por ejemplo, para cuando queramos

8.1 Base de datos en un arreglo

374

una lista de estudiantes que tengan esta informacin bsica incluida. Un posible o a ejemplo se muestra en el listado 8.1. Esta jerarqu se puede extender tanto como a queramos. Si pensamos en estudiantes para listas usamos EstudianteBasico para heredar, agregando simplemente campos necesarios para mantener la lista, como en el caso EstudianteLista del Listado 8.1 omitiremos los comentarios para JavaDoc para ahorrar espacio. Esta clase hereda todos los mtodos que implementamos e anteriormente. Cdigo 8.1 Extendiendo la clase EstudianteBasico o EstudianteLista
10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 210 220 230 240 250 260 270 280 290 300 310 320 330 340 package C u r s o V e c t o r ; import H e r e n c i a . E s t u d i a n t e B a s i c o ; 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 E s t u d i a n t e B a s i c o { 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 ; public EstudianteLista () { } public EstudianteLista ( EstudianteBasico est ) { nombre = e s t . getNombre ( ) ; cuenta = e s t . getCuenta ( ) ; carrera = est . getCarrera (); } 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 , i n t c a r r ) { super ( nmbre , c t a , c a r r ) ; } / O b t i e n e l a r e f e r e n c i a a l s i g u i e n t e en l a l i s t a . @ r e t u r n e l v a l o r de s i g u i e n t e / public EstudianteLista getSiguiente () { return t h i s . s i g u i e n t e ; } / A c t u a l i z a e l v a l o r de l a 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 . @param a r g S i g u i e n t e v a l o r p a r a a s i g n a r a t h i s . 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 L i s t a a r g S i g u i e n t e ) { this . siguiente = argSiguiente ; } }

Si en cambio queremos estudiantes para arreglos, usamos directamente EstudianteBasico. Por ejemplo, si pensamos en registros para estudiantes de un curso podemos usar la denicin de EstudianteCalifs para armar un curso ver o Listado 8.2.

375

Ordenamientos usando estructuras de datos


EstudianteCalifs (1/4)

Cdigo 8.2 Extendiendo la clase EstudianteBasico con calicaciones o


10 package C u r s o V e c t o r ; 20 import H e r e n c i a . E s t u d i a n t e B a s i c o ; 30 import u t i l e s . ; 130 p u b l i c c l a s s 140 protected 150 protected 160 protected 210 220 230 240 250 260 270 280 290 300 310 320 440 450 460 470 480 490 570 580 590 660 670 680 760 770 780 790 800 810 820 830 840 850

E s t u d i a n t e C a l i f s extends E s t u d i a n t e B a s i c o { f l o a t [ ] c a l i f s ; // Para g u a r d a r c a l i f i c a c i o n e s f i n a l int numCalifs ; s t a t i c f i n a l i n t NUMCALIFS = 1 0 ;

public Es tu d i a n t eC a l i f s () { / En a u t o m t i c o i n v o c a a l c o n s t r u c t o r s i n p a r m e t r o s que s e a a d e c l a r en l a s u p e r c l a s e . / o n u m C a l i f s = NUMCALIFS ; c a l i f s = new f l o a t [ n u m C a l i f s ] ; } public E s t u d i a n t e C a l i f s ( EstudianteBasico est , int numCalifs ) super ( e s t ) ; this . numCalifs = numCalifs ; c a l i f s = new f l o a t [ n u m C a l i f s ] ; } p u b l i c E s t u d i a n t e C a l i f s ( S t r i n g nvoNombre , S t r i n g nvaCuenta , int nvaCarrera , int numCalifs ) { super ( nvoNombre , nvaCuenta , n v a C a r r e r a ) ; c a l i f s = new f l o a t [ n u m C a l i f s ] ; this . numCalifs = numCalifs ; } public float [ ] g e t C a l i f s () return c a l i f s ; } { {

public void s e t C a l i f s ( f l o a t [ ] this . c a l i f s = c a l i f s ; }

califs ) {

public void c o p i a C a l i f s ( f l o a t [ ] c a l i f s ) { i f ( c a l i f s == n u l l ) { this . c a l i f s = null ; return ; } t h i s . c a l i f s = new f l o a t [ c a l i f s . l e n g t h ] ; f o r ( i n t i = 0 ; i < c a l i f s . l e n g t h ; i ++) { this . c a l i f s [ i ] = c a l i f s [ i ] ; } }

8.1 Base de datos en un arreglo

376
EstudianteCalifs (2/4)

Cdigo 8.2 Extendiendo la clase EstudianteBasico con calicaciones o


930 940 950 960 970 980 990 1000 1010 1020 1090 1100 1110 1120 1130 1140 1150 1160 1170 1240 1250 1260 1270 1280 1360 1370 1380 1390 1400 1410 public static float [ ] copia ( float [ ] vector ) { i f ( v e c t o r == n u l l ) { return n u l l ; } f l o a t [ ] v e c t C o p i a = new f l o a t [ v e c t o r . l e n g t h ] ; f o r ( i n t i = 0 ; i < v e c t o r . l e n g t h ; i ++) { vectCopia [ i ] = vector [ i ] ; } return vectCopia ; } public f l o a t promedio ( ) { i f ( c a l i f s == n u l l ) return 0; 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 ; }

p u b l i c v o i d s e t C a l i f ( f l o a t c u a l , i n t donde ) { i f ( c a l i f s == n u l l | | donde < 0 | | donde > c a l i f s . l e n g t h ) return ; c a l i f s [ donde ] = c u a l < 0 ? 0 : Math . min ( c u a l , 1 0 . 0 f ) ; } p u b l i c f l o a t g e t C a l i f ( i n t donde ) { i f ( c a l i f s != n u l l && 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 t u r n 1; }

1500 public String toString () { 1510 S t r i n g l i n e a = super . t o S t r i n g ( ) ; 1520 f o r ( i n t i = 0 ; i < c a l i f s . l e n g t h ; i ++) { 1530 l i n e a = l i n e a + Cadenas . f o r m a t o ( c a l i f s [ i ] , , 7 , 2 ) ; 1540 } 1550 l i n e a = l i n e a + Cadenas . f o r m a t o ( p r o m e d i o ( ) , , 5 , 2 ) ; 1560 return l i n e a ; 1570 } 1580 } // c l a s s

Queremos cambiar nuestra estructura de datos de una lista a un arreglo. Estas dos estructuras de datos tienen semejanzas y diferencias. Veamos primero las

377

Ordenamientos usando estructuras de datos

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, o sea acceso directo. Dependiendo de qu tipo de datos tengamos y cules sean las operaciones a e a realizar sobre la estructura, podremos elegir entre listas o arreglos para nuestras estructuras de datos. Esta decisin deber estar justicada de alguna manera. o a Para construir la clase que maneja el arreglo, lo primero que debemos considerar es que en lugar de una cabeza de lista deberemos tener el arreglo, denido de un cierto tamao, que corresponder al mximo nmero de elementos que esn a a u peramos. Nuestros algoritmos son exactamente igual, excepto que interpretamos de distinta manera toma el siguiente o colcate al principio. En el caso de o que los registros estn en un arreglo colcate al principio se interpreta como e o inicializa un ndice en 0; y toma el siguiente se interpreta como incrementa en uno a la variable empleada como ndice. Con esta representacin es fcil obo a tener el anterior, ya que unicamente se decrementa el ndice en 1, si es que existe anterior. Adems, en todo momento tenemos que tener cuidado de no tratar de a tomar elementos ms all del n del arreglo. a a En una primera intencin, tomamos directamente a la clase EstudianteCalifs o para colocarlos en la lista guardada en un arreglo. Sin embargo, para saber quin e es el siguiente ocupa la siguiente posicin en el arreglo o quin es el anterior o e ocupa la posicin anterior en el arreglo vamos a anotar en cada registro la o posicin en la que se encuentran. Con este cambio generamos una nueva clase, o EstudianteVector, que hereda de EstudianteCalifs y que tiene un atributo entero. La implementacin de esta clase se encuentra en el listado 8.3 en la siguiente o pgina nuevamente omitiremos los comentarios de JavaDoc para hacer eciente a

8.1 Base de datos en un arreglo

378

el uso del espacio. Como mencionamos, si el registro se va a encontrar en un arreglo, para obtener el siguiente (o el anterior) debemos conocer la posicin del registro en el arreglo. o Agregamos para ello el atributo pos, que indicar la posicin del registro en el a o arreglo se deber asignar cuando se guarde en el arreglo. Para marcar que una a posicin no est ocupada lo indicamos con un -1 como valor del atributo pos. o a Tambin es el valor inicial de la posicin mientras el registro no sea ingresado al e o arreglo.

Cdigo 8.3 Clase para los elementos del curso (en un arreglo) o
10 20 30 40 50 60 130 140 150 260 270 280 290 300 390 400 410 420 480 490 500 130 140 150 160 } package C u r s o V e c t o r ; import u t i l e s . ; import H e r e n c i a . E s t u d i a n t e B a s i c o ;

EstudianteVector

p u b l i c c l a s s E s t u d i a n t e V e c t o r extends E s t u d i a n t e C a l i f s { p r i v a t e i n t p o s ; // Para r e c u p e r a r s u l u g a r en e l a r r e g l o public EstudianteVector () { p o s = 1; } p u b l i c E s t u d i a n t e V e c t o r ( S t r i n g nmbre , S t r i n g cn ta , i n t c r r e r a , int numCalifs ) { super ( nmbre , c n ta , c r r e r a , n u m C a l i f s ) ; p o s = 1; } public EstudianteVector ( EstudianteBasico est , int numCalifs ) { super ( e s t , n u m C a l i f s ) ; p o s = 1; } public i n t getPos () { return t h i s . pos ; } public void setPos ( i n t argPos ) { t h i s . pos = argPos ; }

Veamos cmo queda con estos cambios.1 Dado un arreglo es fcil saber cul o a a
Se recomienda referirse a los diagramas de Warnier-Orr donde se dieron los algoritmos en su momento.
1

379

Ordenamientos usando estructuras de datos

es el tamao del arreglo, mediante el atributo length, pero no es igual de fcil n a saber el nmero de posiciones en el arreglo que se encuentran realmente ocupadas u elementos vivos. Por lo tanto, es conveniente ir contando los registros que se agregan y los que se quitan, para que en todo momento se tenga claro el nmero u de elementos vivos que tenemos en el arreglo. Tambin es necesario mantener a e todos los elementos vivos ocupando posiciones consecutivas a partir de la primera en el arreglo. En el listado 8.5 en la pgina 381 podemos ver lo relacionado con el a cambio de estructura de datos de una lista a un arreglo. Se reprograma la interfaz ServiciosCurso del paquete ConsultasListas para que simplemente regrese objetos de la clase EstudianteBasico usando herencia podremos acceder a cualquier objeto de clases que hereden de EstudianteBasico, como EstudianteVector o EstudianteCalifs. El cdigo para esta nueva interfaz se encuentra en el listado 8.4. o Cdigo 8.4 Interfaz para distintas implementaciones del curso o ServiciosCurso
10 package C u r s o V e c t o r ; 20 import H e r e n c i a . E s t u d i a n t e B a s i c o ; 120 p u b l i c i n t e r f a c e S e r v i c i o s C u r s o { 180 250 260 330 340 410 420 510 520 530 640 730 790 860 S t r i n g getGrupo ( ) ; // S t r i n g daNombre ( i n t c u a l ) ; S t r i n g daNombre ( E s t u d i a n t e B a s i c o c u a l ) ; // S t r i n g d a C a r r e r a ( i n t c u a l ) ; String daCarrera ( EstudianteBasico cual ) ; // S t r i n g daCuenta ( i n t c u a l ) ; S t r i n g daCuenta ( E s t u d i a n t e B a s i c o c u a l ) ; // Es nuevo E s t u d i a n t e B a s i c o l o c a l i z a A l u m n o ( S t r i n g cadena , i n t campo , EstudianteBasico desde ) ; E s t u d i a n t e B a s i c o l o c a l i z a A l u m n o ( S t r i n g cadena , i n t campo ) ; v o i d agregaAlumno ( S t r i n g n , S t r i n g c t a , i n t ca ) ; void eliminaAlumno ( E s t u d i a n t e B a s i c o c u a l ) ; S t r i n g dameLista ( ) ;

920 S t r i n g losQueCazanCon ( S t r i n g q u i e n , i n t campo ) ; 930 } // S e r v i c i o s C u r s o

Con esta interfaz en mente y usando la extensin del mtodo toString para o e mostrar el contenido de los objetos, procedemos a implementar una clase para el curso, guardados los estudiantes en un arreglo. El cdigo se encuentra en el o listado 8.5 en la pgina 381. Iremos introduciendo los mtodos conforme los disa e cutamos. Por lo pronto presentamos aquellos que no requieren mayor explicacin. o

8.1 Base de datos en un arreglo

380
CursoEnVector (1/2)

Cdigo 8.5 Base de datos implementada en un arreglo o


10 package C u r s o V e c t o r ; 20 import H e r e n c i a . E s t u d i a n t e B a s i c o ;

90 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 S e r v i c i o s C u r s o { 100 protected s t a t i c f i n a l i n t 110 NOMBRE = 1 , // I d d e l campo p a r a nombre 120 CARRERA = 2 , // I d d e l campo p a r a c a r r e r a 130 CUENTA = 3 ; // I d d e l campo p a r a numero de c u e n t a 140 p r i v a t e s t a t i c f i n a l i n t TAM GRUPO = 4 ; // Para l a c l a s e d e l g r u p o 150 160 p r i v a t e i n t NUM CALIFS = 1 0 ; // Nm . de c a l i f i c a c i o n e s p o r alumno u 170 p r i v a t e f i n a l i n t MAXREG = 1 0 ; // Nm . de r e g i s t r o s p o r o m i s i n u o 180 p r i v a t e E s t u d i a n t e B a s i c o [ ] l i s t a ; // L i s t a de alumnos 190 private S t r i n g grupo ; // Nombre d e l g r u p o 200 p r i v a t e i n t numRegs = 0 ; // Nm . r e g i s t r o s v i v o s u 260 270 280 290 300 400 410 420 430 440 450 530 540 550 560 570 580 590 600 610 620 630 640 650 660 670 public CursoEnVector ( S t r i n g gr ){ grupo = gr ; numRegs = 0 ; l i s t a = new E s t u d i a n t e V e c t o r [MAXREG ] ; } 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 ) { NUM CALIFS = n u m C a l i f s ; grupo = gr ; numRegs = 0 ; 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 ] ; } public CursoEnVector ( E s t u d i a n t e B a s i c o [ ] 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 ) ; (( EstudianteVector ) l i s t a [ i ] ) . setPos ( i ) ; 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 ++) }

381

Ordenamientos usando estructuras de datos


CursoEnVector (2/2)

Cdigo 8.5 Base de datos implementada en un arreglo o


760 770 780 790 800 810 820 830 840 850 860 870 660 670 680 1050 1060 1070 1160 1170 1180 1280 1290 1300

public String toString () { S t r i n g r e s u l t = " GRUPO : " + g r u p o + "\t\tNum. de estudiantes : " + numRegs + "\n ======================================= " + " ===============\ n" ; f o r ( i n t i = 0 ; i < numRegs ; i ++) { i f ( l i s t a [ i ] != n u l l ) { r e s u l t += l i s t a [ i ] . t o S t r i n g ( ) + "\n" ; } } // end o f f o r ( i n t i = 0 ; i < numRegs ; i ++) return r e s u l t ; } p u b l i c S t r i n g daNombre ( E s t u d i a n t e B a s i c o e s t ) { r e t u r n e s t . daNombre ( ) ; } public String daCarrera ( EstudianteBasico est ) { return e s t . daCarrera ( ) ; } p u b l i c S t r i n g daCuenta ( E s t u d i a n t e B a s i c o e s t ) { return e s t . getCuenta ( ) ; } public String armaRegistro ( EstudianteBasico est ) { return e s t . t o S t r i n g ( ) ; }

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 hacia abajo 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 que se acaba de desocupar. 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 hacia abajo (para hacer lugar) deberemos recorrer desde el nal del arreglo hacia la primera posicin que se desea o mover. Si no se hace en este orden se tendr como resultado el valor del primer a registro que se desea mover copiado a todos los registros abajo de ste. El mtodo e e para recorrer hacia abajo se encuentra en el listado 8.6 en la siguiente pgina. a El mtodo nos tiene que regresar si pudo o no pudo recorrer a los elementos. En e el caso de que no haya suciente lugar hacia abajo, nos responder falso, y nos a

8.1 Base de datos en un arreglo

382

responder verdadero si es que pudo recorrer. a Si deseamos regresar un lugar hacia arriba, el procedimiento es similar, excepto que tenemos que mover desde el primero hacia el ultimo, para no acabar con una repeticin del ultimo elemento que se recorre. Estos mtodos se muestran en el o e listado 8.6.

Cdigo 8.6 Corrimiento de registros hacia la derecha e izquierda o


1410 1420 1430 1440 1450 1460 1470 1480 1490 1500 1510 1520 1640 1650 1660 1670 1680 1690 1700 1710 1720 1730 1740

CursoEnVector

p r i v a t e boolean r e c o r r e ( i n t des de , 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 ; } // end o f i f ( numRegs + c u a n t o s >= l i s t a . l e n g t h ) 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 ] = null ; (( EstudianteVector ) l i s t a [ i + cuantos ] ) . setPos ( i + cuantos ) ; } // end o f f o r ( i n t i = numRegs 1 ; i >= d e s d e ; i ) return true ; } p r i v a t e boolean r e g r e s a ( i n t des de , 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 + c u a n t o s ; i <= numRegs c u a n t o s ; i ++) { l i s t a [ i c u a n t o s ] = l i s t a [ i ] ; ( ( E s t u d i a n t e V e c t o r ) l i s t a [ i c u a n t o s ] ) . s e t P o s ( i 1); } // end o f f o r ( i n t i = 0 ; i < numRegs 1 ; i ++) l i s t a [ numRegs 1] = n u l l ; return true ; }

El mtodo que regresaba la referencia de la lista del primer elemento de la e lista ahora debe regresar la referencia al arreglo completo. Queda como se muestra en el listado 8.7, junto con los mtodos que ponen y regresan el nmero de grupo. e u Estos dos ultimos no cambian.

Cdigo 8.7 Mtodos de acceso y manipulacin o e o


1810 1820 1830 1900 1910 1920 1990 2000 2010 public S t r i n g getGrupo ( ) return grupo ; } {

CursoEnVector

public EstudianteBasico [ ] getLista () return l i s t a ; } p u b l i c i n t getNumRegs ( ) { r e t u r n numRegs ; }

383

Ordenamientos usando estructuras de datos

Para saber el nmero de registros vivos en el arreglo ya no nos sirve revisarlo u y ver cuntas referencias distintas de null tiene. Por ejemplo, si el arreglo en vez a de objetos tiene nmeros, y valores vlidos pueden ser cero o negativos, no habr u a a forma de distinguir entre un lugar ocupado por un nmero o un lugar que no u estuviera ocupado. Por ello es conveniente ir contando los registros que se van agregando e ir descontando los que se quitan, con los registros activos ocupando posiciones consecutivas en el arreglo. Por ello, ya no se calcula el nmero de u elementos en el arreglo, sino que simplemente se regresa este valor. En implementaciones anteriores usamos tres maneras de agregar registros al arreglo. La primera de ellas, la ms fcil, es agregando al nal de los registros, ya a a que esto no implica mover a nadie, sino simplemente vericar cul es el siguiente a lugar a ocupar. De manera similar a que cuando el nmero de elementos en un u arreglo es n los 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 o a ocupar es precisamente k. En general, numRegs contiene la siguiente posicin a o ocuparse, por lo que si 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 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 el a momento en que se crea. Como no siempre vamos a poder agregar registros (algo que no suced cuando ten a amos una lista), al intentar agregar a un estudiante que ya no quepa el arreglo deber aumentar en tamao con incrementos del tamao a n n dado por omisin. o

Figura 8.1

Aumento de tamao de la lista n $ # ' Tamao nuevo Tamao viejo + MAXREG n n ' 'Inicio ' ' ' ' nuevaLista arreglo de tamao nuevo n ' ' ' # Aumenta & tamao n ' de la lista 'Copia registro '(i=0,...,numRegs-1) nuevaLista[i] lista[i] ' ' ' ' ' ! ' 'Final % Regresa nuevaLista

8.1 Base de datos en un arreglo

384

El algoritmo para el mtodo que hace crecer el espacio disponible en la lista se e encuentra en la gura 8.1 en la pgina anterior y su cdigo en Java en el listado 8.8. a o

Cdigo 8.8 Aumento del tamao de la lista o n


1470 1480 1490 1500 1510 1520 1530 private EstudianteBasico [ ] copiaLista () { EstudianteBasico [ ] nuevaLista = new E s t u d i a n t e B a s i c o [ l i s t a . l e n g t h + MAXREG ] ; f o r ( i n t i = 0 ; i < l i s t a . l e n g t h ; i ++) nuevaLista [ i ] = l i s t a [ i ] ; return nuevaLista ; }

CursoEnVector

Con este mtodo es fcil programar los mtodos que agregan al principio o al e a e nal de la lista; se muestran en el listado 8.9.

Cdigo 8.9 Insercin de registros al principio y nal de la lista o o


1610 1620 1630 1640 1650 1660 1670 1680 1770 1780 1790 1800 1810 1820 1830 1840 1850 1860 1870 1880 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 B a s i c o nuevo ) i f ( numRegs >= l i s t a . l e n g t h ) { EstudianteBasico [ ] nuevaLista ; nuevaLista = copiaLista ( ) ; l i s t a = nuevaLista ; } l i s t a [ numRegs++] = nuevo ; } 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 B a s i c o nuevo ) i f ( numRegs >= l i s t a . l e n g t h ) { EstudianteBasico [ ] nuevaLista ; nuevaLista = copiaLista ( ) ; l i s t a = nuevaLista ; } i f (! r e c o r r e (0 , 1)) { System . o u t . p r i n t l n ( "No se pudo insertar " ) ; return ; } l i s t a [ 0 ] = nuevo ; } { {

CursoEnVector

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

385

Ordenamientos usando estructuras de datos

cadenas. Recordemos que el mtodo compareTo de la clase String nos sirve para e saber la relacin entre dos cadenas, de la siguiente forma: o

$ '1 Si s1 s2 ' & s1.compareTo(String s2) ' 0 Si s1 == s2 ' %


1 Si s1

s2

La programacin del mtodo que agrega en orden a un estudiante se puede o e apreciar en el listado 8.10 en la siguiente pgina. a

8.1 Base de datos en un arreglo

386
CursoEnVector

Cdigo 8.10 Insercin de registros en lista ordenada o o


2590 2600 2610 2620 2630 2640 2650 2660 2670 2680 2690 2700 2710 2720 2730 2740 2750 2760 2770 2780 2790 2800 2810 2820 2830 2840 2850 2860 2870 2880 2890 2900 2910 2920 2930

p u b l i c v o i d agregaAlumno ( E s t u d i a n t e B a s i c o nuevo ) { i f ( numRegs >= l i s t a . l e n g t h ) { EstudianteBasico [ ] nuevaLista ; nuevaLista = copiaLista ( ) ; l i s t a = nuevaLista ; } i n t donde = 1; S t r i n g nmbre = nuevo . getNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) ; int i = 0; f o r ( i = 0 ; i < numRegs ; i ++) { i f ( l i s t a [ i ] == n u l l ) { System . e r r . p r i n t l n ( "Hay un hueco vacio en la " + " posicion *" + i + "*" ) ; continue ; } // end o f i f ( l i s t a [ i ] == n u l l ) i f ( l i s t a [ i ] . getNombre ( ) . t o L o w e r C a s e ( ) . compareTo ( nmbre . t r i m ( ) . t o L o w e r C a s e ( ) ) >= 0 ) { donde = i ; i f (! recorre ( i , 1)) { return ; } // end o f i f ( ! r e c o r r e ( i , 1 ) ) break ; } // end o f i f ( l i s t a [ i ] . getNombre ( ) . comparesTo ( nmbre ) >= 0 ) } // end o f f o r ( i n t i = 0 ; i < numRegs ; i ++) i f ( i >= numRegs ) { // Le t o c a a l f i n a l donde = numRegs ; } i f ( donde == 1) { System . o u t . p r i n t l n ( "No pude registrar a " + nmbre ) ; return ; } // end o f i f ( donde = 1) l i s t a [ donde ] = nuevo ; ( ( E s t u d i a n t e V e c t o r ) l i s t a [ donde ] ) . s e t P o s ( donde ) ; numRegs++; }

Para agregar un registro donde nos proporcionan los datos individuales del estudiante, simplemente armamos el registro e invocamos al mtodo que agrega e un registro ya armado, como se puede ver en el listado 8.11 en la pgina opuesta. a

387

Ordenamientos usando estructuras de datos


CursoEnVector

Cdigo 8.11 Insercin de registros en lista ordenada con datos individuales o o


3000 3010 3020 3030 3040

p u b l i c v o i d agregaAlumno ( S t r i n g nmbre , S t r i n g cn ta , i n t c r r e r a ) { E s t u d i a n t e B a s i c o nuevo = new E s t u d i a n t e B a s i c o ( nmbre , c nt a , crrera ); agregaAlumno ( nuevo ) ; }

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 en las l neas 2680 a 2820 del listado 8.10 en la pgina opuesta. Nos colocamos a al principio del vector, poniendo el ndice que vamos a usar para recorrerlo en 0 l nea 2680. A continuacin, mientras no se nos acaben los registros del arreglo o y estemos viendo registros lexicogrcamente menores o iguales al que buscamos a condicionales en l neas 2740 y 2810 incrementamos el ndice, esto es, pasamos al siguiente. Podemos salir de la iteracin porque se acaben los registros vivos condicin del o o for en la l nea 2680 o porque se encuentre a un elemento mayor lexicogrcamente a el enunciado break de la l nea 2800. En el primer caso habremos salido porque el ndice lleg al nmero de registros almacenados (actual == numRegs), en cuyo o u caso simplemente colocamos al nuevo registro en el primer lugar sin ocupar del arreglo, ya que no hay ningn nombre registrado que sea lexicogrcamente mayor. u a No hay peligro en esto pues al entrar al mtodo vericamos que todav hubiera e a lugares disponibles y si no los hab crecimos el espacio l a, neas 2600 a 2640. 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. Ese to se hace en la l nea 2770, 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 2900 y 2920 no sin antes incrementar el contador de registros. Tambin el mtodo que quita a un estudiante va a cambiar. Mostramos el e e algoritmo en la gura 8.2 en la siguiente pgina. a

8.1 Base de datos en un arreglo

388

Figura 8.2

Algoritmo para eliminar a un estudiante ! $ 'Inicio donde posicin en el arreglo o ' ' ' ' $ ' ' ' ' ' & & Eliminar ' a un Recorrer elementos Recorre un lugar a la izquierda ' ' estudiante '(i=donde,...,numRegs) ' % ' ' ' ' ' ! ' ' %Final r
La codicacin de este algoritmo se puede ver en el listado 8.12. o

Cdigo 8.12 Eliminacin de un estudiante de la base de datos o o


1890 1900 1910 1920 1930 1940 1950 1960 1970 1980 public void eliminaAlumno ( E s t u d i a n t e B a s i c o c u a l ) { i n t donde = d a L u g a r ( ( E s t u d i a n t e V e c t o r ) c u a l ) ; System . o u t . p r i n t l n ( " Eliminando a: " + c u a l ) ; i f ( donde < 0 ) { return ; } // end o f i f ( donde < 0 ) r e g r e s a ( donde , 1 ) ; numRegs ; l i s t a [ numRegs ] = n u l l ; }

CursoEnVector

El mtodo hace lo siguiente: Ubica la posicin del registro en el arreglo l e o nea 1900 del listado 8.12. Una vez hecho esto recordemos que agregamos un atributo que registre esto, se procede a desaparecerlo, recorriendo a los registros que estn a despus que l un lugar a la izquierda, encimndose en el registro que se est quie e a a tando; esto se hace con la llamada a regresa(actual,1) en la l nea 1950 del mismo listado. Al terminar de recorrer a los registros hacia la izquierda, se decrementa el contador de registros numRegs. 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 2060 del listado 8.13 en la pgina opuesta mientras que tomar el siguiente quiere decir a incrementar en 1 el ndice que se est usando para recorrer el arreglo l a nea 2090 del listado 8.13 en la pgina opuesta. a

389

Ordenamientos usando estructuras de datos

Cdigo 8.13 Bsqueda de una subcadena en algn campo del arreglo o u u


1990 2000 2010 2020 2030 2040 2050 2060 2070 2080 2090 2100 2110 2120 2130 2140

(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 p a r a r . @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 E s t u d i a n t e B a s i c o 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 2100 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 Apor 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 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

8.1 Base de datos en un arreglo

390

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.14 Listar todos los registros de la base de datos o


2150 2160 2170 2180 2190 2200 2210 2220 2230 2240 2250 2260 2270 2280 2290 2300 2310

(CursoEnVector)

/ L i s t a todos l o s r e g i s t r o s d e l Curso . @ r e t u r n <code>S t r i n g </code> con l a l i s t a c o m p l e t a / public String listaTodos () { S t r i n g s L i s t a = "" ; 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 ++) { s L i s t a += l i s t a [ a c t u a l ] . t o S t r i n g ( ) ; } i f ( a c t u a l == 0 ) { s L i s t a = "No hay registros en la base de datos " ) ; } return s L i s t a ; }

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 2240 en el listado 8.14. 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.15 e en la pgina opuesta 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 2390 en el listado 8.15 en la pgina opuesta. Si no se encontr ningn registro que satisciera las condiciones a o u dadas, se da un mensaje de error manifestndolo. a

391

Ordenamientos usando estructuras de datos

Cdigo 8.15 Listando los que cumplan con algn criterio o u


2320 2330 2340 2350 2360 2370 2380 2390 2400 2410 2420 2430 2440 2450 2460 2470 2480 2490 2500 2510 2520 2530 2540 2550 2560 2570

(CursoEnVector)

/ Imprime 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 i n t c u a l Con c u l campo s e d e s e a c o m p a r a r . a @param S t r i n g s u b c a d Con e l que queremos que c a c e . @ r e t u r n <code>S t r i n g </code> l a l i s t a con l o s que c a z a n . / p u b l i c S t r i n g losQueCazanCon ( i n t c u a l , S t r i n g s u b c a d ) { int i = 0; subcad = subcad . toLowerCase ( ) ; S t r i n g s L i s t a = "" ; 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 i s 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 ++; s L i s t a += 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 ) { s L i s t a = "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 EstudianteBasico, y de esa manera reutilizar directamente el cdigo que ya tenemos para EstudianteBasico. o Todo lo que tenemos que hacer es agregarle los campos que EstudianteBasico no tiene y los mtodos de acceso y manipulacin para esos campos. La programacin e o o de la clase utilizando herencia se puede observar en el listado 8.16 en la siguiente pgina. a

8.2 Mantenimiento del orden con listas ligadas

392 (Estudiante) 1/3

Cdigo 8.16 Denicin de la clase Estudiante para los registros o o

10 import j a v a . u t i l . S c a n n e r ; 20 / 30 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 40 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 50 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 60 / 70 c l a s s E s t u d i a n t e extends E s t u d i a n t e B a s i c o { 80 protected E s t u d i a n t e s i g u i e n t e ; 90 protected S t r i n g c l a v e ; 100 p u b l i c s t a t i c f i n a l i n t CLAVE = 4 ; 110 / C o n s t r u c t o r s i n p a r m e t r o s . / a 120 public Estudiante () { 130 super ( ) ; 140 clave = null ; 150 siguiente = null ; 160 } 170 / 180 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 . 190 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 200 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 210 y coma . 220 @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 230 cada uno de l o s campos que s e van a l l e n a r . 240 @ 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 250 / 260 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 cn ta , S t r i n g c l v e , 270 String crrera ) { 280 super ( nmbre , c n ta , c r r e r a ) ; 290 clave = clve . trim ( ) ; 300 siguiente = null ; 310 } 320 / 330 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 . 340 / 350 public String getClave () { 360 return c l a v e ; 370 } 380 / 390 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 400 parmetro . a 410 / 420 public void s e t C l a v e ( S t r i n g c l v e ) { 430 clave = clve ; 440 }

393

Ordenamientos usando estructuras de datos

Cdigo 8.16 Denicin de la clase Estudiante para los registros o o


450 460 470 480 490 500 510 520 530 540 550 560 570 580 590 600 610 620 630 640 650 660 670 680 690 700 710 720 730 740 750 760 770 780 790 800 810 820 830 840 850 860 870 880 890 900 910

(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 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 ( ) . tri 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

394 (Estudiante)3/3

Cdigo 8.16 Denicin de la clase Estudiante para los registros o o


920 930 940 950 960 970 980 990 1000 1010 1020 1030 1040 } / 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 tr 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 cn t a , String clve , String crrera ) super . s e t R e g i s t r o ( nmbre , c nt a , 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 sper-objeto de this, a aqul denido por la superclase. u 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

395

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.3, 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.3

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.4 en la siguiente pgina. a

8.2 Mantenimiento del orden con listas ligadas

396

Figura 8.4

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.5 en la pgina opuesta y la programacin del mtodo se puede ver en el listado 8.17. a o e

Cdigo 8.17 Agregar un registro manteniendo el orden o


1050 1060 1070 1080 1090 1100 1110 1120 1130 1140 1150 1160 1170 1180 1190 1200 1210 1220 1230 1240 1250 1260 1270 1280 1290 1300 1310

(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 = nuevo ; 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 (); }

397

Ordenamientos usando estructuras de datos

Cdigo 8.17 Agregar un registro manteniendo el orden o


1320 1330 1340 1350 1360 1370

(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.5

Agregando un registro en orden ! $ 'lista  r Colcalo en la cabeza de la lista o ' ' ' ' ' ' ' $ ' $ ' ' ' ' Le toca al ' Insrtalo antes del ' e ' ' & ' ' ' ' ' ' principio ' ' ' primer elemento ' ' ' ' ' ' ' ' de la lista ' % ' ' ' de la lista ' ' ' ' ' ' ' ' ' ' ' ' ' ' $ ' ' ' ' ' ' 'anterior Primero de ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' la lista ' & ' ' ' ' Agrega registro ' ' 'actual Segundo de ' ' ' ' & ' en orden ' ' ' 'lista  r ' ' ' la lista ' ' ' ' ' ' ' ' ' ' Le toca al ' Recorre $ ' ' ' ' 'anterior actual ' ' ' ' & ' ' ' ' ' ' ' principio ' ' ' ' la lista ' ' ' ' ' ' ' ' ' ' ' ' ' & ' (mientras ' ' de la lista ' ' ' ' ' ' ' ' ' ' ' ' ' ' la llave ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' sea menor 'actual siguiente ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' o igual) % ' ' ' ' ' ' ' ' ' ' ' ' % % %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
1380 1390 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

398

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 arbol, para pasar despus a ver, espec e camente, a los arboles binarios. Un arbol es una estructura de datos dinmica, 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 ra del rbol, que no tiene z a padre: corresponde al punto de entrada de la estructura. A cada nodo le corresponde un nivel en el arbol, y se calcula sumndole 1 a al nivel de su padre. La ra tiene nivel 1. z

399

Ordenamientos usando estructuras de datos

La profundidad del rbol es el mximo de los niveles de sus nodos. a a Cada nodo del arbol va a tener un campo para la informacin que deseamos o organizar, y n referencias, donde n es el nmero mximo de hijos que puede u a tener un nodo. Un arbol es n-ario si el mximo nmero de hijos que puede tener es n. Es a u binario, si el mximo nmero de hijos es 2; terciario para tres hijos, y as sucea u sivamente. Podemos representar arboles con un nmero arbitrario de hijos para u cada nodo, pero dejaremos ese tema para cursos posteriores. Tambin podemos e denir los arboles recursivamente:

$ 'Un nodo que no tiene hijos ' & Un rbol n-ario es a ' ' %

Un nodo que tiene como hijos a n arboles

A esta denicin le corresponde el esquema en la gura 8.6. o

Figura 8.6

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 arbol binario, entonces, es un rbol donde el mximo nmero de hijos para 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

400

a la que se encuentra en la ra Un arbol binario bien organizado se muestra en z. la gura 8.7.

Figura 8.7

Arbol binario bien organizado

Cuando todos los nodos, excepto por las hojas del ultimo nivel, tienen exac tamente el mismo nmero de hijos, decimos que el arbol est completo. Si la prou a fundidad del subrbol izquierdo es la misma que la del subrbol derecho, decimos a a 2 que el rbol est equilibrado . 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 EstudianteBasico, 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.18 en la pgina opuesta. 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

401

Ordenamientos usando estructuras de datos

Cdigo 8.18 Clase ArbolEstudiante para cada registro o nodo o


10 c l a s s A r b o l E s t u d i a n t e extends E s t u d i a n t e B a s i c o { 20 / 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 30 protected ArbolEstudiante izqrdo , 40 / 50 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 60 / 70 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 cn ta , 80 String crrera ) { 90 super ( nmbre , c n ta , c r r e r a ) ; 100 izqrdo = dercho = null ; 110 } 120 / 130 Proporciona la r e f e r e n c i a del subrbol izquierdo . a 140 / 150 public ArbolEstudiante getIzqrdo () { 160 return izq rdo ; 170 } 180 / 190 Proporciona l a r e f e r e n c i a al subrbol derecho . a 200 / 210 public Arbol Estudi ante getDercho () { 220 return dercho ; 230 } 240 / 250 Actualiza el subrbol izquierdo . a 260 / 270 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 ) { 280 i z q r d o = nuevo ; 290 } 300 / 310 Actualiza e l subrbol derecho . a 320 / 330 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 ) { 340 d e r c h o = nuevo ; 350 } 360 }

(ArbolEstudiante)

8.3.1.

Construccin de rboles binarios para ordenamientos o a


Los arboles de ordenamiento se van construyendo conforme se van insertando los nodos. Cuando se inicia la insercin y el arbol est vac el primer dato que o 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

402

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 arbol. En este ejemplo, si los registros van llegando ya ordenados de menor a mayor, el rbol que se obtiene es el que se puede ver en la gura 8.8. a

Figura 8.8

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 arbol para o ordenar, queremos que los datos lleguen con un cierto desorden, que nos garantiza

403

Ordenamientos usando estructuras de datos

un arbol ms equilibrado. Dados n registros en un arbol, la menor profundidad a se alcanza cuando el rbol est equilibrado, mientras que la mayor profundidad se a a alcanza cuando tenemos un arbol degenerado como el que acabamos de mostrar. Hay varios resultados importantes respecto a arboles binarios que se reejan en la eciencia de nuestras bsquedas. En un arbol equilibrado con n nodos, una hoja u 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 arbol.

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.9 en la pgina 405. Queda programado, de acuerdo a

8.3 *Ordenamiento usando rboles a

404

al algoritmo anterior, como se puede apreciar en el listado 8.19 en la pgina 406. a

Del algoritmo en la gura 8.9 en la pgina opuesta podemos ver que unicamente a 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 arbol, dependiendo del resultado de comparar al registro que se desea agregar con el que se encuentra en la ra del subrbol en turno. El algoritmo se traduce bastante z a directamente a cdigo. o

405

Ordenamientos usando estructuras de datos

Figura 8.9

Agregar un registro manteniendo el orden

$ ' ' ' $ ' ' ' ' ' & ' ' ' 'Arbol vac Colcalo en la ra o ' z ' ' o ' % ' ' ' ' ' ' ' ' $ ' $ ' ' ' ' ' ' ' ' ' ' ' $ ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' Hay subrbol ' Inserta registro en el & ' ' ' ' ' ' a ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' a ' ' ' ' ' Dato menor ' izquierdo ' subrbol izquierdo ' ' ' ' ' ' % ' ' & ' ' ' ' ' que el de la ' ' ' ' ' ' ' Inserta ' ' ' ' ' ' ' ' $ & ' ra z ' ' ' nodo ' ' ' ' ' ' ' ' ' ' ' ' ' en ' & ' ' Hay subrbol ' Coloca el registro como ' ' ' a ' ' & ' ' arbol ' ' ' 'Arbol vac ' izquierdo ' ' ' o subrbol izquierdo a ' ' ' ' ' ' ' ' ' ' ' % ' % ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' $ ' ' # ' ' ' ' ' Hay subrbol ' ' a Inserta registro en el ' ' ' ' ' ' ' ' ' ' ' ' ' derecho ' ' ' subrbol derecho a ' ' ' Dato menor ' ' ' ' ' ' ' ' & ' ' ' ' ' ' $ ' que el de la ' ' ' ' ' ' ' ' ' ' ' ' ' & ' ' ' ' ' ' ra z ' ' ' ' Hay subrbol ' ' ' ' a ' ' ' ' ' Coloca el registro como ' ' ' ' ' ' ' % % derecho % ' subrbol derecho a ' ' ' ' %

8.3 *Ordenamiento usando rboles a

406 (ArbolOrden)

Cdigo 8.19 Agregar un registro en un rbol binario ordenado o a

10 p u b l i c c l a s s A r b o l O r d e n { 20 / La r a z d e l r b o l / a 30 private ArbolEstudiante r a i z ; 40 private S t r i n g grupo ; 50 p r i v a t e i n t numRegs ; 60 / 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 70 @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 . 80 / 90 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 ) { 100 return f a l s e ; 110 } 120 / 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 130 @param E s t u d i a n t e B a s i c o nuevo A q u i e n s e va a a g r e g a r 140 / 150 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 ) { 160 return f a l s e ; 170 } 180 / 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 ne r l a l i s t a 190 ordenada 200 @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 210 @ 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 220 / 230 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 ) { 240 i f ( r a i z == n u l l ) { // Es e l p r i m e r o 250 r a i z = nuevo ; 260 return true ; 270 } 280 r e t u r n meteEnArbol ( r a i z , nuevo ) ; 290 } 300 / 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 310 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 , 320 A r b o l E s t u d i a n t e nuevo ) { 330 i n t compara = nuevo . daNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) 340 . compareTo ( r a i z . daNombre ( ) . t r i m ( ) . t o L o w e r C a s e ( ) ) ; 350 i f ( compara < 0 && r a i z . g e t I z q r d o ( ) == n u l l ) { 360 r a i z . p o n I z q r d o ( nuevo ) ; 370 return true ; 380 } 390 i f ( compara >= 0 && r a i z . g e t D e r c h o ( ) == n u l l ) { 400 r a i z . s e t D e r c h o ( nuevo ) ; 410 return true ; 420 } 430 i f ( compara < 0 ) 440 r e t u r n meteEnArbol ( r a i z . g e t I z q r d o ( ) , nuevo ) ; 450 else 460 r e t u r n meteEnArbol ( r a i z . g e t D e r c h o ( ) , nuevo ) ; 470 }

407

Ordenamientos usando estructuras de datos

8.3.4.

Listar toda la base de datos


Tenemos tres posibles recorridos para listar la informacin en un arbol binao 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.10 (el orden de llegada pudo a haber sido primero Anita y despus Octavio, pero si hubiera llegado primero e Anita u Octavio el arbol no se hubiera armado equilibrado).

Figura 8.10

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 ordenes de recorrido posibles. Por ejemplo, si visitramos primero 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 arboles son muy utiles no nada ms para ordenamientos, sino a

8.3 *Ordenamiento usando rboles a

408

que se utilizan para denotar la precedencia en operaciones aritmticas). Dado que e los arboles son estructuras recursivas, el hijo izquierdo es, a su vez, un arbol (lo 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 arbol binario arbitrario quedar como a se muestra en el esquema de la gura 8.11.

Figura 8.11

Recorrido simtrico de un rbol e a $ ! 'Hay hijo izquierdo Visita subrbol izquierdo ' a ' ' ' ' ' ' ' $ ' ' ' ' ' & ' ' 'Hay hijo izquierdo ' ' ' 'r ' % ' ' & Visita subrbol Procesa la ra a z ! ' ' 'Hay hijo derecho Visita subrbol derecho ' a ' ' ' ' ' ' ' $ ' ' ' ' ' & ' ' 'Hay hijo derecho ' ' ' 'r ' % %

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
private void l i s t a A r b o l ( 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.20 en la pgina opuesta y 8.21 en la pgina opuesta a a respectivamente.

409

Ordenamientos usando estructuras de datos

Cdigo 8.20 Listado de la base de datos completa o


2080 2090 2100 2110 2120 2130 2140 2150 2160 2170

(ArbolOrden)

/ 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 en e l d i s p o s i t i v o de s a l i d a . / public void l i s t a T o d o s ( ) { i f ( r a i z == n u l l ) { System . o u t . p r i n t l n ( "No hay registros en la base de datos " ) ; return ; } listaArbol ( raiz ); }

Cdigo 8.21 Recorrido simtrico del rbol o e a


2180 2190 2200 2210 2220 2230 2240 2250 2260 2270 / 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 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 / private void l i s t a A r b o l ( 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 ; listaArbol ( raiz . getIzqrdo ()); System . o u t . i m p r i m e l n ( r a i z . d a R e g i s t r o ( ) ) ; l i s t a A r b o l ( r a i z . getDercho ( ) ) ; }

(ArbolOrden)

8.3.5.

Conservacin de la aleatoriedad de la entrada o


Quisiramos guardar el contenido del arbol en disco, para la prxima vez eme 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 arbol binario es que los datos vengan en 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.12 en la siguiente e pgina. a

8.3 *Ordenamiento usando rboles a

410

Figura 8.12

Recorrido en preorden de un rbol a $ 'Procesa la ra ' z ' ! ' ' 'Hay hijo izquierdo Recrrelo en preorden ' o ' ' ' ' ' ' ' ' # ' ' ' 'Hay hijo izquierdo ' ' ' ' r & Recorrido en ' preorden ! ' ' 'Hay hijo derecho Recrrelo en preorden ' o ' ' ' ' ' ' ' $ ' ' ' ' ' & ' ' 'Hay hijo derecho ' ' ' 'r ' % %

8.3.6.

Bsquedas u

Para encontrar un registro dado en el arbol, debemos realizar algo similar 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 arbol, si cuando tengo que bajar por algn hijo, ste no existe. La 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 arbol. El algoritmo se muestra en la gura 8.13 en la pgina 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.22 en la pgina o e a opuesta.

411

Ordenamientos usando estructuras de datos

Figura 8.13

Bsqueda en un rbol ordenado u a ! $ 'nombre = campo Regresa esta ra z ' ' ' ' ' ' ' ' $ # ' ' ' ' Hay hijo Encuentra nombre en ' ' ' ' ' ' ' ' ' ' izquierdo ' ' subrbol izquierdo a ' ' ' ' ' ' ' & ' 'nombre campo $ ' ' ' ' ' ' ' ' & ' Hay hijo ' ' ' ' ' ' ' ' ' ' ' izquierdo 'r & ' Encuentra nombre ' ' % % ' en subrbol a ' ' ' ' ' $ # ' ' ' ' Hay hijo ' Encuentra nombre en ' ' ' ' ' ' ' ' ' derecho ' ' subrbol derecho a ' ' ' ' ' ' ' & ' ' 'nombre campo $ ' ' ' ' ' ' ' ' & ' Hay hijo ' ' ' ' ' ' ' ' ' ' ' derecho 'r ' ' ' ' ' % % %
(ArbolOrden)

Cdigo 8.22 Bsqueda del registro con el nombre dado o u


2280 2290 2300 2310 2320 2330 2340 2350 2360 2370 2380 2390 2400 2410 2420 2430 2440 2450

/ 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 ) ; }

8.3 *Ordenamiento usando rboles a

412

Figura 8.14

Bsqueda de una subcadena u

$ 'subrbol vac o ' a ' ' ' ' ' ' ' ' ' 'cadena en ra ' z ' ' ' ' ' ' ' ' ' ' ' &Hay rbol izquierdo Busca cadena a ' en subrbol ' a ' ' ' ' ' ' ' 'donde $ nulo ' ' ' ' ' ' ' ' ' ' ' 'Hay subrbol derecho ' a ' %

!
regresa nulo

!
regresa la ra z

donde

Busca cadena en
subrbol izquierdo a

!
Regresa donde

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 arbol 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 arbol. El algoritmo para esta bsqueda se muestra en la gura 8.14 mientras que la programacin se u o muestra en el listado 8.23.

Cdigo 8.23 Bsqueda de subcadena en determinado campo o u


1770 1780 1790 1800 1810 1820 1830 1840 1850 1860 1870

(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 p a r a r . @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 ) ; }

413

Ordenamientos usando estructuras de datos

Cdigo 8.23 Bsqueda de subcadena en determinado campo o u


1770 1780 1790 1800 1810 1820 1830 1840 1850 1860 1870 1880 1890 1900 1910 1920 1930 1940 1950 1960 1970 1980 1990 2000 2010 2020 2030 2040

(ArbolOrden) 2/2

/ 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.15 en la siguiente pgina. La programacin de este algoritmo se puede a o ver en el listado 8.24 en la siguiente pgina. a

8.3 *Ordenamiento usando rboles a

414

Figura 8.15

Seleccin de registros que cumplen una condicin o o $ # ' a ' 'Hay hijo izquierdo Visita subrbol ' ' ' ' izquierdo ' ' ' ' ' ' $ ' ' ' ' ' & ' ' 'Hay hijo izquierdo ' ' ' 'r ' % ' ' ' $ ' ' ' ' subcadena en ! ' ' ' ' ' ' ' Reporta el registro ' ' ' ' z ' ' & ra & z Visita subrbol Procesa la ra a ' ' ' ' ' subcadena en # ' ' ' ' ' ' ' ' ' % ' ' ra z r ' ' # ' ' ' Visita subrbol a ' 'Hay hijo derecho ' ' ' ' derecho ' ' ' ' ' ' $ ' ' ' ' ' & ' ' 'Hay hijo derecho ' ' ' 'r ' % %

Cdigo 8.24 Listado de registros que contienen a una subcadena o


2270 2280 2290 2300 2310 2320 2330 2340 2350 2360 2370 2380 2390 2400 2410

(ArbolOrden) 1/2

/ Imprime 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 i n t c u a l Con c u l campo s e d e s e a c o m p a r a r . 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 ( i n t c u a l , S t r i n g s u b c a d ) { subcad = subcad . trim ( ) . toLowerCase ( ) ; int cuantos = 0; i f ( r a i z == n u l l ) System . o u t . p r i n t l n ( "No hay registros en la base de" + " datos " ) ; else c u a n t o s = b u s c a Ca z a n ( cons , c u a l , subcad , r a i z ) ; i f ( c u a n t o s == 0 ) System . o u t . p r i n t l n ( "No hay registros que cacen ." ) ; }

415

Ordenamientos usando estructuras de datos

Cdigo 8.24 Listado de registros que contienen a subcadena o


2420 2430 2440 2450 2460 2470 2480 2490 2500 2510 2520 2530 2540 2550 2560 2570 2580 2590 2600

(ArbolOrden) 2/2

/ 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 u a l de t i p o <code>i n t </code >. Campo en e l que s e va a b u s c a r . @param s u b c a d de t i p o <code>S t r i n g </code >: Cadena a b u s c a r . @param r a i z t i p o <code>A r b o l E s t u d i a n t e </code >: 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 ( 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 Ca z a n ( 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) { System . o u t . p r i n t l n ( 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 Ca z a n ( 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 arbol. Si el nodo es una hoja, o 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.25 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

8.3 *Ordenamiento usando rboles a

416 (ArbolOrden)

Cdigo 8.25 Localizacin del padre de un nodo o o


1470 1480 1490 1500 1510 1520 1530 1540 1550 1560 1570 1580 1590 1600 1610 1620 1630 1640 1650 1660 1670 1680 1690

/ 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 r a i z t i p o <code>A r b o l E s t u d i a n t e </code >: 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 ( ArbolEstudiante 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 o a la gura 8.7 en la pgina 400 y supongamos que deseamos eliminar el nodo etia o quetado con E. ACmo reacomodamos el arbol de tal manera que se conserve el orden correcto? La respuesta es que tenemos que intercambiar a ese nodo por el nodo menor de su subrbol derecho. APor qu? Si colocamos al nodo menor a e del subrbol derecho en lugar del que deseamos eliminar, se sigue cumpliendo que a todos los que estn en el subrbol izquierdo son menores que l, mientras que e a e todos los que estn en el subrbol derecho son mayores o iguales que l: e 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.16 en la pgina opuesta. a a La programacin de este mtodo se muestra en el listado 8.26. o e

417

Ordenamientos usando estructuras de datos

Figura 8.16

Algoritmo para encontrar el menor de un subrbol a $ 'Baja a hijo derecho ' ' ' &
Encuentra el menor

' ' ' ' %

Baja por hijo izquierdo (mientras haya)

Regresa el ultimo visitado

Cdigo 8.26 Localiza al menor del subrbol derecho o a


1380 1390 1400 1410 1420 1430 1440 1450 1460 1470 1480 1490 / 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.

8.3 *Ordenamiento usando rboles a

418

Otra situacin a considerar es cuando se desea eliminar a la ra del arbol. En o z 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.17 en la pgina opuesta. La programacin del a o mtodo se puede ver en el listado 8.27 en la pgina 420. e a

419

Ordenamientos usando estructuras de datos

Figura 8.17

Eliminacin de un nodo en un rbol o a

$ $ 'El nodo es hoja ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'El nodo tiene slo ' ' ' o ' ' ' ' ' ' ' subrbol derecho ' ' a ' ' ' ' ' ' ' ' ' ' ' ' ' & ' ' 'nodo == ra ' z El nodo tiene slo ' o ' ' ' ' ' 'subrbol izquierdo ' ' a ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'El nodo tiene ' ' ' ' ' ' ' ' ' ' ' ' ambos hijos ' ' ' % ' ' ' & Elimina ' $ nodo ' ' ' 'El nodo es hoja ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'El nodo tiene slo ' ' o ' ' ' ' ' ' ' ' subrbol derecho ' ' a ' ' ' ' ' ' ' ' ' ' ' ' ' & ' 'nodo == ra ' z El nodo tiene slo ' o ' ' ' ' ' ' 'subrbol izquierdo ' ' a ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'El nodo tiene ' ' ' ' ' ' ' ' ' ' ' ' ambos hijos ' ' % %

!
Elimina a la ra z

Pon al subrbol derecho a en la ra z

Pon al subrbol izquierdo a en la ra z

$ 'Localiza menor en subrbol derecho a ' & 'Intercambia a menor y nodo ' %
Elimina a quien qued en menor o

!
Anula el apuntador del padre

Sube al subrbol derecho a al lugar del nodo

Sube al subrbol izquierdo a al lugar del nodo

$ 'Localiza menor en subrbol derecho a ' & 'Intercambia a menor y nodo ' %
Elimina a quien qued en menor o

8.3 *Ordenamiento usando rboles a

420 (ArbolOrden) 1/3

Cdigo 8.27 Eliminacin de un nodo en el rbol o o a


570 580 590 600 610 620 630 640 650 660 670 680 690 700 710 720 730 740 750 760 770 780 790 800 810 820 830 840 850 860 870 880 890 900 910 920 930 940 950 960 970 980 990 1000 1010 1020 1030

/ 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 ) ;

421

Ordenamientos usando estructuras de datos

Cdigo 8.27 Eliminacin de un nodo en el rbol o o a


1040 1050 1060 1070 1080 1090 1100 1110 1120 1130 1140 1150 1160 1170 1180 1190 1200 1210 1220 1230 1240 1250 1260 1270 1280 1290 1300 1310 1320 1330 1340 1350 1360 1370 1380 1390 1400 1410 1420 1430 1440 1450 1460 1470 1480

(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 d o 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

422 (ArbolOrden) 3/3


{

Cdigo 8.27 Eliminacin de un nodo en el rbol o o a


1490 1500 1510 1520 1530 1540 1550 1560 1570 1580 1590 1600 1610 1620 1630 1640 1650 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 arbol.

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 arbol. Asimismo, todos los parmetros o va 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

423

Ordenamientos usando estructuras de datos

Ejercicios
8.1.- Supongamos que tenemos un arreglo de enteros ordenados, ocupando posiciones consecutivas en el arreglo. Podemos hacer una bsqueda muy eciente u en el arreglo usando una bsqueda binaria, que funciona de la siguiente mau nera: a) Se considera al elemento en la posicin correspondiente a la mitad del o arreglo. 1.1. Si es el que buscamos, ya terminamos. 1.2. Si este elemento es mayor que el que buscamos, repetimos la bsqueu da en la mitad izquierda del arreglo. 1.3. Si este elemento es menor que el que buscamos, repetimos la bsqueda en la mitad derecha del arreglo. u b) Terminamos cuando el arreglo restante por buscar slo tenga una poo sicin y no contenga el entero que buscamos. o Disea, programa y prueba la bsqueda binaria en un arreglo. n u 8.2.- Tienes una lista de enteros que est ordenada y almacenada en lugares cona tiguos de un arreglo. Disea, programa y prueba un mtodo para insertar a n e un nuevo entero en el lugar que le corresponde dado un entero duplicado, el ultimo que ingresa va despus del primero que ingresa, pero s debe aparecer e tantas veces como se presente. 8.3.- Para el ejercicio anterior, inserta cdigo en tu mtodo que cuente el nmero o e u de movimientos que se tienen que hacer cada vez que se ingresa un elemento. 8.4.- Como en el ejercicio anterior, pero el mtodo debe eliminar al entero en la e posicin i, manteniendo los registros ocupando posiciones consecutivas. Haz o el cdigo necesario para que se lleve la cuenta del nmero de movimientos o u necesarios para eliminar a un elemento. 8.5.- Disea, programa y prueba un mtodo que mezcle los enteros de dos arreglos n e que vienen en orden cada uno, para obtener un unico arreglo que incluya a ambas listas en orden. 8.6.- Disea, programa y prueba un mtodo que incorpore a una lista de enteros n e que se encuentran en un arreglo a otra lista de enteros que se encuentran en otro arreglo. Se debe considerar el agrandar al arreglo antrin. Incorpora o al mtodo el nmero de movimientos y copias que debes hacer. e u

8. Ejercicios

424 8.7.- Lo mismo que el ejercicio anterior, excepto que los enteros se encuentran en una lista ligada. Incorpora al mtodo el nmero de movimientos y copias e u que debes hacer. 8.8.- Cuando decimos que los arreglos y las listas ligadas corresponden a la misma estructura de datos, a qu se reere esta armacin? e o 8.9.- Cmo se representar con listas los arreglos de dos dimensiones? o an 8.10.- Si tenemos una lista de enteros cuyas magnitudes son relativamente pequeas (por ejemplo, nmeros entre 100 y 100, podr n u amos usar arreglos sin exigir que todos los elementos estn en posiciones consecutivas. Por ejeme plo, a cada entero le podemos asignar la posicin i 100 y acomodar o amos a todos los enteros. Dado este esquema: (a) Cmo manejas los nmeros repetidos? o u (b) Cmo sabes cuntos enteros estn realmente en la lista? o a a (c) Qu haces si deseas alguna informacin adicional al entero? e o Nota: A este tipo de almacenamiento se le conoce como funcin de dispero sin, donde a partir de los datos se calcula una posicin en la que se o o va a acomodar el registro.

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 la pila 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).

9.1 Tipos de errores

426

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


Una excepcin incluye dos conceptos importantes: lanzar la excepcin, que o o sucede en el punto donde algo anmalo se presenta, y manejar la excepcin, que o o 2 dene cmo deber reaccionar la Mquina Virtual de Java cuando se presenta la o a a excepcin. o Entre las excepciones que pueden causarse (o lanzarse) en la ejecucin de una o aplicacin hay de dos tipos: aquellas que tiene que ser vigiladas (la aplicacin o o tiene que incluir cdigo por si el error sucede) y aquellas que, como son muy o comunes o implican un error en la lgica del programa que hay que corregir, no o hay que vigilarlas aunque se puede hacerlo. Bajo comunes queremos decir que se pueden presentar casi en cualquier programa, por simple que ste sea, y en e muchos tipos de enunciados, por lo que si estuvisemos obligados a vigilarlas el e cdigo crecer mucho y se har muy poco claro. o a a Las excepciones que no deben ser vigiladas, que revisaremos primero, son conocidas como de tiempo de ejecucin (RuntimeException)3 . Para ejemplicarlas o juntaremos varios mtodos en los que las excepciones pueden ser lanzadas, el e error en ejecucin puede ocurrir en una clase ExcepcionesRuntimeSimples e ireo mos presentando cada una de las que nos interesan. Implementaremos para cada una un mtodo y elegiremos el mtodo a ejecutar pasando un argumento a main; e e la implementacin de esta clase se encuentra en el listado 9.1. Iremos presentando o cada uno de los mtodos invocados conforme lo discutamos. 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 En adelante JVM. 3 El nombre no es muy adecuado ya que todas las excepciones se presentan en tiempo de ejecucin. o
1

427

Manejo de errores en ejecucin o


ExcepcionesRuntimeSimples (1/3)

Cdigo 9.1 Clase para causar excepciones o


10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 210 220 230 240 250 260 270 280 290 300 310 320 330 340 350 360 370 380 390 400 410 420 430 440 450 460 470 480 490 package e x c e p c i o n e s ; import j a v a . u t i l . S c a n n e r ; import j a v a . u t i l . I n p u t M i s m a t c h E x c e p t i o n ;

public class ExcepcionesRuntimeSimples { / E j e m p l i f i c a A r i t h m e t i c E x c e p t i o n / p r i v a t e s t a t i c double c a l c u l a ( i n t k ) { i n t n = 100 / k ; double x = 1000 / k ; r e t u r n Math . max ( n , x ) ; } / E j e m p l i f i c a A r r a y S t o r e E x c e p t i o n / private s t a t i c void guardaObjeto ( Object [ ] A, i n t i , Object o b j e t o ){ A[ i ] = objeto ; } / E j e m p l i f i c a C l a s s C a s t E x c e p t i o n / private s t a t i c void i n t e r p r e t a O b j e t o ( I n t e g e r [ ] A, i n t i , Object objeto ) { A[ i ] = ( I n t e g e r ) objeto ; } / E j e m p l i f i c a I l l e g a l A r g u m e n t E x c e p t i o n / p r i v a t e s t a t i c double r a i z C u a r t a ( double x ) { r e t u r n Math . s q r t ( Math . s q r t ( x ) ) ; } / E j e m p l i f i c a I n d e x O u t O f B o u n d s E x c e p t i o n / p r i v a t e s t a t i c i n t maximo ( i n t [ ] v a l o r e s , i n t maxInd ) { i n t maxi = v a l o r e s [ 0 ] ; f o r ( i n t i =0; i <= maxInd ; i ++) { try { i f ( maxi < v a l o r e s [ i ] ) { maxi = v a l o r e s [ i ] ; } } catch ( A r r a y I n d e x O u t O f B o u n d s E x c e p t i o n e ) { throw new A r r a y I n d e x O u t O f B o u n d s E x c e p t i o n ( " tamanho del arreglo :" + v a l o r e s . l e n g t h +"\n" + " valor de i="+i ) ; } } r e t u r n maxi ; } / E j e m p l i f i c a N e g a t i v e A r r a y S i z e E x c e p t i o n / p r i v a t e s t a t i c i n t [ ] l l e n a A r r e g l o ( i n t tamanho , i n t v a l o r ) { i n t [ ] a r r e g l o = new i n t [ tamanho ] ; f o r ( i n t i = 0 ; i < tamanho ; i ++) { arreglo [ i ] = valor ; } return a r r e g l o ; }

9.1 Tipos de errores

428
ExcepcionesRuntimeSimples (2/3)

Cdigo 9.1 Clase para causar excepciones o


500 510 520 530 540 550 560 570 580 590 600 610 620 630 640 650 660 670 680 690 700 710 720 730 740 750 760 770 780 790 800 810 820 830 840 850 860 870 880 890 900 910 920 930 940

/ E j e m p l i f i c a N u l l P o i n t e r E x c e p t i o n / private s t a t i c void l l e n a A r r e g l o ( I n t e g e r [ ] a r r e g l o , i n t v a l o r ){ f o r ( i n t i = 0 ; i < a r r e g l o . l e n g t h ; i ++) { a r r e g l o [ i ] = new I n t e g e r ( v a l o r ) ; } } / E j e m p l i f i c a I n p u t M i s m a t c h E x c e p t i o n de j a v a . u t i l / private s t a t i c void leeNumeros ( ) { S c a n n e r s I n = new S c a n n e r ( System . i n ) ; while ( true ) { System . o u t . p r i n t l n ( "Dame un n mero" ) ; u i n t num = s I n . n e x t I n t ( ) ; System . o u t . p r i n t l n ( "El n mero le u do es:" + num ) ; } System . o u t . p r i n t l n ( "Sal del while " ) ; } 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 ) { S c a n n e r s I n = new S c a n n e r ( a r g s [ 0 ] ) ; int cual = sIn . nextInt ( ); I n t e g e r [ ] i n t A r r a y = new I n t e g e r [ 5 ] ; I n t e g e r [ ] malos = n u l l ; switch ( c u a l ) { case 1 : // A r i t h m e t i c System . o u t . p r i n t l n ( " Llamamos a calcula (0):" + calcula (0)); break ; case 2 : // A r r a y S t o r e System . o u t . p r i n t l n ( " ArrayStoreException :" ) ; g u a r d a O b j e t o ( i n t A r r a y , 0 , "abc" ) ; break ; case 3 : // C l a s s C a s t E x c e p t i o n System . o u t . p r i n t l n ( " ClassCastExceptionException " ) ; i n t e r p r e t a O b j e t o ( i n t A r r a y , 0 , "abc" ) ; break ; case 4 : // I l l e g a l A r g u m e n t E x c e p t i o n System . o u t . p r i n t l n ( " IllegalArgumentException " ) ; System . o u t . p r i n t l n ( " C lculo de ra z cuarta " a + raizCuarta ( 5.0)); break ; case 5 : // A r r a y I n d e x O u t O f B o u n d s E x c e p t i o n int [ ] enteros = {6 ,2 ,6 ,8}; System . o u t . p r i n t l n ( " Llamamos a maximo (enteros , 4)=" + maximo ( e n t e r o s , 4 ) ) ; break ;

429

Manejo de errores en ejecucin o


ExcepcionesRuntimeSimples (3/3)

Cdigo 9.1 Clase para causar excepciones o

950 case 6 : // N e g a t i v e A r r a y S i z E x c e p t i o n 960 i n t [ ] b u e n o s = l l e n a A r r e g l o ( 5 , 1); 970 System . o u t . p r i n t l n ( b u e n o s . l e n g t h ) ; 980 break ; 990 case 7 : // N u l l P o i n t e r E x c e p t i o n 1000 System . o u t . p r i n t l n ( " Llenando arreglo nulo" ) ; 1010 l l e n a A r r e g l o ( malos , 7 ) ; 1020 break ; 1030 case 8 : // I n p u t M i s m a t c h E x c e p t i o n 1040 System . o u t . p r i n t l n ( " Leyendo enteros " ) ; 1050 leeNumeros ( ) ; 1060 break ; 1070 } 1080 } // main 1090 }

Como los mtodos no vigilan las excepciones excepto por el de ArrayIndee xOutOfBoundsException, y las estamos provocando, se va a tener que invocar la ejecucin del mismo por cada excepcin que queramos causar. En la l o o nea 680 creamos un Scanner que se va a aplicar a una cadena, el primer argumento pasado en la ejecucin. En la l o nea 680 leemos esa cadena para extraer el valor del selector de casos. Revisaremos ahora algunas de las excepciones de tiempo de ejecucin. o
ArithmeticException

Es lanzada cuando el programa intenta hacer una operacin o aritmtica invlida, como la divisin entre enteros que tiene a 0 (cero) como e a o divisor4 . En el mtodo calcula l e neas 70 a 110, por ejemplo, puede presentarse este tipo de excepcin, si es invocado con los argumentos equivocados. o Si el mtodo se invoca con un valor k  0 el mtodo se ejecuta bien y no tiene e e problemas. En cambio, si se le invoca con k  0, se lanzar la excepcin. El a o resultado de lanzar la excepcin es que la Mquina Virtual de Java empieza o a a buscar, en la pila de ejecucin, siguiendo la cadena de llamadas de los o mtodos, para ver si encuentra un manejador de la excepcin; si lo encuentra, e o hace lo conducente; si no lo encuentra, el programa aborta con un mensaje de error que incluye la historia de llamadas la pila de ejecucin. Un error o provocado por el procedimiento que acabamos de mostrar se muestra en la gura 9.1.

Java contempla el valor innity, por lo que una divisin donde el dividendo sea un nmero o u real y el divisor 0 no lanza esta excepcin, sino que entrega innity como resultado. o

9.1 Tipos de errores

430

Figura 9.1

Ejecucin de AritmExc o

elisa@lambda :/ programas$ j a v a e x c e p c i o n e s / ExcepcionesRuntimeSimples 1 E x c e p t i o n i n t h r e a d "main" j a v a . l a n g . A r i t h m e t i c E x c e p t i o n : / by z e r o at excepciones . ExcepcionesRuntimeSimples . c a l c u l a ( ExcepcionesRun timeSimples . java :7) a t e x c e p c i o n e s . E x c e p c i o n e s R u n t i m e S i m p l e s . main ( E x c e p c i o n e s R u n t i meSimples . j a v a : 6 3 ) elisa@lambda :/ programas$

ArrayStoreException

Es lanzada cuando el programa intenta guardar un objeto de tipo errneo en un arreglo. Veamos el ejemplo en las l o neas 130 a 150 del listado 9.1. En la l nea 140, 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

programas$ j a v a e x c e p c i o n e s / ExcepcionesRuntimeSimples 2 ArrayStoreException : E x c e p t i o n i n t h r e a d "main" j a v a . l a n g . A r r a y S t o r e E x c e p t i o n : j a v a . l a n g . S t r i n g at excepciones . ExcepcionesRuntimeSimples . guardaObjeto ( Excepcion esRuntimeSimples . java :13) a t e x c e p c i o n e s . E x c e p c i o n e s R u n t i m e S i m p l e s . main ( E x c e p c i o n e s R u n t i m eSimples . java :68) elisa@lambda :/ programas$

ClassCastException

Es lanzada cuando el programa intenta aplicar un cast de una clase a un objeto de otra clase, y la conversin no est permitida (no se trata o a de una subclase). Al invocarse al programa con el argumento 2 se ejecuta la l nea 790, que consiste de una llamada al mtodo que se encuentra en las e l neas 170 a 200. Al ejecutarse la l nea 190 se lanza esta excepcin. o 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 190). Sin embargo, al pasarle como parmetro una a cadena, que es sintcticamente compatible con un objeto, el programa no a

431

Manejo de errores en ejecucin o

puede hacer la coercin, por lo que lanza una excepcin. La ejecucin del o o o programa ClassCE la podemos ver en la gura 9.3.

Figura 9.3

Ejecucin del programa ClassCE o

. . . programas$ j a v a e x c e p c i o n e s / ExcepcionesRuntimeSimples 3 ClassCastExceptionException E x c e p t i o n i n t h r e a d "main" j a v a . l a n g . C l a s s C a s t E x c e p t i o n : j a v a . l a n g . S t r i n g c a n n o t be c a s t t o j a v a . l a n g . I n t e g e r a t e x c e p c i o n e s . E x c e p c i o n e s R u n t i m e S i m p l e s . i n t e r p r e t a O b j e t o ( Excep cionesRuntimeSimples . java :18) a t e x c e p c i o n e s . E x c e p c i o n e s R u n t i m e S i m p l e s . main ( E x c e p c i o n e s R u n t i m eSimples . java :72) e l i s a @ l a m b d a : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $

Esta excepcin es lanzada cuando algn mtodo de Java o u e o denido 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. Se z u encuentra ejemplicada en las l neas 220 a 240 en el mtodo raizCuarta, cuya e llamada se encuentra en la l nea 880. La ejecucin muestra algo muy similar o a lo que hemos estado mostrando, por lo que ya no aparece. Lo unico que cambia es el nombre de la excepcin y las l o neas donde es lanzada ya no se muestra la ejecucin pues toma exactamente la misma forma que los dos o ejemplos anteriores. IllegalMonitorStateException Tiene que ver con sincronizacin de procesos. Lo vereo mos ms adelante. a IndexOutOfBoundsException Es lanzada 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. Se ejemplica con la ejecucin seleccionando el caso o 5 en main. Al ejecutarse la l nea 300 con i valiendo 4, como el arreglo que se pas como parmetro tiene elementos del 0 al 3 (4 elementos), el o a ndice 4 lanza la excepcin. o NegativeArraySizeException Se lanza si es que se intenta crear un arreglo con un nmero negativo de elementos. Se ejemplica en la llamada de la l u nea 960. Nuevamente los parmetros son de los tipos especicados en el mtodo, por a e lo que no hay error sintctico. Sin embargo, al no poderse construir el arreglo a en la l nea 440, ya que el tamao es negativo, se lanza la excepcin. n o NullPointerException Se lanza si se trata de usar una referencia nula para acceder a un objeto que no existe. Se invoca al mtodo que ejemplica a esta excepcin e o en la l nea 1010 y la excepcin se lanza desde la l o nea 530, al tratar de asignarle asignar un objeto (Integer) en un lugar de un arreglo que no ha
IllegalArgumentException

9.1 Tipos de errores

432

sido construido. Nuevamente este error no se puede detectar sintcticamente, a pues el uso de los distintos tipos y clases es correcto. 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), que no revisaremos en esta ocasin. o java.util.EmptyStackException Es lanzada cuando un programa, que utiliza la clase Stack de Java intenta tener acceso a un elemento de la pila cuando la pila est vac a a. java.util.NoSuchElementException Error relacionado con enumeraciones. Una enumeracin es una lista de variables que asumen el orden en el que estn o a listadas. Cada una de las variables corresponde a una constante del tipo dado a la enumeracin. o java.util.InputMismatchException Esta excepcin es lanzada cuando se intenta leer o un cierto tipo de dato pero lo que se le da es un dato de otro tipo. Por ejemplo, se le indica a un objeto de la clase Scanner que lea un entero con nextInt() y se encuentra caracteres que no puede interp[retar como entero, como una letra o un s mbolo de puntuacin antes o inmediatament3e despus o e del nmero propiamente dicho. u 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 atrapar 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 atrapan la excepcin es a lo que se conoce como el manejador de o o la excepcin. o Se recomienda enfticamente que las excepciones de tiempo de ejecucin a o (RuntimeException) no sean atrapadas (atrapadas). Este tipo de excepcin indica o errores de diseo en la aplicacin, por lo que si se decide atraparlas esto se den o ber hacer unicamente en una etapa de depuracin del programa, con el objeto a o de obtener un poco ms de informacin sobre la causa de la excepcin. a o o

433

Manejo de errores en ejecucin 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

xlista de clases de Excepcionesy

y en el cuerpo del mtodo, o de alguno de los mtodos invocados dentro de ese e e cuerpo, deber aparecer un enunciado a throw new xconstructor de una excepciny o En el caso de las excepciones en tiempo de ejecucin RuntimeException los o 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 JVM busca en su u e o entorno inmediato de ejecucin (llamadas en la pila de ejecucin) al manejador de o o la excepcin. Si lo encuentra, lo ejecuta, y ya sea que procese la excepcin o que o o simplemente la propague (la vuelva a lanzar). Si en el entorno inmediato no se encuentra al manejador de la excepcin, la JVM sigue buscando hacia afuera en o la cadena de llamadas del mtodo en cuestin a ver si encuentra a algn manejador e o u de la excepcin. Si no lo encuentra en la aplicacin se lo pasa a la JVM, que procede o o a suspender el 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

9.3 Cmo detectar y atrapar una excepcin o o


public Exception(String msg)

434

Construye el objeto pasndole una cadena que proa porciona informacin adicional a la del nombre de la clase. o

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 msg. o

Mtodos de la pila (stack) de ejecucin: e o


public native Throwable llInStackTrace()

Coloca en el objeto una copia de la cadena dinmica de la pila de ejecucin en ese momento. a o public void printStackTrace() Escribe en la pantalla el contenido de la pila 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 la pila de ejecucin producida 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

435

Manejo de errores en ejecucin o

9.3 Cmo detectar y atrapar 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 las llae madas de tiempo de ejecucin (RuntimeException) y que s deben ser vigilao das y manejadas de alguna manera. Entre ellas podemos mencionar las que son lanzadas en relacin con procesos de entrada y salida (IOException); cuando duo rante la ejecucin de una aplicacin no se encuentra una clase que se debe cargar o o (ClassNotFoundException); cuando se pretende hacer una copia de un objeto que no es duplicable (CloneNotSupportedException); cuando se da alguna interrupcin en o un proceso que se est ejecutando (InterruptedException); y algunas ms. Adems, a a a el usuario puede declarar sus propias clases de excepciones, que pueden extender a una clase particular de excepciones o a la clase genrica Exception. e

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 en la siguiente pgina5 . a

En este caso subrayamos lo que forma parte del enunciado en Java para distinguirlo de lo que marca repeticin de algn segmento. o u

9.3 Cmo detectar y atrapar una excepcin o o

436

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

catch ( xtipo de excepciny o

xidentify )

Semantica: Se pueden agrupar tantos enunciados como se desee en la clusula try, a por lo que al nal de ella se puede estar reaccionando frente a distintas excepciones. Se va a lanzar unicamente la primera excepcin que se presente, o pues al ocurrir el error no se contina ejecutando el bloque try. De manera u similar a como se hace en un switch, se elige uno y solo un manejador de excepciones, utilizando la primera excepcin que calique con ser del tipo o especicado en las clusulas catch. a

La parte que aparece a continuacin de cada catch corresponde al manejador o 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 atrapada y manejada dentro de ese bloque. En general, una excepcin que o extiende a otra puede ser atrapada por cualquier manejador para cualquiera de sus superclases. Una vez que se lista el manejador para alguna superclase en uno de los bloques catch, no tiene sentido listar manejadores para las subclases despus e de l. e 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 del registro de o o activacin actual a aquel desde el cual fue invocado buscando un manejador o

437

Manejo de errores en ejecucin o

de la excepcin, y as sucesivamente hasta que encuentra uno o bien la JVM o aborte el programa porque no hay en la pila de ejecucin ningn mtodo en o u e el que se maneje la excepcin. o 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.2 atrapa 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, que en este caso consiste o unicamente en seguir ejecutando main imprimiendo un mensaje de despedida.

Cdigo 9.2 Manejo de una excepcin a travs de la superclase o o e


10 20 30 40 package e x c e p c i o n e s ; import j a v a . i o . ; import j a v a . u t i l . S c a n n e r ; import j a v a . u t i l . I n p u t M i s m a t c h E x c e p t i o n ;

CatchExc (1/2)

9.3 Cmo detectar y atrapar una excepcin o o

438
CatchExc (2/2)

Cdigo 9.2 Manejo de una excepcin a travs de la superclase o o e

60 p u b l i c c l a s s CatchExc { 70 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 ) { 80 int k = 0; 90 int c ; 100 int i = 3; 110 i n t [ ] e n t e r o s = new i n t [ 3 ] ; 120 S c a n n e r s c a n = new S c a n n e r ( System . i n ) ; 130 try { 140 System . o u t . p r i n t l n ( "Dame un entero para trabajar " ) ; 150 w h i l e ( ( c = System . i n . r e a d ( ) ) >= 0 && c <= 9 ) 160 k = k 10 + ( c ( i n t ) 0 ) ; 170 System . o u t . p r i n t l n ( "Lei el valor " + k ) ; 180 c = 1000/ k ; 190 System . o u t . p r i n t l n ( "El valor final de c es " + c ) ; 200 System . o u t . p r i n t l n ( "Dame otro entero :" ) ; 210 c = scan . n e x t I n t ( ) ; 220 i f ( c != 0 ) 230 System . o u t . p r i n t l n ( " Divisi n :" + ( k / c ) ) ; o 240 enteros [ i ] = c ; 250 } catch ( E x c e p t i o n e ) { 260 System . o u t . p r i n t l n ( "La excepci n es de la clase : \n\t" o 270 + e . getClass ()); 280 } 290 System . o u t . p r i n t l n ( "A punto de salir normalmente " 300 + "del metodo main" ) ; 310 } 320 }

Este programa atrapa cualquier excepcin que se presente en el bloque try y la o maneja, permitiendo que el programa termine normalmente. Se pueden presentar las siguientes excepciones: L nea 180: Puede haber una divisin entre 0 (ArithmeticaException) pues la k es o calculada en el programa y depende de los datos de entrada. L nea 210: El usuario puede proporcionar al dispositivo de lectura (Scanner) algo que no sea un entero. L nea 240: Si la ejecucin llega a esta l o nea se produce el error de ndice invlido, a pues el arreglo slo tiene tres elementos. o En las guras 9.5 a 9.7 mostramos ejecuciones en los que se presenta cada uno de estos errores (se lanza la excepcin). o En el primer caso gura 9.5 cuando la aplicacin pide el segundo entero se o

439

Manejo de errores en ejecucin o

teclean caracteres l neas 60 y 70, por lo que se lanza una excepcin que indica o que la entrada no coincide con lo esperado InputMismatchException. En el segundo caso gura 9.6 se proporciona el valor 0 para la variable k, lo que provoca una divisin entre 0 y el lanzamiento de la excepcin correspondiente, o o ArithmeticException.

Figura 9.5
10 20 30 40 50 60 70 80 90 100 110

Excepciones de tiempo de ejecucin atrapadas con una superclase (A) o


. . . p r o g r a m a s $ j a v a e x c e p c i o n e s / CatchExc Dame un e n t e r o p a r a t r a b a j a r 35 L e i e l v a l o r 35 E l v a l o r f i n a l de c e s 28 Dame o t r o e n t e r o : lmn La e x c e p c i n e s de l a c l a s e : o class java . u t i l . InputMismatchException A punto de s a l i r normalmente d e l metodo main e l i s a @ l a m b d a : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $

Figura 9.6
10 20 30 40 50 60 70 80

Excepciones de tiempo de ejecucin atrapadas con una superclase (B) o


. . . p r o g r a m a s $ j a v a e x c e p c i o n e s / CatchExc Dame un e n t e r o p a r a t r a b a j a r Lei el valor 0 La e x c e p c i n e s de l a c l a s e : o class java . lang . ArithmeticException A punto de s a l i r normalmente d e l metodo main e l i s a @ l a m b d a : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $

En el tercer caso los datos se proporcionan bien pero se intenta acceder al elemento con ndice 3 de un arreglo, cuyos ndices vlidos son del 0 al 2, por lo a que se lanza la excepcin correspondiente. o

Figura 9.7
10 20 30 40 50 60 70 80 90 100 110 120

Excepciones de tiempo de ejecucin atrapadas con una superclase (C) o


. . . p r o g r a m a s $ j a v a e x c e p c i o n e s / CatchExc Dame un e n t e r o p a r a t r a b a j a r 45 L e i e l v a l o r 45 E l v a l o r f i n a l de c e s 22 Dame o t r o e n t e r o : 10 Divisin :4 o La e x c e p c i n e s de l a c l a s e : o class java . lang . ArrayIndexOutOfBoundsException A punto de s a l i r normalmente d e l metodo main e l i s a @ l a m b d a : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $

9.3 Cmo detectar y atrapar una excepcin o o

440

Si queremos que el programa se interrumpa despus de lanzada una excepcin, e o podemos relanzarla en el manejador bloque catch. En este caso, el mtodo e en el que esto se hace debe avisar que se va a lanzar una excepcin, ya que o habr una salida brusca del mismo. En el listado 9.3 modicamos la clase CatchExc a CatchExc1 para que relance la excepcin y termine la ejecucin. En la gura 9.8 o o en la pgina opuesta vemos la ejecucin de esta nueva clase. a o

Cdigo 9.3 La excepcin es atrapada y relanzada o o


10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 210 220 230 240 250 260 270 280 290 300 310 320 330 340 350 360 package e x c e p c i o n e s ; import j a v a . i o . ; import j a v a . u t i l . S c a n n e r ; p u b l i c c l a s s CatchExc1 {

CatchExc1

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 E x c e p t i o n { int k = 0; int c ; int i = 3; i n t [ ] e n t e r o s = new i n t [ 3 ] ; S c a n n e r s c a n = new S c a n n e r ( System . i n ) ; try { 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 ( "Lei el valor " + k ) ; c = 1000/ k ; System . o u t . p r i n t l n ( "El valor final de c es " + c ) ; System . o u t . p r i n t l n ( "Dame otro entero :" ) ; c = scan . n e x t I n t ( ) ; i f ( c != 0 ) { System . o u t . p r i n t l n ( " Divisi n :" + ( k / c ) ) ; o } enteros [ i ] = c ; } catch ( E x c e p t i o n e ) { C l a s s en = e . g e t C l a s s ( ) ; System . o u t . p r i n t l n ( "La excepci n es de la clase : \n\t" o + en ) ; throw ( ( E x c e p t i o n ) en . n e w I n s t a n c e ( ) ) ; } System . o u t . p r i n t l n ( "A punto de salir normalmente " + "del metodo main" ) ; } }

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 270, se vuelve a relanzar en la

441

Manejo de errores en ejecucin o

l nea 310, por lo que la l nea 330 del programa, que se encuentra a continuacin del o bloque de la excepcin, ya no se ejecuta. En este ejemplo usamos varios mtodos o e de la clase Class, como getClass(), que regresa el tipo de la clase, y newInstance(), que construye un objeto de la clase especicada. La clase Class contiene muchos mtodos que se usan para obtener las descripciones de cualquier clase que e se encuentre disponible.

Figura 9.8
10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180

Ejecucin con relanzamiento de la excepcin o o

. . . p r o g r a m a s $ j a v a e x c e p c i o n e s / CatchExc1 Dame un e n t e r o p a r a t r a b a j a r 0 Lei el valor 0 La e x c e p c i n e s de l a c l a s e : o class java . lang . ArithmeticException E x c e p t i o n i n t h r e a d "main" j a v a . l a n g . A r i t h m e t i c E x c e p t i o n a t sun . r e f l e c t . N a t i v e C o n s t r u c t o r A c c e s s o r I m p l . n e w I n s t a n c e 0 ( N a t i v e Method ) a t sun . r e f l e c t . N a t i v e C o n s t r u c t o r A c c e s s o r I m p l . n e w I n s t a n c e ( N a t i v e C onstructorAccessorImpl . java :57) a t sun . r e f l e c t . D e l e g a t i n g C o n s t r u c t o r A c c e s s o r I m p l . n e w I n s t a n c e ( Del egatingConstructorAccessorImpl . java :45) at java . lang . r e f l e c t . Constructor . newInstance ( Constructor . java :532) at java . lang . Class . newInstance0 ( Class . java :372) at java . lang . Class . newInstance ( Class . java :325) a t e x c e p c i o n e s . CatchExc1 . main ( CatchExc1 . j a v a : 3 1 ) e l i s a @ e l i s a d e s k t o p : / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s $

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.4 y 9.5 en la siguiente pgina. a

Cdigo 9.4 Implementacin de excepciones propias o o

DivPorCeroException

10 package e x c e p c i o n e s ; 20 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 { 30 public DivPorCeroException () { 40 super ( ) ; 50 } 60 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 ) { 70 super ( msg ) ; 80 } 90 }

9.3 Cmo detectar y atrapar una excepcin o o

442
DivPorCeroUso

Cdigo 9.5 Deteccin de excepciones propias o o


10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 210 220 230 240 250 260 270 280 290 300 310

package e x c e p c i o n e s ; import j a v a . u t i l . S c a n n e r ; p u b l i c c l a s s D i v P o r Ce r o U s 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 raiz de numero " + " negativo !" ) ; 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 ) { // Aca l e e r i a m o s v a l o r p a r a r en v e z de c a l c u l a r l o a l a z a r 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 ) r = r ; // Aca 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 try { System . o u t . p r i n t l n ( "La raiz cuadrada de "+ r + " es " + sqrt ( 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 que construimos, DivPorCeroException, extiende a Arithmeo ticException y esta ultima no debe ser vigilada, la invocacin a sqrt no tendr o a que hacerse dentro de un bloque try. Asimismo, el mtodo sqrt no tendr por e a qu avisar que va a lanzar una excepcin. Sin embargo, en este ejemplo usamos e o ambos enunciados porque queremos ejemplicar su uso. Supongamos ahora que la excepcin hereda directamente a Exception o a cualo quiera que no sea RuntimeException, como se muestra en el listado 9.6 en la pgina a opuesta. En este caso, si en el archivo DivPorCeroUso.java comentamos las l neas 50, 220 y 250 a 290, el mtodo lanza una excepcin que no es de la clase RuntimeExe o ception, por lo que no puede dejar de avisar que va a lanzar una excepcin: al o compilar a la clase DivPorCeroUso, suponiendo que DivPorCeroException extiende a Exception, da el error de compilacin que se muestra en la gura 9.9. o

443

Manejo de errores en ejecucin o


DivPorCero2Exception

Cdigo 9.6 Excepcin que no se declara subclase de RuntimeException o o

10 package e x c e p c i o n e s ; 20 p u b l i c c l a s s D i v P o r C e r o 2 E x c e p t i o n extends E x c e p t i o n { 30 public DivPorCero2Exception () { 40 super ( ) ; 50 } 60 p u b l i c D i v P o r C e r o 2 E x c e p t i o n ( S t r i n g msg ) { 70 super ( msg ) ; 80 } 90 }

Figura 9.9
10 20 30 40 50 60 70 80 90 100 110 120 130

Excepcin que hereda de Exception o

c l a s s p a t h

CompileServer output : /home/ e l i s a / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s s o u r c e p a t h /home / e l i s a / ICC1 : / home/ e l i s a / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s t a r g e t 1 . 6 g / home/ e l i s a / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s / e x c e p c i o n e s / D i v P o r Ce r o U s o . j a v a /home/ e l i s a / ICC1 / n o t a s / n o t a s 2 0 1 0 / p r o g r a m a s / e x c e p c i o n e s / D i v P o r Ce r o U s o . j a va : 8 : u n r e p o r t e d e x c e p t i o n e x c e p c i o n e s . D i v P o r C e r o E x c e p t i o n ; must be cau g h t o r d e c l a r e d t o be thrown throw new D i v P o r C e r o E x c e p t i o n ( "Se pide raiz de numero negat ivo!" ) ; 1 error C o m p i l a t i o n e x i t e d a b n o r m a l l y w i t h code 1 a t Thu May 26 1 0 : 5 6 : 5 1

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 Como la excepcin se maneja totalmente dentro del mtodo en cuestin, que o e o 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: son 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.5 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, detectan 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

9.3 Cmo detectar y atrapar una excepcin o o

444

contexto de nuestra aplicacin. Una manera elegante de manejarlo es a travs de o e excepciones, como se muestra en los listados 9.7 a 9.9.

Cdigo 9.7 Excepciones del programador (I) o


10 20 30 40 50 60 70 80 90

RegDuplicadoException

package e x c e p c i o n e s ; 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.8 Uso de excepciones del programador (I) o


10 20 30 40 50 60 70 80 90

RegNoEncontradoException

package e x c e p c i o n e s ; 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 ) ; } }

Cdigo 9.9 Excepciones del programador y su uso (II) o


10 20 30 40 50 60 70 80 90 100 110

BaseDeDatos (1/2)

package e x c e p c i o n e s ; 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 ) ; ... }

445

Manejo de errores en ejecucin o


BaseDeDatos (2/2)

Cdigo 9.9 Excepciones del programador y su uso (II) o


120 130 140 150 160 170 180 190 200 210 220 230 240 250 260 270 280 290 300 310 320 330 340 350 360 370 380 390 400 410 420 430 440 450 460 470 480 490 500 510 520

... 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 a d e c u a 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 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 ) { // P ro d uc e un manejo ad e c u ad o 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.9 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

9.4 Las clases que extienden a Exception

446

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 240 a la l nea 350. Si es que estos mtodos lanzan la excepcin, ya sea en la e o l nea 260 o 290, ya no se ejecuta la l nea 270 en el primer caso y la l nea 300 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 slo eso, sino que unio 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 o Throwable. Por ejemplo, la clase RegNoEncontradoException que diseamos para n la base de datos pudiera proporcionar ms informacin al usuario que simplemena o te el mensaje de que no encontr al registro solicitado; podr proporcionar los o a registros inmediato anterior e inmediato posterior al usuario. En ese caso, deber a poder armar estos dos registros. Para ello, podr amos agregar a la clase dos cam-

447

Manejo de errores en ejecucin o

pos, uno para cada registro, y dos mtodos, el que localiza al elemento inmediato e anterior en la lista y el que localiza al inmediato posterior. En los listados 9.10 y 9.11 podemos ver un bosquejo de cmo se lograr esto. o a

Cdigo 9.10 Denicin de Excepciones propias o o

RegNoEncontradoException (1/2)

10 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 { 20 p r i v a t e R e g i s t r o regAnt , r e g P o s t ; 30 public RegNoEncontradoException ( ) { 40 super ( ) ; 50 } 60 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 ) { 70 super ( msg ) ; 80 } 90 public RegNoEncontradoException ( R e g i s t r o a n t e r i o r , 100 Registro actual ) { 110 regAnt = b u s c a A n t e r i o r ( padre ) ; 120 regPost = buscaPosterior ( actual ) ; 130 } 140 private Registro buscaAnterior ( Registro a n t e r i o r ) { 150 ... 160 } 170 private Registro buscaPosterior ( Actual ) { 180 ... 190 } 200 p u b l i c R e g i s t r o daRegAnt ( ) { 210 return regAnt ; 220 } 230 p u b l i c R e g i s t r o daRegPost ( ) { 240 return regPost ; 250 } 260 }

Cdigo 9.11 Denicin de excepciones propias (ejemplo) o o

Ejemplo (1/2)

10 p u b l i c c l a s s E j e m p l o { 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 ) { 30 ... 40 try { 50 i f ( a c t u a l . s i g == n u l l ) { 60 throw new 70 RegNoEncontradoException ( a c t u a l . g e t A n t e r i o r ( ) , 80 actual ); 90 } // f i n de i f 100 ... 110 } // f i n de t r y 120 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 ) {

9.4 Las clases que extienden a Exception

448
Ejemplo (2/2)

Cdigo 9.11 Denicin de excepciones propias (ejemplo) o o

130 System . o u t . p r i n t l n ( "El registro debi encontrarse " o 140 +" entre \n" ) ; 150 System . o u t . p r i n t l n ( "***" + e . daRegAnt ( ) . daNombre ( ) 160 + "***\n" + " y\n" ) ; 170 System . o u t . p r i n t l n ( "***" 180 + e . daRegPost ( ) . daNombre ( ) 190 + "***" ) ; 200 } // f i n de c a t c h 210 ... 220 } // main 230 } // c l a s s

En las l neas 60 a 80 tenemos un constructor adicional declarado para nuestra clase que extiende a Exception. Como se puede ver en las l neas 130 a 170, el manejador de la excepcin hace uso de los campos y mtodos declarados en la o e 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 la pila, o el que llena esta pila 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.12 y 9.13 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.

449

Manejo de errores en ejecucin o


MiExcepcion2

Cdigo 9.12 Excepciones creadas por el programador o


10 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 { 20 private int i ; 30 public MiExcepcion2 () { 40 super ( ) ; 50 } 60 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 ) { 70 super ( msg ) ; 80 } 90 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 ) 100 super ( msg ) ; 110 i = x; 120 } 130 public int val () { 140 return i ; 150 } 160 }

Cdigo 9.13 Uso de excepciones creadas por el programador o

CaracteristicasExtra (1/2)

10 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 { 20 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 { 30 System . o u t . p r i n t l n ( " Lanzando MiExcepcion2 desde f()" ) ; 40 throw new M i E x c e p c i o n 2 ( ) ; 50 } 60 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 { 70 System . o u t . p r i n t l n ( " Lanzando MiExcepcion2 desde g()" ) ; 80 throw new M i E x c e p c i o n 2 ( "Se origin en g()" ) ; o 90 } 100 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 { 110 System . o u t . p r i n t l n ( " Lanzando MiExcepcion2 desde h()" ) ; 120 throw new M i E x c e p c i o n 2 ( "Se origin en h()" , 4 7 ) ; o 130 } 140 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 ) { 150 try { 160 f (); 170 } 180 catch ( M i E x c e p c i o n 2 e ) { 190 e . p r i n t S t a c k T r a c e ( System . e r r ) ; 200 } 210 try { 220 g(); 230 } 240 catch ( M i E x c e p c i o n 2 e ) { 250 e . p r i n t S t a c k T r a c e ( System . e r r ) ; 260 }

9.4 Las clases que extienden a Exception

450
CaracteristicasExtra (2/2)

Cdigo 9.14 Uso de excepciones creadas por el programador o


270 280 290 300 310 320 330 340 350 } try { h();

} catch ( M i E x c e p c i o n 2 e ) { e . p r i n t S t a c k T r a c e ( System . e r r ) ; System . e r r . p r i n t l n ( "e.val () = " + e . v a l ( ) ) ; } }

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.10. o

Figura 9.10

Ejecucin de CaracteristicasExtra o
e l i s a @ l a m b d a . . . ICC1 / p r o g s / e x c e p c i o n e s % j a v a C a r a c t e r i s t i c a s E x t r a Lanzando M i E x c e p c i o n 2 d e s d e f ( ) MiExcepcion2 at C a r a c t e r i s t i c a s E x t r a . f ( C a r a c t e r i s t i c a s E x t r a . java :23) a t C a r a c t e r i s t i c a s E x t r a . main ( C a r a c t e r i s t i c a s E x t r a . j a v a : 3 8 ) Lanzando M i E x c e p c i o n 2 d e s d e g ( ) M i E x c e p c i o n 2 : Se o r i g i n en g ( ) o at C a r a c t e r i s t i c a s E x t r a . g( C a r a c t e r i s t i c a s E x t r a . java :28) a t C a r a c t e r i s t i c a s E x t r a . main ( C a r a c t e r i s t i c a s E x t r a . j a v a : 4 4 ) Lanzando M i E x c e p c i o n 2 d e s d e h ( ) M i E x c e p c i o n 2 : Se o r i g i n en h ( ) o at C a r a c t e r i s t i c a s E x t r a . h( C a r a c t e r i s t i c a s E x t r a . java :33) a t C a r a c t e r i s t i c a s E x t r a . main ( C a r a c t e r i s t i c a s E x t r a . j a v a : 5 0 ) e . v a l ( ) = 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.

451

Manejo de errores en ejecucin o

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 lanzara alguna otra excepcin, el compilador exie o gir que hubiera un manejador por cada tipo de excepcin. Se puede atrapar a o excepciones usando superclases, pero cada clase de excepcin lanzada por o un mtodo tiene que tener su manejador propio o uno que se reera a la e superclase. Si un mtodo lanza una excepcin y no la atrapa en el mismo mtodo, su e o e encabezado tiene que especicar que lanza aquellas excepciones que no sean atrapaDas 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 e o va a pedir a la excepcin que reporte el punto donde estaba la ejecucin en el o o momento en que fue lanzada la excepcin usando printStackTrace la excepcin o o lanzada 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 a o o la pila de ejecucin se utiliza el mtodo llInStackTrace al momento de relanzar la o e excepcin; esto va a hacer que la pila 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 atrapar las excepciones los o

9.5 El enunciado nally

452

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.16.

Cdigo 9.15 Ejemplo con la clusula nally (Excepcin) o a o


10 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 { 20 public TresException () { 30 super ( ) ; 40 } 50 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 ) { 60 super ( msg ) ; 70 } 80 }

TresException

Cdigo 9.16 Ejemplo con la clusula nally (uso) o a

FinallyTrabaja

90 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 { 100 s t a t i c int cuenta = 0; 110 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 ) { 120 while ( true ) { 130 try { 140 // 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 150 i f ( c u e n t a++ == 0 ) { 160 throw new T r e s E x c e p t i o n ( ) ; 170 } 180 System . o u t . p r i n t l n ( "No hubo excepci n " ) ; o 190 } 200 catch ( T r e s E x c e p t i o n e ) { 210 System . e r r . p r i n t l n ( " TresException " ) ; 220 } 230 finally { 240 System . e r r . p r i n t l n ( "En la cl usula finally " ) ; a 250 i f ( c u e n t a == 2 ) { 260 break ; // s a l d e l w h i l e 270 } 280 } 290 } 300 } 310 }

Como se puede ver la salida de la ejecucin de este algoritmo en la gura 9.11 o en la pgina opuesta, el mensaje mandado por el bloque nally se imprime siempre, a sin importar si hubo o no excepcin. o

453

Manejo de errores en ejecucin o

Figura 9.11

Ejecucin de FinallyTrabaja o
e l i s a @ l a m b d a . . . ICC1 / p r o g s / e x c e p c i o n e s % j a v a F i n a l l y T r a b a j a TresException En l a c l u s u l a f i n a l l y a No hubo e x c e p c i n o En l a c l u s u l a f i n a l l y 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 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.17. e

Cdigo 9.17 Otro ejemplo con la clusula nally o a


10 c l a s s S w i t c h { 20 boolean s t a t e = f a l s e ; 30 boolean r e a d ( ) { 40 return s t a t e ; 50 } 60 v o i d on ( ) { 70 s t a t e = true ; 80 } 90 void o f f ( ) { 100 state = false ; 110 } 120 }

Switch

Cdigo 9.18 Un ejemplo ms con la clusula nally o a a


10 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 20 public OnOffException1 () { 30 super ( ) ; 40 } 50 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 ) { 60 super ( msg ) ; 70 } 80 } {

OnOException1

9.5 El enunciado nally

454
OnOException2 {

Cdigo 9.19 Ms ejemplos con la clusula nally o a a


10 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 20 public OnOffException2 () { 30 super ( ) ; 40 } 50 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 ) { 60 super ( msg ) ; 70 } 80 }

Cdigo 9.20 Ejemplo adicional con la clusula nally o a


10 c l a s s O n O f f S w i t c h { 20 s t a t i c S w i t c h sw = new S w i t c h ( ) ; 30 s t a t i c void f ( ) 40 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 { 50 } 60 }

OnOSwitch

Cdigo 9.21 Ejemplo nal con la clusula nally o a


10 p u b l i c c l a s s C o n F i n a l l y { 20 s t a t i c S w i t c h sw = new S w i t c h ( ) ; 30 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 ) { 40 try { 50 sw . on ( ) ; 60 // 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 70 OnOffSwitch . f ( ) ; 80 } 90 catch ( O n O f f E x c e p t i o n 1 e ) { 100 System . e r r . p r i n t l n ( " OnOffException1 " ) ; 110 } 120 catch ( O n O f f E x c e p t i o n 2 e ) { 130 System . e r r . p r i n t l n ( " OnOffException2 " ) ; 140 } 150 finally { 160 sw . o f f ( ) ; 170 } 180 } 190 }

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.22 en la pgina a opuesta tenemos un ejemplo de anidamiento de bloques try.

455

Manejo de errores en ejecucin o


CuatroException

Cdigo 9.22 Excepcin opara mostrar anidamiento de bloques try o o


10 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 { 20 public CuatroException () { 30 super ( ) ; 40 } 50 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 ) { 60 super ( msg ) ; 70 } 80 }

Cdigo 9.23 Anidamiento de bloques try o

SiempreFinally

10 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 { 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 ) { 30 System . o u t . p r i n t l n ( " Entrando al primer bloque try" ) ; 40 try { 50 System . o u t . p r i n t l n ( " Entrando al segundo bloque try" ) ; 60 try { 70 throw new C u a t r o E x c e p t i o n ( ) ; 80 } 90 finally { 100 System . o u t . p r i n t l n ( " Finally en el segundo " 110 + " bloque try" ) ; 120 } 130 } 140 catch ( C u a t r o E x c e p t i o n e ) { 150 System . e r r . p r i n t l n ( " Atrapando CuatroException en " + 160 "el primer bloque try" ) ; 170 } 180 finally { 190 System . e r r . p r i n t l n ( " Finally en primer bloque try" ) ; 200 } 210 } 220 }

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.12 en la siguiente pgina. o a

9.6 Restricciones para las excepciones

456

Figura 9.12

Ejecucin de SiempreFinally o
e l i s a @ l a m b d a . . . ICC1 / p r o g s / e x c e p c i o n e s % j a v a S i e m p r e F i n a l l y Entrando a l primer bloque try Entrando a l segundo bloque try F i n a l l y en e l s e g u n d o b l o q u e t r y A t r a p a n d o C u a t r o E x c e p t i o n en e l p r i m e r b l o q u e t r y F i n a l l y en p r i m e r b l o q u e t r y

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 s 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.

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.

457

Manejo de errores en ejecucin o

9.7 Recomendaciones generales


Las excepciones en general se usan en cualquiera de las siguientes circunstancias: i. Arreglar el problema y llamar otra vez al mtodo que caus la excepcin. e o o ii. Parchar el proceso y continuar sin volver a intentar el mtodo. e iii. Calcular algn resultado alternativo en lugar del que el mtodo se supone que u e deb haber calculado. a iv. Hacer lo que se pueda en el contexto actual y relanzar la excepcin para que o sea manejada en un contexto superior. v. Hacer lo que se pueda en el contexto actual y lanzar una excepcin distinta o para que sea manejada en un contexto superior. vi. Terminar el programa. vii. Simplicar el algoritmo. viii. 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 Con esto damos por terminado este tema, aunque lo usaremos extensivamente en los cap tulos que siguen.

Ejercicios
9.1.- Qu est mal en el cdigo siguiente? Identica todos los errores de sintaxis. e a o
10 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 S c a n n e r e s c a n e r = new S c a n n e r ( System . i n ) ; 30 try { 40 i n t num = e s c a n e r . n e x t I n t ( ) ; 50 i f (num > 1 0 0 ) { 60 catch new E x c e p t i o n ( " Fuera del l mite " ) ; 70 } 80 } catch ( I n p u t M i s m a t c h E x c e p t i o n e ) { 90 System . o u t . p r i n t l n ( " Entrada inv lida" ) ; a 100 } f i n a l l y ( Exception e ) { 110 System . o u t . p r i n t l n ( " Hecho " ) ; 120 } 130 }

9. Ejercicios

458 9.2.- Determina la salida del pedazo de cdigo siguiente cuando se teclean, suceo sivamente, las siguientes entradas: (a) 12 (b) -12 (c) a12
10 20 30 40 50 60 70 80 90 100 110 120 try { i n t num = e s c a n e r . n e x t I n t ( ) ; i f (num < 0 ) { throw new E x c e p t i o n ( " Negativo no" ) ; } } catch ( I n p u t M i s m a t c h E x c e p t i o n e ) { System . o u t . p r i n t l n ( " Entrada inv lida " ) ; a } catch ( E x c e p t i o n e ) { System . o u t . p r i n t l n ( " Error : " + e . g e t M e s s a g e ( ) ) ; } finally { System . o u t . p r i n t l n ( " Terminado " ) ; }

9.3.- Determina la salida del pedazo de cdigo anterior si se ponen las l o neas 80 y 90 antes que las l neas 60 y 70, como se muestra a continuacin y si se le o alimenta, sucesivamente, las siguientes entradas: (a) 12 (b) -12 (c) a12
10 20 30 40 50 60 70 80 90 100 110 120 try { i n t num = e s c a n e r . n e x t I n t ( ) ; i f (num < 0 ) { throw new E x c e p t i o n ( " Negativo no" ) ; } } catch ( E x c e p t i o n e ) { System . o u t . p r i n t l n ( " Error : " + e . g e t M e s s a g e ( ) ) ; } catch ( I n p u t M i s m a t c h E x c e p t i o n e ) { System . o u t . p r i n t l n ( " Entrada inv lida " ) ; a } finally { System . o u t . p r i n t l n ( " Terminado " ) ; }

En algunos compiladores este pedazo de cdigo da error de sintaxis al llegar o a la l nea 80, con el mensaje de que esa excepcin ya est siendo manejada. o a Por qu es el error? e 9.4.- El siguiente segmento de cdigo reporta varios errores de sintaxis. D cules o a son, explica por qu se presentan y d cmo arreglarlos sin eliminar las e o excepciones.

459

Manejo de errores en ejecucin o

10 p u b l i c s t a t i c S t r i n g e j e r c i c i o 4 ( O b j e c t l i s t a ) { 20 i f ( l i s t a == n u l l ) { 30 throw new E x c e p t i o n ( " lista vac a " ) ; 40 } 50 return l i s t a . t o S t r i n g ( ) ; 60 } 70 80 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 ) { 90 O b j e c t nuevo = n u l l ; 100 System . o u t . p r i n t l n ( " nuevo es: " + e j e r c i c i o 4 ( nuevo ) ) ; 110 }

9.5.- Si el cdigo del ejercicio anterior, en la l o nea 30, en lugar de lanzar la excepcin Exception lanza la excepcin NullPointerException, el programa compila o o y ejecuta bien. Explica las razones. 9.6.- Tenemos el siguiente mtodo recursivo para calcular el factorial de un entero e positivo:
10 p u b l i c long f a c t o r i a l ( i n t n ) { 20 i f ( n == 1 ) r e t u r n 1 ; 30 r e t u r n n f a c t o r i a l ( n 1); 40 }

Si a este mtodo se le pasa como argumento un entero negativo, el mtoe e do recurrir para siempre. Dene una excepcin vigilada que haga que la a o funcin reaccione frente a esta situacin mandando un mensaje indicando o o lo que pas. o 9.7.- Agrega una l nea al principio del mtodo para que si el entero es negativo e lance la excepcin que deniste al mtodo que la llam. Esta excepcin debe o e o o ser manejada en todos los puntos en donde hay invocaciones a factorial. Modica el mtodo para que as sea y d cmo debe ser llamada desde fuera. e o 9.8.- Modica el mtodo anterior para la excepcin sea atrapada y manejada e o dentro del mtodo, regresando un valor invlido. e a 9.9.- Tenemos un mtodo que busca en una lista ligada la presencia de una cae dena. Codica el mtodo para objetos que tengan un campo de informacin e o info y una referencia al siguiente sigue. Puedes suponer los mtodos get y e set correspondientes. El mtodo recibe como parmetro una cadena y debe e a regresar la referencia del objeto que tenga a esa cadena como subcadena. Agrega excepciones para que el mtodo lance excepciones si: e

9. Ejercicios (a) La cadena que le pasan es nula o vac a. (b) La cadena no se encuentra en ningn elemento de la lista. u

460

9.10.- Cuando declaras tus propias excepciones, deben ser vigiladas o no? Justica.

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 aplicacin) la o a o informacin que generamos no vive ms all excepto, posiblemente, si la salida o a a de la aplicacin se redirigi a disco. No tenemos manera de almacenar lo que o o construimos 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 mediano te archivos, que son conjuntos de datos guardados en un medio de almacenamiento externo. Los archivos sirven de puente entre la aplicacin y el medio exterior, ya o sea para comunicarse con el usuario o para, como acabamos de mencionar, darle persistencia a nuestras aplicaciones. Hasta ahora hemos usado extensamente la clase Scanner, que nos permite aplicar mecanismos de revisin de cadenas a la consola (o a una cadena). Tambin o e hemos usado dos archivos (objetos) que estn dados en Java y que son System.out a y System.err. Ambos objetos corresponden a archivos de salida; el primero es para

10.1 Conceptos generales

462

salida normal en consola y el segundo para salida, tambin en consola, pero de e reporte de errores, como los que suceden en una aplicacin y que generan una exo cepcin. Por ejemplo, cuando un programa que aborta reporta dnde se lanz la o o o excepcin, el reporte lo hace a System.err noten que aun cuando redirijamos la o salida desde el sistema operativo a un archivo en disco llamado salida.txt por ejemplo, cuando invocamos la ejecucin de una clase con o java miClase > salida.txt los mensajes de error siguen apareciendo en la consola. La razn por la que usamos un objeto de la clase Scanner hasta el momento es o que en Java prcticamente toda la entrada y salida sobre todo la entrada puede a lanzar excepciones; eso implica que cada vez que usemos un archivo para leer, escribir, crearlo, eliminarlo y, en general, cualquier operacin que tenga que ver o con archivos, esta operacin tiene que ser vigilada en un bloque try, con el manejo o correspondiente de las excepciones que se pudieran lanzar. Como Scanner no es un objeto de entrada y salida y la entrada a travs de System.in ya est creada e e a inicializada al empezar nuestra aplicacin, podemos aplicar un objeto de la clase o Scanner, al construirlo con new, ya sea a una cadena o a un archivo de entrada que ya est inicializado. El signicado de esto es, simplemente, que vamos a ver al ujo e de entrada o a la cadena como a travs de cristales de colores que nos permitan e separar elementos de la cadena o ujo como interpretar un entero, buscar un carcter de n de l a nea, agrupar en una cadena, etc.. 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 caracteres (ya sea de 8 o 16 bits). En el caso de los ujos de entrada, stos proporcionan caracteres, uno e 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 caracteres para que sean proporcionados al 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 para cualquier lenguaje a de programacin. En el caso de querer un ujo de entrada, el ujo tiene que o localizarse y ponerse listo para leer de l. Si el ujo no existe (por ejemplo, en e disco), no puede seguir adelante la aplicacin. En el caso de los ujos de salida o se tiene que preparar el espacio donde se va a escribir o podr darse el caso de a

463

Entrada y salida

que se quisiera extender un archivo que ya existe (append ). En este ultimo caso tambin deber vericar que el archivo ya exista y que est disponible. Tanto en e a e ujos de entrada como de salida el sistema deber vericar que la aplicacin tenga a o los permisos necesarios para acceder, crear y/o escribir en el ujo especicado.

Figura 10.1

Algoritmo para el uso de ujos de entrada


6 9Inicializar 9 9 9 9 9 9 9 9 8 9 9 9 9 9 9 9 7Final 3

Abrir el ujo
6 9 8

Lectura de Procesar informacin o Leer informacin o caracteres 9 (mientras haya) 9 9 7


3

Cerrar el ujo

Figura 10.2

Algoritmo para el uso de ujos de salida


6 9Inicializar 9 9 9 9 9 9 9 9 8 9 9 9 9 9 9 9 7Final 3

Abrir el ujo
6 9 8

o Escritura de Procesar informacin Escribir informacin o caracteres 9 (mientras haya) 9 9 7


3

Cerrar el ujo

Las actividades ms tardadas que lleva a cabo una aplicacin son las de entraa o da y salida, pues tienen que comunicarse con el mundo exterior de los dispositivos f sicos. En general, el sistema se tiene que sentar a esperar que uya la informacin; para acortar estos tiempos el sistema lee (o escribe) por bloques: lee un o bloque completo y va proporcionando de ah dato por dato; cuando el bloque de memoria se vac carga un nuevo bloque; o escribe a un bloque de memoria (llaa, mado buer ) y cuando el bloque se llena es cuando lo escribe al dispositivo f sico. Decimos entonces que el proceso de entrada/salida est gestionado a travs de a e un buer. Los ujos de entrada y salida, como su nombre lo indica, uyen. En un ujo de entrada la aplicacin va consumiendo el ujo como se le va presentando, por lo que o al abrir el ujo deberemos tener un apuntador o referencia a cul es el siguiente a

10.2 Jerarqu de clases a

464

elemento disponible del ujo, que deber ser el primer elemento del mismo; en a el caso de un ujo de salida deberemos saber hasta dnde se ha emitido el ujo o y que al abrirse deber decir que no se ha emitido nada. Con cada operacin de a o entrada/salida esta referencia se debe actualizar para que el objeto sea capaz de ir entregando (produciendo) los caracteres conforme se solicitan (emiten). Podemos ver un esquema de este funcionamiento en las guras 10.3 y 10.4, donde la aplicacin los produce y consume en el orden en que se muestran en el o ujo.

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 e e objeto 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.

465

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 caracteres Unicode. Dado que Java es fundamentalmente un lenguaje cuya caracter stica principal es su portabilidad, se diseo un juego de caracteres n 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 caracteres. n 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 caracteres. 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 un stream. Hablaremos de un ujo de bytes o de un ujo de caracteres (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 caracteres cuyo valor est entre 0 y 255. Para que podamos a manejar as los bytes, la lectura (y la escritura) se har siempre en enteros o en a caracteres Unicode, de tal manera que el mtodo utilice unicamente el byte ms e a bajo (al que corresponden las posiciones ms de la derecha). a En la gura 10.5 en la siguiente pgina se encuentra la jerarqu de clases para a a InputStream, mientras que en la gura 10.6 en la pgina 469 est el esquema de la a a jerarqu de clases para OutputStream. a

10.3 Entrada y salida de bytes

466

Figura 10.5

Jerarqu de clases para InputStream. a


DataInput InputStream ObjectInputStream AudioInputStream SequenceInputStream ByteArrayInputStream FilterInputStream FileInputStream PipedInputStream SequenceInputStream StringBuerInputStream ObjectInput

LineNumberInputStream DataInputStream BueredInputStream PushBackInputStreeam CheckedInputStream 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 .

467

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 los o datos que ha le do. public class CipherInputStream extends FilterInputStream (Paquete: javax.crypto) Est compuesto de un InputStream y un Cipher que permite entregar cifrados a 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 extends InputStream Simplemente recibe el ujo de un ujo subyacente y los pasa a la aplicacin. o Redene los mtodos de entrada para poder transformarla. 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

468

public class LineNumberInputStream extends FilterInputStream (sobreseido1 ) Este ujo lleva la cuenta del nmero de l u neas que ha le Una l do. nea es una sucesin de bytes que terminan con zr, zn o un retorno de carro seguido de o 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 bya tes. 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 Pipedo InputStream para adquirir datos y el otro usa un PipedOutputStream para entregar datos. Ambos hilos pueden hacer un proceso de la informacin del o 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 este caso a 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 caracteres, en lugar de venir de un dispositivo. La jerarqu de clases para los ujos de salida de bytes se da en la gura 10.6. a Con sus marcadas excepciones por el uso que se le pueda dar, hay una correspondencia entre ambas jerarqu as.

Corresponde a deprecated, que indica que no se recomienda su uso porque ya no va a ser actualizada y soportada

469

Entrada y salida

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 caracteres

470

10.4 Entrada y salida de caracteres


Cuando hablamos de caracteres en el contexto de Java nos referimos a caracteres Unicode, donde cada uno ocupa 2 bytes (16 bits). Tenemos una jerarqu a similar a la que maneja bytes para caracteres Unicode, las superclases Writer y 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

471

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 explicacin, excepto que donde dice byte hay que sustituir por carcter. Unio a

10.4 Entrada y salida de caracteres

472

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 caracteres y ujos de bytes, codicados de acuerdo a un conjunto de caracteres espec 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 caracteres) denen prcticamente los mismos mtodos para bytes y caracteres, a e pero para fomentar la portabilidad de las aplicaciones se ha optado por soportar de mejor manera las clases relativas a caracteres. 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 ;

473

Entrada y salida

Por ser stos tres objetos estticos de la clase System se pueden usar sin conse a truir objetos. Todo programa en ejecucin cuenta con ellos, por lo que los puede o usar, simplemente 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 466 y 10.6 en la pgina 469. 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

474

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 tiene 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.

475

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 y de a e implementar al mtodo que lee de un entero, agrega los mtodos que l;istamos a e e continuacin. Tiene la siguiente denicin: o 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

476

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.

477

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 caracteres, convirtindolos a bytes. 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.6 El catlogo de carreras a

478

10.6 El catlogo de carreras a


Regresemos al catlogo de carreras que vimos unicamente en cuanto a su uso; la a razn expuesta para ello es que si quer o amos tener el catlogo en disco ten a amos que usar excepciones para manejarlo. Como ya tenemos ese conocimiento y la especicacin general de archivos, podemos proceder a abrir esa clase para ver o cmo est implementada. El cdigo se encuentra en el listado 10.1. o a o

Cdigo 10.1 Implementacin del catlogo de carreras o 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 33 34 35

CatalogoCarreras (1/4)

package u t i l e s ; import j a v a . i o . ; import j a v a . u t i l . S c a n n e r ; / C l a s s <code>C a t a l o g o C a r r e r a s </code> c a r g a a memoria e l c a t a l o g o de carreras . @ a u t h o r <a h r e f = m a i l t o : e l i s a @ l a m b d a . f c i e n c i a s . unam . mx></a> @version 1.0 / public class CatalogoCarreras { / V a l o r v a r i a b l e <code>c a r r e r a s </code> p a r a l a s c l a v e s de l a s carreras . / p r i v a t e s t a t i c S t r i n g c a r r e r a s = "" ; / V a l o r v a r i a b l e <code>s C a r r e r a s </code> p a r a l o s nombres de l a s carreras . / p r i v a t e s t a t i c S t r i n g s C a r r e r a s = "" ; / V a l o r v a r i a b l e <code>h a y C a r r e r a s </code> p a r a m a r c a r cuando ya s e hayan c a r g a d o l a s c a r r e r a s a memoria . / p r i v a t e s t a t i c boolean h a y C a r r e r a s = f a l s e ; / C o n s t a n t e <code>TAM CLAVE</code> p a r a d a r e l tama o de l a s n c l a v e s de c a r r e r a . / p u b l i c s t a t i c f i n a l i n t TAM CLAVE = 3 ; / C o n s t a n t e <code>TAM NOMBRE</code> p a r a d a r e l tama o de l o s n nombres de c a r r e r a . / p u b l i c s t a t i c f i n a l i n t TAM NOMBRE = 3 6 ;

479 Cdigo 10.1 Implementacin del catlogo de carreras o o a


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 79 80 81 82 83 84 85

Entrada y salida
CatalogoCarreras (2/4)

/ Metodo <code>l e e C a r r e r a s </code> l e e l a s c l a v e s y nombres de l a s c a r r e r a s d e s d e un a r c h i v o en d i s c o . @param a r c h i v o v a l o r de t i p o <code>S t r i n g </code> p a r a / public s t a t i c void l e e C a r r e r a s ( S t r i n g a r c h i v o ){ Scanner cons = n u l l ; try { c o n s = new S c a n n e r ( new F i l e ( 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 ) { hayCarreras = false ; System . e r r . p r i n t l n ( "No pude leer el catalogo de carreras " ) ; System . e x i t ( 1 ) ; } hayCarreras = true ; // Se pudo a b r i r e l a r c h i v o i n t n u m Ca r r e r a = 0 ; S t r i n g nombreCarrera = n u l l ; w h i l e ( n u m Ca r r e r a != 1) { n u m Ca r r e r a = c o n s . n e x t I n t ( ) ; i f ( n u m Ca r r e r a != 1) { nombreCarrera = cons . nextLine ( ) ; c a r r e r a s += Cadenas . rellenaCampo ( I n t e g e r . t o S t r i n g ( numCarrera ) , TAM CLAVE , 0 , i ) ; s C a r r e r a s += Cadenas . r e l l e n a C a m p o ( n o m b r e C a r r e r a ,TAM NOMBRE, , d ) ; } } cons . c l o s e ( ) ; } / Metodo <code>g e t C a r r e r a s </code> r e g r e s a una c a d e n a con l a s claves . @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >. / public static String getCarreras () { return c a r r e r a s ; } / Metodo <code>g e t S C a r r e r a s </code> r e g r e s a una c a d e n a con l o s nombres . @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >. / public static String getSCarreras () { return s C a r r e r a s ; }

10.6 El catlogo de carreras a

480
CatalogoCarreras (3/4)

Cdigo 10.1 Implementacin del catlogo de carreras o o a


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 132

/ Metodo <code>b H a y C a r r e r a s </code> r e g r e s a s i ya e s t n c a r g a d a s a las carreras . @ r e t u r n v a l o r de t i p o <code>b o o l e a n </code >. / p u b l i c s t a t i c boolean b H a y C a r r e r a s ( ) { return hayCarreras ; } / Metodo <code>d a C a r r e r a </code >, dada l a c l a v e de l a c a r r e r a r e g r e s a e l nombre . @param c u a l v a l o r de t i p o <code>i n t </code >. @ r e t u r n v a l o r de t i p o <code>S t r i n g </code >. / public s t a t i c S t r i n g daCarrera ( i n t c u a l ){ i f (! hayCarreras ) { l e e C a r r e r a s ( " CarrerasCiencias .txt" ) ; hayCarreras = true ; } S t r i n g s C u a l = Cadenas . r e l l e n a C a m p o ( I n t e g e r . t o S t r i n g ( c u a l ) , TAM CLAVE , 0 , i ) ; i n t donde = c a r r e r a s . i n d e x O f ( s C u a l ) ; i f ( donde == 1 | | ( donde % TAM CLAVE) != 0 ) { r e t u r n Cadenas . r e l l e n a C a m p o ( " C digo invalido " ,TAM NOMBRE, , d ) ; o } else { i n t empza = ( donde /TAM CLAVE) TAM NOMBRE; r e t u r n s C a r r e r a s . s u b s t r i n g ( empza , empza+TAM NOMBRE ) ; } } / Metodo <code>m u e s t r a C a t a l o g o </code> m u e s t r a e l c a t l o g o de l a s a carreras . / public s t a t i c void muestraCatalogo ( ) { i f (! hayCarreras ) { l e e C a r r e r a s ( " CarrerasCiencias .txt" ) ; hayCarreras = true ; } System . o u t . p r i n t l n ( " Catalogo de carreras de la Facultad " + " de Ciencias \n" +" ================================== " + " ============= " ) ;

481 Cdigo 10.1 Implementacin del catlogo de carreras o o a


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 }

Entrada y salida
CatalogoCarreras (4/4)

f o r ( i n t i =0, j =0; i< carreras . length (); i+=TAM CLAVE , j+=TAM NOMBRE) { System . o u t . p r i n t l n ( c a r r e r a s . s u b s t r i n g ( i , i+TAM CLAVE)+"\t" + s C a r r e r a s . s u b s t r i n g ( j , j+TAM NOMBRE) + "\n" ) ; } System . o u t . p r i n t l n ( " =========================================== " + " ============== " ) ; } / Metodo <code>e s C a r r e r a </code> d i c e s i l a c l a v e p r o p o r c i o n a d a e s valida . @param c u a l v a l o r de t i p o <code>i n t </code> c l a v e p r o p o r c i o n a d a . @ r e t u r n v a l o r de t i p o <code>b o o l e a n </code >. / p u b l i c s t a t i c boolean e s C a r r e r a ( i n t c u a l ) { i f (! hayCarreras ) { l e e C a r r e r a s ( " ConsultasListas / CarrerasCiencias .txt" ) ; hayCarreras = true ; } S t r i n g s C u a l = Cadenas . r e l l e n a C a m p o ( I n t e g e r . t o S t r i n g ( c u a l ) , TAM CLAVE , 0 , i ) ; i n t donde = c a r r e r a s . i n d e x O f ( s C u a l ) ; i f ( donde == 1 | | ( donde % TAM CLAVE) != 0 ) { return f a l s e ; } return true ; }

Lo primero que vemos en la implementacin de esta clase es que estamos o importando a las clases en el paquete java.io, adems de Scanner, que ya lo ima portbamos l a neas 2 y 3. La clase consiste de mtodos estticos. Es necesario e a importar todo el paquete java.io.* porque queremos tener acceso tambin a las e excepciones que se pudiesen presentar. En este mtodo tenemos como atributos, e tambin estticos, cadenas que corresponden a las claves de las carreras y a los e a nombres de las mismas l neas 15 y 20. Podr amos tener, en lugar de las cadenas correspondientes, una declaracin de arreglos o
public static int [ ] c a r r e r a s ; public static String [ ] sCarreras ;

10.6 El catlogo de carreras a

482

e ir agregando cada una de las claves con su correspondiente nombre. El unico problema hubiese sido la inicializacin del arreglo, pues tendr o amos primero que leer para saber cuntas claves estn registradas. Tambin la localizacin de cada a a e o clave hubiese sido un poco ms engorrosa, pues habr que hacer una bsqueda lia a u neal del arreglo de claves en lugar de utilizar indexOf que tiene una implementacin o muy eciente. El mtodo central de la clase es leeCarreras en las l e neas 42 a 67 del listado 10.1 en la pgina 479; recibe como parmetro una cadena que corresponde al nombre o a a ruta de acceso del archivo en disco en donde se encuentran grabadas las carreras de la Facultad de Ciencias. Este mtodo es invocado la primera vez que cualquier e otro mtodo de la clase sea invocado. e Declaramos un objeto de la clase Scanner y lo construimos encima de un archivo en disco, localizado y abierto cuando creamos el objeto de la clase File en la l nea 45 del listado 10.1 en la pgina 479. Es importante mencionar que esta a construccin no se puede hacer fuera del try porque el constructor del Scanner, o cuando se aplica a un objeto de la clase File, puede lanzar la excepcin FileNoto FoundException, que debe ser vigilada. La construccin de un objeto de la clase File o tambin puede lanzar una excepcin, pero sta es de la clase NullPointerException, e o e por lo que no tiene que ser vigilada. La clase Scanner tiene varios constructores que se listan a continuacin: o

Scanner( File source)

throws FileNotFoundException

Construye un objeto Scanner que produce valores obtenidos del archivo especicado. Lanza la excepcin si no puede encontrar el archivo especio cado.

Scanner( File source , String charsetName) throws FileNotFoundException


Construye un objeto Scanner que produce valores obtenidos del archivo especicado. Espera la entrada codicada en el conjunto de caracteres dados. Lanza la excepcin si no puede encontrar el archivo especicado. o

Scanner(InputStream source)
Construye un objeto Scanner que obtiene su entrada del ujo de entrada especicado.

Scanner(InputStream source, String charsetName)


Construye un objeto Scanner que obtiene su entrada del ujo de entrada especicado. Espera el ujo codicado en el conjunto de caracteres especicado.

483

Entrada y salida

Scanner(Readable source)
construye un Scanner que toma su entrada de un objeto de una clase que implemente a la interfaz Readable.

Scanner(ReadableByteChannel source)
Construye un nuevo Scanner que produce valores obtenidos del canal especicado.

Scanner(ReadableByteChannel source, String charsetName)


Construye un nuevo Scanner que produce valores obtenidos del canal especicado. Los valores estn codicados en el conjunto de caracteres esa pecicado.

Scanner(String source)
Construye un nuevo Scanner que produce valores obtenidos de interpretar la cadena especicada.

10.7 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 ( P r i n t S t r e a m 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" ) ) ) ;

En cuanto a System.in no es cmodo trabajar con este ujo. La razn es que, o o como ya vimos, no tiene mtodos poderosos para leer e interpretar datos, ya que e su proceso es byte por byte, o bien un nmero dado de bytes le u dos a un arreglo

10.8 Persistencia de la base de datos

484

de bytes. Pero lo podemos usar como dispositivo en el que montemos algn otro u tipo de procesador de bytes o caracteres como Scanner o BueredReader para poder hacer lecturas de ms alto nivel. a En cambio, tanto System.out como System.err, que son objetos de la clase PrintStream interpretan representaciones internas a cadenas, usando los mtodos e toString correspondientes. Son muy cmodos, sobre todo si se trata de interaccioo nar con un usuario.

10.8 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 son muy utiles para leer y escribir cadenas, pero en esta ocasin o queremos que el ujo subyacente sea un archivo en disco y no un ujo estndar. a Revisemos entonces la clase FileReader y FileWriter que me van a dar esa facilidad. Estos ujos extienden a InputStreamReader y OutputStreamWriter respectivamente, que a su vez heredan, respectivamente, de Reader y Writer. De esta jerarqu a unicamente hemos revisado la clase Reader, as que procedemos a ver las otras clases de la jerarqu que vamos a requerir. a

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 caracteres a ser sincronizado por l mismo. e

protected Writer(Object lock)


Construye un ujo de caracteres que ser sincronizado usando lock. a

485

Entrada y salida

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 caracteres, codicados en cualquier o cdigo de 16 bits en cadenas o subsucesiones. Agrega el argumento al o ujo this.

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 caracteres presentes en el arreglo cbuf.

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 caracteres. o

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 caracteres. o

10.8 Persistencia de la base de datos

486

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, 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 caracteres.

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

487

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 caracteres en bytes. Se monta sobre un ujo de salida de bytes.

public OutputStreamWriter(OutputStream out, Charset cs)


Construye un ujo de salida de caracteres a bytes, montado sobre un ujo 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 caracteres en memoria.

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 Ahora s ya podemos pasar a revisar las clases FileReader y FileWriter que here dan respectivamente de InputStreamReader y OutputStreamWriter. Empezaremos

10.8 Persistencia de la base de datos

488

por el ujo de entrada. En esta subclase unicamente se denen los constructores, ya que se heredan precisa y exactamente los mtodos implementados en Inpute StreamReader.

public class FileReader extends InputStreamReader


Dado que sta es una subclase de InputStreamReader, va a leer bytes y e convertirlos en caracteres. Al igual que su superclase, tambin trabaja 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 FileWriter son los constructores. Para el resto de los mtodos y campos se heredan las ime plementaciones dadas por OutputStreamWriter. Veamos la denicin. o

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.

489

Entrada y salida

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 dondequiera 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.8.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

10.8 Persistencia de la base de datos

490

cuatro e interpretar cada cuatro bytes como un entero; o podemos tomarlos de seis en seis e interpretar a cada grupo como un double. En ningn otro caso es u ms cierto el dicho de que todo depende del color del cristal con que se mira que a en la lectura (interpretacin) de archivos en disco. Y esta interpretacin depende o o del 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 caracteres de n de l 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 clase de archivo usar deber ser una decisin que se toma una vez o e a o elegida 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 o del 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 en la pgina opuesta. o a 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 en consola, y que queda como se ve en el listado 10.2 en la pgina opuesta. a

491

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 7Final Cerrar el archivo

Cdigo 10.2 Mtodo que solicita al usuario nombre de archivo o e


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

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 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 cul caso se trata (caso) para que pueda armar el mensaje correspondiente a

10.8 Persistencia de la base de datos

492

(l neas 38 a 43). 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 anterior. El a cdigo lo podemos ver en el listado 10.3. o

Cdigo 10.3 Cdigo para guardar la base de datos o o


305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341

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 ( "Abri 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 . g e t 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 . getSiguiente (); } // 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 l os 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 l os 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 GUARDAR;

493

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 332 a 340 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 307 y 308 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 311 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 313 a 320. 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 trabajando la e aplicacin. En las l o neas 321 a 324 simplemente se hace eco de lo que se escribi en o el disco como medida de vericacin. 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.8.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.4 en la o o siguiente pgina. a

10.8 Persistencia de la base de datos

494

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 9 desde un archivo en disco 9 9 9

Abrir el archivo 9Colocarse al prin9 9 7 cipio del archivo


5

Procesar registro 9P roceso 9 9 9 (mientras haya) 9


9 9 9 9 9 9 9 7F inal 3

Leer registro de disco Pasar al siguiente

Cerrar el archivo

Cdigo 10.4 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 )

495 Cdigo 10.4 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

Entrada y salida
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 neas 244 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.8 Persistencia de la base de datos

496

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.8.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.5. o

Cdigo 10.5 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 ( ) ;

497 Cdigo 10.5 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

Entrada y salida
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 l os e ( ) ; } 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 l os 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 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.9 Escritura y lectura de campos que no son cadenas

498

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.9 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 caracteres, donde o o sabemos donde empieza un registro y termina el otro porque contamos caracteres (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 bytes2 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

499

Entrada y salida

Figura 10.12

Formato de un archivo binario autodescrito


Primer campo

campo1 campo2 campo3 campo4 hkkkikkkjhkkkikkkjhkkkikkkjhkkkikkkjhkkkikkkjhkkkkkkkkkkkkkkkkkkkkkkkkkikkkkkkkkkkkkkkkkkkkkkkkkkj short short short short short

n n n n Nm de tamao tamao tamao tamao u campos

410
0 1

4010
2 3 4

910
5 6

410
7

2010
8 9

z ...
14 15 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

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 caracteres ASCII, no como an variables de Java; por supuesto que si hacen esto la mayor de estos caracteres a no se podrn ver en pantalla (por ejemplo, el 0 binario) o aparecern caracteres a a que no guardan ninguna relacin con lo que ustedes esperar ver. o an

10.9 Escritura y lectura de campos que no son cadenas

500

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 467, 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)

throws IOException throws IOException

Implementa el mtodo write de la clase OutputStream. e

public nal void write XXX ( Y Y Y par)

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

501

Entrada y salida

YYY 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 caracteres (2 bytes por carcter). 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.9 Escritura y lectura de campos que no son cadenas

502

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.6 para lo que tiene que ver con el proceso de la opcin y el listado 10.7 en la pgina opuesta para lo que tiene que o a ver con la opcin misma. o Cdigo 10.6 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 racin de los ujos la hacemos en las l o neas 141 y 142. Tanto en este caso como en

503

Entrada y salida

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. . . como , e readLine y writeLine y Data. . . Stream como readShort y writeShort hubisee mos tenido que hacer casting para que el compilador los identicara. Pero, aun cuando requerimos bsicamente un ujo de entrada y uno de salida, no podr a amos tener nada ms una declaracin por funcin ya que la superclase comn a Reader a o o u e InputStream es Object, y el usar a Object en la declaracin hubiese requerido de o casting prcticamente en cada momento de uso de los ujos. La asignacin de un a o valor null en la declaracin es para detectar, en su caso, si el ujo pudo construirse o o no. Adicionalmente, dadas las caracter sticas de las excepciones, donde puede haber cdigo que no se ejecute, en el bloque catch no se tiene la seguridad de que o 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.6 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.7 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.9 Escritura y lectura de campos que no son cadenas

504
MenuListaIOReg (2/3)

Cdigo 10.7 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 ;

505 Cdigo 10.7 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

Entrada y salida
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.9 Escritura y lectura de campos que no son cadenas

506

10.9.1. Figura 10.13

Escritura en archivos binarios Algoritmo para escribir archivo binario


6 9 9 9 9Abrir el archivo 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

archivo binario

9 9 9 descriptor 9 9 9 9 9de campos 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 7Cerrar el archivo

Escribir

6 9Escribir n mero de u 9 9 9 9 campos en binario 9 8

Escribir tamao n 9 9 9 9 9 de campo 9 7 descriptorr0s

6 9 8 9 7

Escribir campoi 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 498 y mostrado en la gura 10.12 o a en la pgina 499. a En las l neas 440 a 442 del listado 10.7 se implementa la parte correspondiente a abrir el archivo seleccionado mediante el mtodo pideNombreArch. Como este e

507

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 ser e a. 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.9 o a y 10.9 . 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 all de a a a la ejecucin de la aplicacin. o o

10.9.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.9 Escritura y lectura de campos que no son cadenas

508

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 Localizarlo y 8 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 n mero 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 7Cerrar el archivo

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

Comparemos ahora el algoritmo con el cdigo del listado 10.7. 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 clase DataInputStream y le pasamos como argumento un objeto de la clase FileIn-

509

Entrada y salida

putStream, 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 su u 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 k, n n donde k es el tamao de registro calculado. El unico problema que tenemos ac es n a que el acceso al ujo sigue siendo secuencial, as que los bytes que saltemos en

10.9 Escritura y lectura de campos que no son cadenas

510

la lectura ya no los podemos regresar3 . n el listado 10.8 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.8 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 ;

511 Cdigo 10.8 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

Entrada y salida
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.9 Escritura y lectura de campos que no son cadenas

512
MenuListaIOReg (3/3)

Cdigo 10.8 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.9.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 no e podemos hacer es querer leer el registro i y posteriormente el registro j con j i.4 Sin embargo, es muy comn querer un acceso directo5 . 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 En Java tenemos una clase que nos da esta facilidad y que es la clase RanPara 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 5 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.
4

513

Entrada y salida

domAccessFile. 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


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:

10.9 Escritura y lectura de campos que no son cadenas

514 (contina. . . ) u

class RandomAccessFile
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. Igual que rws, excepto que los meta-datos no tienen que actualizarse de manera sincronizada.

rwd

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 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

515

Entrada y salida

class RandomAccessFile

(contina. . . ) u

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 () void setLength(long newLength)

throws IOException throws IOException

Regresa el tamao del archivo en bytes. n

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.9.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

10.9 Escritura y lectura de campos que no son cadenas

516

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 9 u tamanhos[0] Primer short 9Leer nmero de 9 9 9 9 campos del archivo 9 9 9 9 9 9 9 6 6 9 9 9 9Leer siguiente 9 9 8 9Calcular tama o 8 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 9Leer cadena de 9 9 9 9Obtener del usuario el 9 9 9 9 8 9 9 consola 9 9 9 u 9 9 nmero del registro 9 9 9 9 9 9 Procesar d gito 9 9 9 9 9 9 solicitado 9 9 7 9 9 9 Agregar registros 9 mientras haya 9 8 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 9 9 9 Procesar registro 8Colocar el apuntador en esa posicin 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 9Leer carrera disco 9 9 9 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

517

Entrada y salida

El unico problema que enfrentamos es si suponemos un ujo en el que queramos leer cadenas y no tenemos caracteres de n de l 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 opuesta. 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.9. 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. o Cdigo 10.9 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.9 Escritura y lectura de campos que no son cadenas

518
case LEERDIRECTO (2/2)

Cdigo 10.9 Lectura del nombre del archivo y apertura del mismo o
609 610 611 612 613 614 615 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

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 // 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 pgina opuesta. a

519 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

Entrada y salida
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

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 px q 
n i 0

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

ci xi

Pero como acabamos de mencionar, esta es una manera costosa y poco elegante

10.9 Escritura y lectura de campos que no son cadenas

520

de hacerlo. La regla de Horner nos dice: P pxq  c0 xpc1 xpc2 xp. . .qq . . .q 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

 8000 700 20 5  8725

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

521

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.9 Escritura y lectura de campos que no son cadenas

522

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 de a u 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 agregarlo do 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 un bloque o try. En caso de que haya algn problema, simplemente descarta esta escritura y u procede a pedir el siguiente nmero de registro. u

10.9.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.

523

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 9 9Leer nmero 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 tama o 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 9 9 9 9Leer de consola 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.9 Escritura y lectura de campos que no son cadenas

524
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 {

525 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

Entrada y salida
case GUARDARDIRECTO (1/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.10 Lectura y escritura de objetos

526

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.10 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 a 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 ObjectOutput-

527

Entrada y salida

Stream 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.10 Lectura y escritura de objetos

528

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

529

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.10 Lectura y escritura de objetos

530

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

531

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.10 Lectura y escritura de objetos

532

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.

533

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 interfaz6 . 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 siguiente pgina 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.10 Lectura y escritura de objetos

534
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 nt a , String crrera ) { nombre = nmbre . t r i m ( ) ; cuenta = cnta . trim ( ) ; carrera = crre ra . 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 . ; // 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 { EstudianteSerial (1/2)

......
/ 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 Estud i an teS er i 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 } ;

535 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

Entrada y salida
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 cn ta , String crrera , String clve ) { public Estud i an teS er i al 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 que la hace o 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 o o
99 100 101 102 103 104 EstudianteSerial (1/2)

/ 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.10 Lectura y escritura de objetos

536
EstudianteSerial (2/2)

Cdigo 10.15 Conversin de Estudiante a EstudianteSerial y viceversa 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 cr rera = this . daCarrera ( ) ; S t r i n g c l v e = th is . daClave ( ) ; r e t u r n new E s t u d i a n t e ( nmbre , c n ta , 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 ;

......

537

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

Figura 10.17

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

Algoritmo para la lectura de objetos

6 9Abrir el archivo 9 9 6 9 9 9Leer el registro actual 9 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 9Inicio 9 9 9 9 9 9 9 9 9 9 9 8 5

Abrir el archivo Colocarse al principio de la lista

Escritura de Objetos

9 Tomar registro 9 9 9 9(mientras haya) 9 9 9 9 9 9 9 9 7

6 9Convertir al estudiante actual 9 9 8

en estudiante serial 9Escribirlo al ujo de objetos 9 9 7 Tomar como actual al siguiente

Cerrar el archivo

Antes de proceder con la codicacin de estos esquemas debemos nuevamente o declarar los ujos correspondientes y una variable para almacenamiento temporal

10.10 Lectura y escritura de objetos

538

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 = ( Estu di an teS er i al ) archvoObjetosIn . readObject ( ) ;

539 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

Entrada y salida
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.10 Lectura y escritura de objetos

540
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 970971 y 1030 1031 ya que esa es la denicin o del constructor de un ujo de objetos. 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 adecuado de cundo se acab el archivo y lo unico que podemos usar es la excepa o

541

Entrada y salida

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 a caso 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.11 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 deben 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

You might also like