Escolar Documentos
Profissional Documentos
Cultura Documentos
CITATIONS READS
0 567
3 authors, including:
Madrid Mxico Santaf de Bogot Buenos Aires Caracas Lima Montevideo San Juan San Jos
Santiago So Paulo White Plains
Datos de catalogacin bibliogrfica
ISBN: 84-205-3440-4
MATERIA: Informtica 681.3
DERECHOS RESERVADOS
2002 respecto a la primera edicin en espaol por:
PEARSON EDUCACIN, S.A.
Nez de Balboa, 120
28006 Madrid
ISBN: 84-205-3440-4
ISBN eBook: 978-84-8322-584-4
Edicin en espaol:
Equipo editorial:
Editor: David Fayerman Aragn
Tcnico editorial: Ana Isabel Garca
Equipo de produccin:
Director: Jos A. Clares
Tcnico: Diego Marn
Diseo de cubierta: Equipo de diseo de Pearson Educacin, S.A.
Sistemas basados
en computador
Sumario
1.1. INTRODUCCIN
Este tema pretende proporcionar una visin global de los sistemas basados en computador 1 y de la
informtica como disciplina. Desde la perspectiva de un texto introductorio como es ste, presentare-
mos el concepto de computacin, as como una pequea semblanza cronolgica, histrica, de las tc-
nicas de computacin que han desembocado en el ordenador moderno. A partir de aqu presentaremos
el concepto de informtica, como campo de conocimiento, y de sistema basado en computador. En el
cuarto apartado se considera la estructura y funcionamiento genrico de los computadores modernos.
Por su inters actual y su relacin con el lenguaje que se utilizar para la implementacin de los ejem-
plos (lenguaje Java) se presenta el tema de las redes de computadores y de Internet.
1
Segn el Diccionario de la Lengua de la Real Academia Espaola los trminos computador, ordenador y computado-
ra se pueden utilizar indistintamente. A lo largo de este libro as se usarn.
Sistemas basados en computador 3
Sistemas de numeracin
Una vez establecido un sistema de numeracin, la raza humana ide dispositivos de ayuda para
la realizacin de tareas aritmticas. En los primeros tiempos de la historia humana, para las ta-
reas ms sencillas no era necesaria una habilidad aritmtica ms all de sumas y restas simples o
de multiplicaciones sencillas. Al aumentar la complejidad de la vida en comn, se incrementa la
complejidad de los clculos aritmticos necesarios para los tratos comerciales, los impuestos, la
creacin de calendarios o las operaciones militares. Para agilizar la realizacin de estos cmpu-
tos la primera ayuda es la de los dedos, para simbolizar cantidades e incluso realizar opera-
ciones.
Los dedos pueden usarse de la forma ms simple para indicar una cantidad mostrando el nme-
ro de dedos equivalentes. Sin embargo, tambin pueden usarse de forma simblica para representar
cantidades arbitrarias con combinaciones distintas de dedos mostrados u ocultados. Una primera
necesidad en la antigedad fue la de disponer de un medio de representar cantidades que fuera cono-
cido por todos los pueblos (al menos en el entorno euro-asitico-africano clsico). Esta tcnica se
usaba fundamentalmente para el intercambio comercial entre pueblos cuyas lenguas podan ser des-
conocidas entre s. En la antigedad clsica existi este sistema que usaba los dedos (de las dos
manos) para representar simblicamente cantidades hasta de 9999 elementos y que estaba extendi-
do por el norte de frica, Oriente Medio y Europa. Herodoto y otros autores ms modernos como
Cicern o Marco Fabio Quintiliano lo mencionan. Con los dedos se pueden aplicar tcnicas de
clculo ms complejas que la simple enumeracin de elementos. Por ejemplo, en Europa existieron
hasta pocas relativamente recientes, tcnicas de multiplicacin que usaban las manos para realizar
los clculos.
Sin embargo, el medio mecnico ms antiguo de realizacin de clculos parece ser el baco (en
sus diferentes versiones). El baco es, en esencia, una tabla de conteo que puede ser tan simple
como una serie de piedras colocadas sobre el suelo. Su estructura tpica es la de un marco de made-
ra con una serie de alambres donde se ensartan varias cuentas, vase la Figura 1.1. Esta herra-
mienta, a pesar de su simplicidad, es una gran ayuda de computacin si se usa adecuadamente.
Actualmente se asocia el baco con Oriente pero se us en Europa desde la antigedad clsica has-
ta har unos 250 aos. La potencia del baco reside en que no es una simple tabla de anotaciones;
adems se pueden realizar operaciones aritmticas con l. De hecho, la capacidad de clculo con
un baco es muy alta. Un ejemplo de su eficacia es el siguiente: en 1946 en un concurso de velo-
cidad y precisin de clculo, Kiyoshi Matsuzake del Ministerio de Administracin Postal Japons
derrot con un baco, en cuatro de cinco ocasiones, al soldado Thomas Nathan Wood del ejrcito
americano de ocupacin que era el operador ms experto de mquina mecnica de calcular de la
marina de los EE.UU.
4 Introduccin a la programacin con orientacin a objetos
En la evolucin hacia el computador moderno, merece mencin especial el escocs John Napier,
barn de Merchiston, quien invent los logaritmos en el siglo XVII 2. Como herramienta de clculo los
logaritmos permiten transformar las multiplicaciones en sumas y las divisiones en restas. Los logarit-
mos presentan una tremenda utilidad prctica, pero el problema del barn era la creacin de las tablas
de logaritmos necesarias para su uso. En la historia de los medios de computacin el barn es impor-
tante por haber diseado varios instrumentos de cmputo. Entre ellos el denominado rabdologia, popu-
larmente conocido como huesos de Napier, que l usaba para calcular sus tablas. Estos huesos
eran una serie de barras cuadrangulares que en esencia representaban la tabla de multiplicar y que per-
mitan realizar esta operacin. El apelativo de huesos deriva del aspecto que presentaban y del mate-
rial del que muchas veces estaban hechos. Otros dispositivos de clculo de Napier fueron el Prontuario
de Multiplicacin (una versin ms elaborada de los huesos) y el dispositivo de Aritmtica Local,
un tablero de ajedrez modificado para ser usado como una especie de baco que trabajaba en sistema
binario. Napier public un libro describiendo el manejo de la rabdologia. El libro titulado Rabdologia
muestra tambin una de las primeras menciones del punto 3 decimal. Los huesos de Napier se difun-
dieron con rapidez por Europa.
Sin embargo, en la historia de la computacin la invencin de los logaritmos dio nacimiento al que
probablemente haya sido el dispositivo de cmputo ms usado desde el siglo XVII hasta la ltima mitad
del siglo XX: la regla de clculo. El origen de la regla de clculo es el siguiente. Tras conocer la inven-
cin de los logaritmos por Napier, Henry Briggs, Profesor de Geometra del Gresham College en Lon-
dres, comenz a trabajar en el tema, introduciendo los logaritmos en base 10 y creando nuevas tablas
de logaritmos para los nmeros enteros. Edmund Gunter, Profesor de astronoma y matemticas tam-
bin en el Gresham College, conoci por Briggs la existencia de los logaritmos. Gunter estaba intere-
sado en problemas de astronoma y navegacin, lo que implicaba el uso de funciones trigonomtricas.
Dado que las tablas de logaritmos de Briggs eran para nmeros enteros, no presentaban mucha utili-
dad para Gunter, quien decidi abordar el clculo de tablas de logaritmos para senos y tangentes. Gun-
ter haba trabajado en la popularizacin y desarrollo del comps de sector, un instrumento de clculo
2
El apellido Napier se escriba de varias formas en la poca: Napier, Napeir, Napair, Nepier y algunas formas ms, y de
l toman el nombre los logaritmos en base e, los logaritmos neperianos.
3
Entre los anglosajones los decimales se indican con punto, no con coma.
Sistemas basados en computador 5
consistente en un par de brazos unidos en un extremo por un pivote en forma de comps y con una
serie de escalas calibradas sobre cada uno. Puesto que el logaritmo de un producto es la suma de los
logaritmos, Gunter pens en un sistema parecido al sector que permitiera realizar mecnicamente un
producto. Gunter ide una regla graduada en escala logartmica con un comps. Para multiplicar dos
nmeros x e y se abra el comps la cantidad x, midiendo sobre la regla logartmica. A continuacin
se colocaba uno de los brazos del comps apoyado en el punto de la regla correspondiente al valor y.
Sin cerrar el comps se colocaba el otro brazo en la direccin creciente de la escala. El proceso equi-
vala a sumar las dos distancias en la escala (suma de logaritmos), as que el valor que sealaba el bra-
zo final del comps sobre la escala logartmica era el producto de los dos nmeros. Para hacer un
cociente se sustraan las distancias en lugar de sumarse. El dispositivo simplificaba el clculo de pro-
ductos y cocientes y tambin evitaba tener que gastar tiempo buscando en las tablas de logaritmos.
La modificacin final del dispositivo se debi a William Oughtred quien hoy sera definido
como un matemtico puro y que se puede considerar el inventor de la versin definitiva de la regla
de clculo. En una visita que realiz en 1610 a Henry Briggs, conoci a Edmund Gunter quien le
mostr su instrumento de clculo logartmico. Oughtred se dio cuenta de que se poda eliminar la
necesidad del comps si se usaban dos reglas graduadas logartmicamente que se deslizaran una con
respecto a la otra. As, para multiplicar los dos nmeros x e y bastaba con colocar el origen de la
segunda escala sobre el punto del valor x en la primera, localizar el valor y en la segunda y mirar
cul era el valor que corresponda en la primera escala. Este sistema de reglas deslizantes en escala
logartmica es la base de todas las reglas de clculo posteriores 4. Las reglas de clculo evoluciona-
ron a lo largo del tiempo hasta adquirir forma muy sofisticada, vase la Figura 1.2, representando
un instrumento analgico de cmputo de precisin. Hasta la introduccin de las calculadoras
electrnicas de mano, la regla de clculo era un instrumento que todo ingeniero o cientfico usaba
en su trabajo cotidiano.
Dispositivos mecnicos
Aparte de los instrumentos manuales indicados en el apartado anterior, los intentos autnticos de com-
putacin automtica comienzan con el desarrollo de los distintos modelos de calculadoras mecnicas.
Sin embargo, el desarrollo prctico de estas mquinas tuvo que esperar hasta el siglo XVII cuando la
4
Por esta razn, en ingls la regla de clculo se denomina sliding rule (regla deslizante).
6 Introduccin a la programacin con orientacin a objetos
ingeniera mecnica estuvo lo suficientemente desarrollada como para permitir la construccin de los
sistemas de engranajes y palancas en los que se basa su funcionamiento.
La primera calculadora mecnica (sumadora) con un dispositivo de acarreo para tener en cuenta
que se ha conseguido pasar a una posicin decimal superior (el me llevo uno de la aritmtica ele-
mental) se atribuye a Blaise Pascal. Sin embargo, la primera fue realizada por el matemtico alemn
Wilhelm Schickard a principios del siglo XVII quien comunic su invencin a Kepler, con quien haba
colaborado. Por la descripcin y los diagramas que Schickard remiti a Kepler se sabe que la mqui-
na de Schickard automatizaba el trabajo con una serie de huesos de Napier, realizando mecnica-
mente las sumas implicadas en la obtencin de multiplicaciones. La mquina representaba cada
posicin decimal con una rueda donde estaban representados los diez dgitos 1-2-3-4-5-6-7-8-9-0. El
problema de la mquina era el del acarreo acumulado cuando se pasaba de 9 a 0 en un disco y haba
que mover el disco de la siguiente posicin decimal en una unidad. Esto se realizaba con una rueda
dentada (rueda de acarreo) con un diente que haca avanzar a la rueda a la nueva posicin decimal
cuando la de la posicin anterior daba una vuelta completa. El problema se entiende si imaginamos
que tenemos el valor 9999999 y sumamos 1. Habr un total de 7 discos que tienen que girar a la vez
a base de ruedas dentadas con un solo diente (una por posicin decimal). Es fcil entender que la rue-
da de acarreo del primer dgito debe aguantar el esfuerzo necesario para poder girar todos los dems
dgitos. En la prctica el sistema no poda aguantar el esfuerzo y se rompa si el nmero de posicio-
nes decimales era grande. Schickard slo construy mquinas con un mximo de seis posiciones
decimales.
Posteriormente y de forma independiente Pascal desarroll una serie de ingenios mecnicos simi-
lares. La primera mquina fue diseada cuando Pascal contaba 19 aos. Cuando intent que los arte-
sanos locales construyeran las piezas necesarias, el resultado fue tan catastrfico que decidi l mismo
aprender mecnica, e incluso trabaj con un herrero para aprender a manejar el metal y construir las
piezas. Pascal construy unas cincuenta mquinas a lo largo de su vida, en esencia todas mquinas
sumadoras. El problema de Pascal para construir mquinas capaces de multiplicar era nuevamente
acumular el acarreo sobre varias posiciones decimales. Pascal ide un sistema de pesos que evitaba el
sistema de engranajes de Schickard. El problema era que la mquina slo poda avanzar sus engrana-
jes en un sentido. En la prctica esto se traduca en que la mquina slo poda aumentar acarreos, no
disminuir o dicho de otra forma, slo sumaba.
Otro interesante diseo es el de la mquina de Leibniz. Habiendo odo hablar de la mquina suma-
dora de Pascal, Leibniz se interesa por el tema y comienza con el diseo de una mquina multiplica-
dora. El diseo original no era factible y Leibniz abandona el tema durante varios aos. Finalmente,
acaba construyendo una mquina multiplicadora operativa gracias a la invencin de un elegante siste-
ma de engranajes con dientes de anchura variable (tambor escalonado). Otras calculadoras mecnicas
fueron construidas por personajes como el ingls Samuel Morland o el francs Ren Grillet ambos en
el siglo XVII.
Comercialmente, la primera calculadora mecnica de utilidad fue el aritmmetro de Thomas de
Colmar fabricado en la dcada de 1820 en Francia y basado en el diseo de tambor escalonado de
Leibniz. Sin embargo, el gran paso en la produccin comercial de calculadoras mecnicas se da con
las mquinas de Baldwin-Odhner. El problema con las calculadoras mecnicas previas era que el sis-
tema de tambor escalonado de Leibniz resultaba un dispositivo pesado y engorroso que implicaba que
las mquinas fueran grandes y masivas. A finales del siglo XIX, Frank. S. Baldwin en EE.UU. y W. T.
Odhner, un suizo que trabajaba en Rusia, idearon un nuevo diseo para las ruedas dentadas que repre-
sentaban los dgitos decimales de cada posicin decimal. La idea era que el nmero de dientes en las
ruedas fuera variable, correspondiendo el nmero de dientes al nmero representado. Estos dientes
podan aparecer o desaparecer segn se seleccionaba un dgito u otro con una palanca colocada sobre
la propia rueda. Estas ruedas dentadas variables se podan construir como discos finos y ligeros, lo que
permita colocar varios de estos discos, representando cada uno un dgito, en unos pocos centmetros
de espacio. El resultado era una mquina mucho ms compacta y ligera que las existentes hasta enton-
ces, vase la Figura 1.3. A principios del siglo XX estas mquinas se vendan por decenas de miles.
Sistemas basados en computador 7
todo el proceso. Su idea era la de una mquina capaz de calcular e imprimir sin intervencin humana
las tablas matemticas deseadas. En la poca, las tablas se calculaban aproximando las funciones a cal-
cular por formas polinmicas y manejando las formas polinmicas con el mtodo de diferencias. Usan-
do el mtodo de diferencias para representar un polinomio se evitaba tener que realizar operaciones de
multiplicacin y divisin. Este mtodo puede an verse explicado en textos de clculo numrico apli-
cado al problema de la interpolacin de funciones (Demidovich y Maron, 1977; Kopchenova y Maron,
1987). Babbage imagin su mquina de clculo de tablas como una Difference Engine, Mquina de
Diferencias, que aplicara de forma automtica el mtodo de diferencias. Babbage construy un
pequeo prototipo y solicit ayuda oficial para la construccin del diseo completo. El problema era
que la tecnologa mecnica no estaba suficientemente avanzada en aquel entonces para la construccin
de algunas partes de la mquina. El mismo Babbage colabor en el desarrollo de nuevas herramientas
de fabricacin mecnica que permitieran construir las piezas de la mquina. Este trabajo adicional y
las demoras oficiales en la provisin de fondos hicieron que el trabajo se parara numerosas veces y
que finalmente la mquina no acabara de construirse. Durante uno de estos perodos de inactividad
Babbage trabajaba en un rediseo de la mquina y se le ocurri que el resultado de las computaciones
de la mquina pudiera volver a ser introducido como dato en la propia mquina. Babbage se dio cuen-
ta de que ese diseo circular dotaba a la mquina de una potencia de cmputo mucho mayor que la del
modelo inicial. Un diseo tal permita el manejo de funciones que no tenan solucin analtica. Bab-
bage denomin la nueva mquina Analytical Engine (Mquina Analtica) y al respecto de la misma
escribi en una carta en mayo de 1835 (Williams, 2000):
... durante seis meses he estado dedicado a los diseos de una nueva mquina de clculo de
mucha mayor potencia que la primera. Yo mismo estoy asombrado de la potencia de que he
podido dotar a esta mquina; hace un ao no hubiera credo que este resultado fuera posible.
El almacn era una memoria mecnica, el taller una unidad aritmtica y el cilindro de control una
unidad de control de procesos que contena el equivalente mecnico de un juego de instrucciones bsi-
cas de trabajo. En esencia, el diseo de Babbage responda a la estructura moderna de un computador.
El trabajo de la mquina analtica se realizaba indicndole qu acciones elementales tena que realizar
por medio de una serie de tarjetas perforadas, de forma similar a como entonces se introducan los
diseos en las tejedoras mecnicas de Jackard. La mquina, por lo tanto, responda a un programa de
instrucciones externo que lea como entrada.
La mquina analtica no lleg a construirse principalmente porque Babbage no consigui los fon-
dos necesarios. Desde la perspectiva actual, la mquina hubiera supuesto un avance de alcance inima-
ginable en la poca 5.
La necesidad de desarrollar el conjunto de instrucciones que compusieran un programa a ser eje-
cutado por la mquina analtica da carta de nacimiento a la ciencia y el arte de la programacin. En
este contexto tiene especial inters la colaboracin entre Babbage y Ada Augusta, condesa de Love-
5
Como fabulacin de lo que habra ocurrido en caso de construirse la mquina analtica se recomienda la lectura de la
novela The Difference Engine (Gibson y Sterling, 1996). Aqu se nos muestra un siglo XIX alternativo, donde Babbage ha
podido construir sus mquinas y un imperio britnico que ha conjugado la revolucin industrial con la revolucin informti-
ca controlando el mundo con sus computadoras mecnicas movidas a vapor.
Sistemas basados en computador 9
lace. Ada era hija del poeta Lord Byron y tena una slida formacin matemtica, algo muy raro en la
poca para una mujer. En 1843 public un trabajo donde se describa la mquina analtica y la mane-
ra de programarla. En particular en el trabajo se presentaba el programa que permita el clculo de los
nmeros de Bernoulli (Kim y Toole, 1999). En su trabajo, Ada mostraba la gran potencia y la flexibi-
lidad que un programa modificable de instrucciones permita a la mquina. Por estas razones, la con-
desa de Ada Lovelace es considerada la primera terica (y prctica) de la programacin.
El computador moderno
A finales de la dcada de 1930 aparecieron distintos grupos de trabajo interesados en la construccin
de mquinas de calcular con algn tipo de sistema automtico de control. Estos esfuerzos se aborda-
ron tanto desde el punto de vista mecnico como del electrnico.
Respecto a las mquinas mecnicas, destaca el trabajo en Alemania de Konred Zuse. Zuse, inge-
niero de formacin, conoca el esfuerzo de cmputo necesario para los trabajos tcnicos. Se dio cuen-
ta de que el problema fundamental, usando una regla de clculo o una mquina sumadora mecnica,
era el almacenamiento de los resultados intermedios que se van produciendo. A tal efecto es necesa-
rio un sistema de memoria para mantener la informacin. En 1934 Zuse era consciente de que una cal-
culadora automatizada slo requiere tres unidades funcionales: un control, una memoria y una seccin
aritmtica. Con este diseo bsico construye la Z1 (la primera mquina de la serie Z). La Z1 usaba una
memoria mecnica codificada en binario y lea la secuencia de instrucciones a realizar de una serie de
tarjetas perforadas. Al mismo tiempo en Harvard, Howard Aitken construa otra secuencia de mqui-
nas automticas, la serie de las Mark.
Todos estos esfuerzos se basaban an en el uso de elementos mecnicos. La gran revolucin sur-
gi con el advenimiento de las mquinas electrnicas. Con respecto a las mquinas electrnicas uno
de los primeros esfuerzos fue el diseo de la ABC (Atanasoff-Berry Computer). El ABC no lleg a ser
operativo, pero su diseo tuvo importancia en el desarrollo de modelos posteriores. En particular, el
ABC usaba el sistema binario, lo que simplificaba los circuitos electrnicos usados. La primera com-
putadora electrnica operativa fue la ENIAC (Electronic Numerical Integrator and Computer) que tra-
bajaba en sistema decimal.
Todas estas mquinas estaban programadas con algn tipo de instrucciones en tarjetas perforadas
o directamente como conexiones (cableado). El siguiente paso se gest en el equipo de desarrollo del
ENIAC y se trata de la invencin del concepto de programa almacenado. En este asunto tuvo cierta
participacin John von Neumann (fsico, qumico y matemtico) aunque no fue el inventor del con-
cepto. Slo el hecho de que l escribiera el borrador del informe que se present a los patrocinadores
militares del proyecto ENIAC, y que recoga la idea de problema almacenado en memoria, fue la cau-
sa de que se asociara con l dicho concepto y hoy se hable de mquinas de von Neumann. Este nuevo
concepto form parte del diseo de la descendiente del ENIAC, la EDVAC (Electronic Discrete Varia-
ble Arithmetic Computer). La EDVAC almacenaba el programa en memoria, con lo que las instruc-
ciones se lean a mucha mayor velocidad que hacindolo una a una desde una fuente externa como las
tarjetas perforadas.
a) Datos
Como tales se entiende el conjunto de smbolos usados para representar un valor numrico, un hecho,
una idea o un objeto. Individualmente los datos tienen un significado puntual. Como ejemplo de dato
tenemos el nmero de la seguridad social de un empleado, un nmero de telfono, la edad de una per-
sona, etc.
b) Informacin
Por tal se entiende un conjunto de datos procesados, organizados, es decir, significativos. La informa-
cin implica tanto un conjunto de datos como su interrelacin. Dependiendo de esta ltima el mismo
conjunto de datos suministra diferente informacin. Por ejemplo, imaginemos los datos de los traba-
jadores de una empresa:
Nombre
Edad
Estudios
Salario
Por separado se trata de un conjunto de datos individuales. Sin embargo, si los organizamos por
edad y salario tenemos un informe sobre la distribucin del sueldo en funcin de la edad. Por otro lado,
si organizamos por estudios y salario tendremos un informe diferente que nos indica la distribucin
del salario en funcin de la formacin de los empleados.
Como no vamos a tratar especficamente sistemas de gestin de informacin consideraremos datos
e informacin como sinnimos.
a) Representacin analgica
Cuando una magnitud fsica vara para representar la informacin tenemos una representacin anal-
gica. Por ejemplo, el voltaje en funcin de las variaciones de presin producidas por la voz en un
micrfono.
Sistemas basados en computador 11
b) Representacin digital
En este caso la informacin se divide en trozos y cada trozo se representa numricamente. Lo que se
maneja al final es ese conjunto de nmeros. La cantidad de trozos en que se divide lo que se quiere
representar est relacionada con la calidad de la representacin. La Figura 1.4 ilustra este concepto
considerando una cierta magnitud, X, que vara con el tiempo, t.
En el caso de la Figura 1.4 (a) el espaciado entre los puntos tomados sobre el eje de abscisas no es
suficiente para representar el pico central. En el segundo caso, sin embargo, s recogemos el pico y la
representacin es ms fiel a la realidad. Cuanto menor sea el intervalo entre puntos ms fiable es la
representacin, aunque mayor es el nmero de datos que necesitamos para representar el mismo inter-
valo (temporal en este ejemplo). Lo que al final almacenaramos para representar la informacin repre-
sentada por la curva anterior sera el conjunto de valores de ordenada para cada punto tomado sobre
el eje de abscisas.
En los ordenadores modernos toda la informacin est almacenada digitalmente, desde los nme-
ros al texto pasando por el audio o el vdeo. Esto nos lleva a una cuestin: cmo est representado el
texto en un ordenador?
En un ordenador el texto est representado por un cdigo numrico. Cada carcter (letras mayscu-
las y minsculas, signos de puntuacin, signos especiales como #, @, &, etc.) tiene asociado un valor
numrico. Estos valores numricos son arbitrarios y se asigna un valor u otro dependiendo del cdigo
usado. Un cdigo tpico y tradicional es el cdigo ASCII (American Standard Code for Information Inter-
change) pero el conjunto de caracteres es muy limitado, slo el conjunto bsico necesitado en ingls, sin
caracteres acentuados, por ejemplo. Existen otros cdigos, como el Unicode que puede codificar 216 posi-
bilidades (usa 16 bits), con lo que se pueden representar los caracteres de multitud de lenguajes sin tener
que estar mezclando cdigos. Por ejemplo, en Unicode la frase Hola, Pepe. queda como:
H o 1 a , P e p e .
En el ejemplo anterior cada carcter muestra en la parte inferior el correspondiente cdigo Unico-
de. Obsrvese que el blanco es un carcter con su cdigo (el ordenador lo tiene que almacenar para
saber que est ah) y que las maysculas tienen cdigo distinto de las minsculas. Al final lo que habra
en el ordenador sera la secuencia anterior de nmeros.
Otro problema es cmo representar los nmeros. Resulta conveniente, por su simplicidad, usar el
sistema de numeracin binario donde slo tenemos 2 dgitos: 0 y 1. Estos dos dgitos se pueden repre-
sentar fcilmente en los circuitos electrnicos, por ejemplo como conduccin o no conduccin o, en
Figura 1.4. Ilustracin del efecto del incremento de muestras sobre una seal
12 Introduccin a la programacin con orientacin a objetos
general, por cualquier mtodo que pueda distinguir entre dos estados. La base dos se maneja con nota-
cin posicional igual que la decimal. Al construir el nmero se comienza con el 0, luego se pasa al 1
y al agotar la base se coloca un 0 o un 1 a la izquierda, vase la Tabla 1.1.
Decimal Binario
0 0
1 1
2 10
3 11
En informtica un dgito binario se denomina bit (contraccin de binary digit). Otra unidad comn
es el conjunto de 8 bits, denominado byte:
Es interesante saber cul es el mayor valor numrico que se puede representar con un cierto nme-
ro de bits. Puesto que con un bit se pueden representar dos posibilidades, con 2 bits tendremos cuatro
(2 3 2) y en general, con N bits tenemos 2N, vase la Tabla 1.2.
Bits Posibilidades
0 2051
1 2152
2 2254
3 2358
4 24516
5 25532
6 26564
7 275128
8 (1 byte) 285256
9 295512
10 21051024
Esta secuencia de valores es muy tpica, aparece por ejemplo en los valores de la memoria de los
ordenadores (aunque referida a un mltiplo del byte, el kilobyte como veremos ms adelante).
Fsicas (Hardware 6 )
Lgicas (Software 6 )
Humanas (Peopleware)
La parte fsica del ordenador est constituida por los dispositivos que conforman el ordenador. La
palabra hardware es inglesa y literalmente significa material de ferretera. Con el advenimiento de la
informtica se empieza a aplicar en el contexto que aqu se describe. Se suele coloquialmente decir
que todo lo que se puede tocar es hardware. En el siguiente apartado consideraremos en ms detalle la
parte fsica de un ordenador.
La parte lgica est formada por los programas y toda la informacin asociada a ellos (informa-
cin de desarrollo y documentacin del programa). En ingls se denomina software, haciendo un jue-
go de palabras con hardware, cambiando el hard (rgido o duro) por soft (blando). El software es la
componente lgica, intangible, de un sistema informtico.
Siguiendo con los juegos de palabras la componente humana de un sistema informtico se deno-
mina a veces peopleware. La componente humana de un sistema se refiere generalmente a los usua-
rios del mismo.
Puesto que la programacin (una parte del proceso de desarrollo de software) va a ser el tema que
nos ocupe en este texto, vamos a considerar el software con algo ms de detalle en el siguiente apar-
tado.
6
Tanto hardware como software son palabras que el uso ha adoptado en nuestro idioma y en este texto se utilizan tal
cual.
14 Introduccin a la programacin con orientacin a objetos
dor (actualmente el ratn). La interfaz grfica del sistema operativo Windows es un ejemplo tpico. Des-
de el punto de vista del usuario la interfaz es el programa. Como una buena indicacin de diseo tene-
mos que la interfaz debe estar separada de la parte funcional del programa, vase la Figura 1.5. Las
ventajas de esta organizacin son un mejor mantenimiento de las dos partes componentes del software
(son independientes, se pueden modificar por separado) y mayor reutilizabilidad de los elementos de la
interfaz y de la parte funcional (se pueden aprovechar con facilidad en nuevos desarrollos).
* Unidad de entrada
* Unidad de salida
* Unidad de control
* Unidad aritmtica (hoy aritmtico-lgica)
* Memoria
Utilizacin del sistema binario (el ENIAC, por ejemplo, usaba sistema decimal).
Incorporacin del concepto de programa almacenado en la memoria. As, la memoria no slo
almacena los datos, sino tambin las instrucciones necesarias para el procesamiento de los mis-
mos.
c) Memoria
Sirve para almacenar los datos y las instrucciones del programa (recordemos que nuestro modelo
almacena el programa en memoria). Tenemos dos tipos de memoria, la primera es la denominada
memoria central. sta est formada por circuitera electrnica y de rpido acceso, pero relativamente
pequea. La memoria central se encuentra organizada en posiciones de memoria (grupos de un
tamao concreto de bits). Cada posicin est identificada por una direccin de memoria que permi-
te acceder a ella (para leer o para escribir). La direccin puede entenderse como un nmero de orden,
vase la Figura 1.7. Un dato puede necesitar ms de una posicin de memoria para su codificacin.
La memoria se mide en bytes (8 bits). Como sta es una unidad muy pequea se usan mltiplos,
vase la Tabla 1.3.
La memoria central es de tipo RAM (Random Access Memory) lo que indica que se puede acce-
der directamente a cualquier posicin de memoria sin pasar por las anteriores. La RAM es de lectura-
escritura, es decir, se puede leer la informacin almacenada all y se puede escribir en ella. Otra carac-
terstica es que es voltil, entendiendo por ello que la informacin slo se mantiene mientras est
conectada (al cortar la corriente se pierde). El tamao de la memoria central es de algunos cientos de
KB (128 256 en compatibles PC) a algunos o muchos GB en sistemas de altas prestaciones. En un
ordenador existen tambin memorias de tipo ROM (Read Only Memory) que son permanentes, slo
16 Introduccin a la programacin con orientacin a objetos
permiten la lectura y se usan para almacenar, por ejemplo, el programa de arranque de un ordenador
o las operaciones bsicas de entrada y salida.
El segundo tipo de memoria es la denominada masiva, auxiliar o secundaria, mucho ms lenta de
acceso que la memoria principal, pero de mucha mayor capacidad y permanente (no se pierde la infor-
macin al cortar la corriente). Se trata, fundamentalmente, de los discos duros, los disquetes o los CD-
ROM (almacenamiento ptico). Algunos de estos dispositivos son regrabables y otros, como los CD-
ROM tradicionales, son de slo lectura, no permitiendo la regrabacin. Normalmente los programas y
los datos se graban desde algn dispositivo de entrada en la memoria secundaria y desde ah se cargan
en la memoria principal para la ejecucin. La capacidad tpica, en la actualidad, de los discos duros es
de varias decenas de GB en los compatibles PC.
En un ordenador es posible encontrar tambin cierta cantidad de la denominada memoria cach
(oculta en francs). Una memoria cach es similar a la RAM pero mucho ms rpida que ella y se usa
como un elemento intermedio entre la CPU y la memoria central, vase la Figura 1.8.
Cuando la CPU necesita leer datos o instrucciones de la RAM primero mira si ya se encuentran en
la cach. Si estn all los toma de ella. Al ser la cach mucho ms rpida que la RAM el proceso se
realiza en mucho menos tiempo. Si los datos o la instruccin no estn en la cach, la CPU los lee de
la RAM y se guarda una copia en la cach para poder tomarla de all si se vuelven a necesitar. El resul-
tado de este proceso es una mejora en el rendimiento de la CPU. En un sistema moderno se dispone
de todos estos tipos de memoria.
e) Unidad de control
Coordina los distintos pasos del procesamiento. En esencia recibe seales (de estado) de las dis-
tintas unidades determinando su estado de funcionamiento. Capta de la memoria central las instruc-
ciones del programa una a una y va colocando los datos en los registros correspondientes haciendo que
las distintas unidades implicadas realicen sus tareas. El trabajo de la unidad de control est sincroni-
zado por un reloj interno, que oscila con una frecuencia dada. La velocidad de trabajo de la CPU vie-
ne determinada por la frecuencia del reloj. La frecuencia se mide en ciclos por segundo o Hertzios (Hz)
y la unidad que se usa es un mltiplo, el MegaHertzio (1 MHz5106 Hz). Las frecuencias actuales de
los microprocesadores son del orden de 1000-2000 MHz (1-2 GHz). La inversa de la frecuencia es el
perodo y nos da el tiempo que tarda en realizarse un ciclo.
La informacin entre la CPU y la memoria se transfiere como grupos de un cierto nmero de bits
que se pasan a la vez. Esto define la palabra y, el nmero de bits transferidos de una vez es la longi-
tud de la palabra. Ejemplos de longitud de palabra son 32 64 bits. Como para referirnos a una posi-
cin de memoria usamos una palabra en la CPU, la longitud de palabra determina el mximo nmero
de posiciones que se pueden representar (2N donde N es la longitud de palabra). Se puede tener menos
memoria central que la que corresponde a ese valor pero no se puede tener ms.
La CPU funciona siguiendo lo que se denomina ciclo de recuperacin-descodificacin-ejecucin, va-
se la Figura 1.9. El ciclo funciona de la siguiente forma. De la memoria central se recupera la siguiente
instruccin del programa buscando en la direccin indicada por el contador del programa. El contador se
incrementa para saber en el siguiente ciclo donde est la siguiente instruccin. La instruccin actual se des-
codifica para saber qu operacin hay que realizar y la unidad de control activa los circuitos necesarios
para realizar la instruccin, la cual puede cargar un dato en un registro o sumar dos valores, por ejemplo.
1.5.1. GENERALIDADES
Una red consiste en dos o ms ordenadores conectados entre s de forma que puedan compartir infor-
macin o recursos. En particular el intercambio de informacin ha devenido en una parte fundamen-
tal de la informtica. La comunicacin por correo electrnico, el intercambio de informacin tcnica
y cientfica o la comparticin de la informacin de los clientes de una empresa en diferentes sucursa-
les de la misma son ejemplos de la utilidad de las redes de computadores. A pesar de todo, en una red
cada ordenador tiene su individualidad propia, poseyendo algn tipo de informacin de identificacin,
una direccin de red. Dentro de una red el o los ordenadores que ofrecen algn tipo de servicio a los
dems se denominan servidores, por ejemplo un servidor de ficheros o de impresora.
Para conectar computadoras en red podemos usar distintas topologas, por ejemplo:
Una LAN es una red de rea local (Local Area Network) que cubre una pequea distancia y est
formada por un nmero pequeo de ordenadores. Una WAN es una red de rea ancha o amplia (Wide
Area Network) que conecta dos o ms LANs sobre grandes distancias. Una LAN pertenece normal-
mente a una sola organizacin pero las WANs suelen conectar LANs de grupos diferentes, incluso de
pases distintos. En las LAN conectadas en una WANs un ordenador de cada LAN maneja las comu-
nicaciones sobre la WAN.
1.5.2. INTERNET
Internet es una WAN que abarca todo el mundo. El trmino Internet proviene de internetworking indi-
cando que es una red de redes. Internet permite la comunicacin entre sistemas hardware y software
heterogneos usando una serie de estndares de comunicacin. Internet es descendiente de la ARPA-
NET, un sistema de red desarrollado en un proyecto del ARPA (Advanced Research Projects Agency)
de los EE.UU. La historia del desarrollo de Internet puede encontrarse en el excelente libro de Hafner
y Lyon (Hafner y Lyon, 1998).
El software que gestiona la comunicaciones en Internet se denomina TCP/IP (Transmission Con-
trol Protocol/ Internet Protocol). Son dos entidades separadas, cada una conteniendo muchos progra-
mas. Estas dos entidades podran definirse de la forma siguiente (Comer, 1995):
a) IP
Se trata de un protocolo y no de un programa especfico. Sus misiones son varias. Define el formato
de todos los datos sobre la red, tambin realiza el proceso de routing (direccionamiento en la red) esco-
giendo el camino por el que circularn los datos, y finalmente establece una serie de reglas indicando
cmo los ordenadores deben procesar los paquetes de datos, cmo y cundo deben generarse mensa-
jes de error y las condiciones bajo las cuales se deben descartar paquetes.
b) TCP
Se trata tambin de un protocolo de comunicacin y no de una pieza de software. La misin concreta
del TCP es difcil de definir en pocas palabras. El protocolo especifica el formato de los datos y los
reconocimientos que dos computadores deben intercambiar para obtener una transmisin fiable, as
como los procedimientos que los computadores usan para asegurarse de que los datos llegan correcta-
mente a su destino.
En Internet cada ordenador conectado se identifica con lo que se denomina una direccin IP. La
direccin IP identifica tanto al ordenador como a la red a la que est conectada. Dicho en otras pala-
bras, la direccin IP no identifica una mquina sino una conexin a una red. Nada impide sustituir un
ordenador por otro y mantener la direccin IP original. Las direcciones IP estn formadas por 32 bits
organizados en cuatro grupos de ocho. Cada uno de esos grupos de ocho se expresa en decimal y se
separan unos de otros por un punto, por ejemplo:
167.55.44.11
Es normal que la direccin IP tenga asociado un nombre que se suele denominar direccin de Inter-
net como,
pepe.uclm.es
20 Introduccin a la programacin con orientacin a objetos
La primera parte es el nombre asignado a ese ordenador en concreto (pepe en el ejemplo), la segun-
da parte es el dominio e indica la organizacin a la que pertenece (uclm.es en el ejemplo). La ltima
seccin de cada nombre de dominio indica, normalmente, el tipo de organizacin:
o bien el pas:
.es: Espaa
.uk: Reino Unido
Cuando se usa una direccin de Internet sta se traduce a direccin IP por un software denomina-
do DNS (Domain Name Service). Cada organizacin conectada a Internet tiene un servidor de domi-
nio con una lista de todos los ordenadores de esa organizacin y sus direcciones IP. Cuando se pide
una IP, si el servidor DNS no la tiene, contacta a travs de Internet con otro servidor que s la tenga.
Hipertexto: Una forma de texto no secuencial en la cual se siguen unos caminos o enlaces a travs
del conjunto completo de documentos. El concepto no es nuevo, se encontraba ya pergeado en algu-
nos documentos del proyecto Manhattan (el proyecto secreto de los EE.UU. destinado al desarrollo de
la bomba atmica). La idea es poder saltar entre la informacin del documento en forma no secuen-
cial.
Hipermedia: Es una generalizacin del concepto de hipertexto donde no slo se incluye texto en
la secuencia no lineal de informacin sino tambin grficos, audio, vdeo o programas.
Al web se accede a travs de un programa especial, un navegador (browser) que presenta la infor-
macin hipermedia, y con el que se va accediendo a los distintos enlaces, links, para obtener la infor-
macin deseada. Lgicamente, los documentos en el web se identifican con algn tipo de nombre, aqu
denominado URL (Uniform Resource Locator), por ejemplo:
www.inf-cr.uclm.es
Con una URL se accede a una pgina web donde aparece la informacin fundamentalmente como
en una pgina impresa. Estas pginas estn escritas usando un lenguaje estndar formalizado llamado
HTML (HyperText Markup Language 8). En la actualidad se est trabajando con versiones ms sofis-
ticadas del HTML como el XML (Extensible Markup Language) (Bosak y Bray, 1999).
Por ltimo, merece la pena indicar que el lenguaje de programacin Java (lenguaje que vamos a
usar para implementar todos los ejemplos de programacin en este texto) ha sido diseado con la capa-
cidad de interaccin a travs de Internet y, en particular, para poder generar aplicaciones que se eje-
cuten a travs de una pgina web. La tcnica para ello es simple. Al solicitar la ejecucin del programa
7
World Wide Web significa literalmente red (o telaraa) de alcance mundial. Normalmente se conoce por las iniciales
www e incluso como w 3 o w cubo.
8
HTML puede traducirse como lenguaje de marcas para hipertexto.
Sistemas basados en computador 21
a travs de la pgina web, pinchando en el correspondiente enlace con el ratn, una versin del pro-
grama se copia a la mquina del usuario donde se ejecuta de forma local. Los navegadores actuales
incorporan un intrprete de bytecode de Java 9 para poder ejecutar estos programas o Applets.
EJERCICIOS PROPUESTOS
Ejercicio 1.* Cul es la diferencia entre datos e informacin?
REFERENCIAS
BOSAK, J. y BRAY, T.: XML and the Second-Generation Web, Scientific American, 79-83, May 1999.
CERUZZI, P. E.: A History of Modern Computing, The MIT Press, 1999.
COMER, D. E.: Internetworking with TCP/IP. Volume I, Third Edition, Prentice-Hall, 1995.
DEMIDOVICH, B. P. y MARON, I. A.: Clculo Numrico Fundamental, Paraninfo, 1977.
GIBSON, W. y STERLING, B.: The Difference Engine, Orion paperback, 1996.
HAFNER, K. y LYON, M.: Where wizards stay up late. The origins of the internet, Touchstone, 1998.
KIM, E. E. y TOOLE, B. A.: Ada and the First Computer, Scientific American, 66-71, May 1999.
KOPCHENOVA, N. V. y MARON, I. A.: Computational Mathematics, MIR Publishers, Moscow, Fourth printing,
1987.
WILLIAMS, M. R.: A history of computing technology, Second edition, IEEE Computer Society Press, 2000.
9
El concepto de traductor e intrprete y de bytecode de Java se expondr en el Captulo 2.
2
Elementos de
programacin y lenguajes
Sumario
2.1. INTRODUCCIN
// Imprime un refrn
class Refran {
public static void main(String[] args) {
System.out.println(Donde fueres haz lo que vieres);
} // Fin mtodo main
} // Fin class Refran
Como podemos apreciar, es necesario indicar las instrucciones al computador usando un lenguaje
determinado (en el ejemplo anterior Java). Sin embargo, no existe un nico lenguaje sino que hay
muchos que se han ido desarrollando a lo largo del tiempo y que pueden ser clasificados en varios tipos
estndar.
a) Lenguaje mquina
b) Lenguaje ensamblador
c) Lenguajes de alto nivel
d) Lenguajes de cuarta generacin
Vamos a comentar cada uno de los tipos de lenguajes de menor a mayor nivel de abstraccin:
a) Lenguaje mquina
Es el lenguaje nativo de una CPU. Las instrucciones de este lenguaje se indican en binario. El cdigo
se expresa como una serie de dgitos binarios y es muy difcil para los humanos leerlo y escribirlo,
aunque antiguamente se haca. Hay un lenguaje mquina por cada tipo diferente de CPU. No podemos
por lo tanto hablar del lenguaje mquina, sino siempre de un lenguaje mquina determinado. Todo pro-
grama debe, en ltima instancia, ser traducido al lenguaje mquina del ordenador sobre el que se va a
ejecutar.
b) Lenguaje ensamblador
Corresponde a un mayor nivel de abstraccin que los lenguajes mquina. Un ensamblador utiliza
smbolos mnemotcnicos, palabras cortas, para hacer referencia a las instrucciones o datos del len-
guaje mquina, en lugar de usar los dgitos binarios directamente. Para ejecutar un programa escri-
to en ensamblador es necesario convertir el programa a lenguaje mquina. Un lenguaje
ensamblador corresponde a un determinado lenguaje mquina, por lo tanto no hay un solo lengua-
je ensamblador.
Estos dos lenguajes se consideran de bajo nivel de abstraccin operativa.
26 Introduccin a la programacin con orientacin a objetos
b) Lenguajes declarativos
Describen estructuras de datos y las relaciones entre ellas necesarias para una determinada tarea, indicando
tambin cul es el objetivo de la tarea. El programador no indica el procedimiento (el algoritmo) para rea-
lizar la tarea. Hay dos tipos de lenguajes declarativos:
b.1. Lenguajes funcionales. Basados en la definicin de funciones, como LISP.
b.2. Lenguajes de programacin lgica. Basados en la definicin de predicados (relaciones lgi-
cas entre dos o ms elementos) como PROLOG.
Tanto si se usa un tipo de lenguaje u otro, al final el ordenador siempre lo traduce a lenguaje
mquina, pues ste es el nico lenguaje que reconoce. El proceso de traduccin puede realizarse de
diversas formas. Segn el mtodo que se use para llevar a cabo la traduccin se habla de lenguajes
compilados o lenguajes interpretados.
a) Lenguajes compilados
Estos lenguajes realizan una traduccin completa del programa a lenguaje mquina (compilacin del
programa). Normalmente el proceso se realiza en dos etapas. En la primera, la compilacin, el cdigo
que hemos escrito, denominado cdigo fuente, se traduce a lo que se denomina cdigo objeto que an
1
Librera es una mala traduccin del trmino ingls library que significa biblioteca. Este trmino es muy comn en
informtica.
Elementos de programacin y lenguajes 27
no es ejecutable. Para serlo, este cdigo objeto debe enlazarse con los mtodos, funciones o procedi-
mientos predefinidos en el lenguaje, y que se encuentran en libreras 1 externas. En el segundo paso
denominado de enlazado (linkado 2) se incorporan estas funciones, mtodos o procedimientos y el
resultado es un programa ejecutable, es decir, un programa en lenguaje mquina que puede funcio-
nar, bajo una CPU determinada, vese la Figura 2.1.
b) Lenguajes interpretados
El cdigo que se ha escrito, cdigo fuente, se va leyendo poco a poco y va traducindose y ejecutndo-
se segn se traduce. Aqu no hay una traduccin completa, no generamos un programa directamente eje-
cutable. Por eso, tradicionalmente, los lenguajes interpretados son menos eficientes que los compilados.
En particular, el lenguaje Java aplica una aproximacin intermedia entre estas dos. Existe una
compilacin inicial donde el compilador de Java traduce el cdigo fuente a bytecode, el cual es
una representacin de programa a bajo nivel. El bytecode no es el lenguaje mquina de ninguna
CPU. El bytecode sera el cdigo mquina de una hipottica CPU que hoy por hoy no existe fsica-
mente 3. En este contexto se dice que el bytecode se ejecuta sobre una mquina virtual. Tras la com-
pilacin, el bytecode se interpreta. El intrprete de JAVA lo lee y lo ejecuta en una mquina
concreta. El bytecode es estndar y no depende de ninguna CPU. La idea es que pueda ejecutarse en
cualquier mquina. Interpretar el bytecode es ms rpido que interpretar directamente el cdigo
fuente, puesto que el bytecode est ms prximo al lenguaje mquina que el fuente original. El byte-
code es transportable de mquina a mquina, aunque debe haber para cada tipo de procesador un
intrprete de bytecode para poder ejecutar los programas. De forma esquemtica tendramos el dia-
grama de la Figura 2.2. En l, se muestra un bucle en el proceso de interpretacin, indicando que es
un proceso iterativo donde se lee una seccin del bytecode y se ejecuta, repitindose el proceso has-
ta finalizar todo el bytecode.
2
Se trata de una traduccin incorrecta del ingls linking que significa literalmente enlazar. Como el de librera, el tr-
mino est muy extendido en el campo informtico.
3
En la actualidad se estn desarrollando CPUs que usan el bytecode como lenguaje nativo.
28 Introduccin a la programacin con orientacin a objetos
Desde el punto de vista de un lenguaje de programacin conocer las reglas sintcticas del lengua-
je implica conocer cmo se usan las sentencias, declaraciones y los otros constructores del lenguaje.
La semntica de un lenguaje de programacin representa el significado de los distintos constructores
sintcticos (Pratt y Zelkowitz, 1996). Las reglas de sintaxis de un lenguaje de programacin dictan la
forma de un programa. Durante la compilacin de un programa, se comprueban todas las reglas de sin-
taxis. La semntica dicta el significado de las sentencias del programa. La semntica define qu suce-
der cuando una sentencia se ejecute. Dicho de otra forma, saber qu se puede decir en un lenguaje
hace referencia a la componente semntica. Por otro lado, saber cmo hay que decir en un lenguaje lo
que queremos se refiere a la componente sintctica. En cierto sentido la sintaxis es el continente y la
semntica el contenido. Hay que tener muy en cuenta que un programa que sea sintcticamente correc-
to no tiene por qu serlo semnticamente. Un programa siempre har lo que le digamos que haga y no
lo que queramos decirle que hiciera.
Un conjunto finito, ordenado de reglas o instrucciones bien definidas, tal que siguindolas
paso a paso se obtiene la solucin a un problema dado.
Tengamos claro que un programa implica usar uno o varios algoritmos. En cualquier caso, usan-
do un diagrama de bloques, la estructura genrica de un programa sera la que se muestra en la Figu-
ra 2.3.
Todo programa acepta informacin de entrada (los datos) y la procesa por medio de un/os algorit-
mo/s. El resultado constituye la informacin de salida que es la que vamos buscando. Por lo que res-
pecta a la parte de procesamiento, sta debe considerarse en el contexto de los conceptos de eficacia y
eficiencia. Estos dos conceptos son importantes y deben ser claramente distinguidos por el programa-
dor. Eficacia se refiere a la consecucin de los objetivos deseados, es decir, que el programa funcione.
Eficiencia se refiere a la consecucin de los objetivos con un adecuado consumo de recursos (tiempo,
memoria central o de disco), es decir, que el programa funcione bien. En el terreno de la programacin,
el objetivo no es slo que el programa funcione, sino que funcione y adems consuma pocos recursos.
Un programa no es bueno slo por funcionar, eso es lo mnimo exigible. Se parte de que un programa
debe funcionar. Un programa es bueno o no en funcin del consumo de recursos que haga.
Por lo que respecta a un programa cualquiera, con sus componentes de entrada-procesamiento-
salida, podemos organizarlo como un solo bloque monoltico de cdigo o modularizarlo (subdividir-
lo) en varios bloques ms pequeos. Esta modularizacin se realizar, en nuestro contexto, desde un
punto de vista funcional, como veremos en el Captulo 5. Dicho de otra forma, nos vamos a centrar
en las tareas o funciones que el programa debe desarrollar, vase la Figura 2.4.
En un programa monoltico slo tendramos un bloque, un solo programa principal (muchas veces
denominado en ingls como main). Un programa modular se subdivide en funcin de las tareas a rea-
lizar. Cada uno de los bloques funcionales sera una unidad, una especie de programa principal que se
comunica con los bloques que necesite para tomar o enviar informacin. En la Figura 2.4 las flechas
indican el sentido de movimiento de la informacin. El programa principal es siempre el bloque que
comienza a ejecutarse al arrancar el programa. En un programa monoltico no habra ningn otro blo-
que de cdigo, y en un programa modular existira un modulo principal, a partir del cual se iran lla-
4
El concepto de mtodo se presentar en detalle en el Captulo 5. Los conceptos interrelacionados de clase y objeto se
introducirn formalmente en el Captulo 5 y se considerarn en el Captulo 7.
Elementos de programacin y lenguajes 31
al, vase la Figura 2.6. Cuando la ejecucin es no lineal debe existir algn mecanismo (a fin de cuen-
tas una condicin que se cumpla o no) que permita que se realice o no la bifurcacin. Las bifurcacio-
nes no tienen por qu seguir el orden secuencial, es decir, puede ramificarse de arriba abajo o de abajo
arriba produciendo ciclos o bucles. Estos conceptos se expondrn con ms detalle en los Captulos 3
y 4.
a) Errores de compilacin. Este tipo de errores son detectados por el compilador. Son errores de
compilacin los errores de sintaxis o el uso en el programa de tipos de datos incompatibles,
tal como pretender almacenar un valor real en una variable entera.
b) Errores en tiempo de ejecucin. Aunque el programa compile bien puede dar error al ejecu-
tarlo, por ejemplo por intentar dividir por cero. En este caso el programa puede que estuviera
bien escrito, pero al adquirir la variable que realiza el papel de divisor el valor cero, y tratar
de realizar la divisin, es cuando se produce el error y ocasiona que el programa se pare. Los
programas tienen que ser robustos, es decir, que prevengan tantos errores de ejecucin como
sea posible. En Java muchos errores de ejecucin son excepciones que pueden ser captu-
radas y manejadas. Las excepciones se explicarn en el Captulo 9.
c) Errores lgicos. Se producen cuando el programa compila bien y se ejecuta bien pero el resulta-
do es incorrecto. Esto ocurre porque el programa no est haciendo lo que debera hacer (le hemos
dicho en realidad que haga otra cosa). Los errores lgicos son los ms difciles de descubrir.
5
El trmino ingls bug puede traducirse por bicho. El porqu se denomina debugging eliminacin de bichos a la
depuracin de errores de un programa es cuanto menos curiosa. En los primeros tiempos de los computadores, cuando la escri-
tura de un programa consista en cablear (soldar) una serie de cables en los ordenadores, un programa se empeaba en fallar
sistemticamente. Tras tratar de encontrar el error del programa se descubri que un insecto estaba achicharrado entre un
par de conectores en la mquina, cortocircuitando el sistema. A partir de ese momento se empez a utilizar el trmino debug-
ging para representar (con cierta sorna) el proceso de depuracin de errores de un programa.
32 Introduccin a la programacin con orientacin a objetos
grama localizaremos el punto, la sentencia, en el que se produce el fallo. A continuacin, se debe ana-
lizar la sentencia a fin de identificar la causa del error. Es muy til para esto consultar el valor de las
variables involucradas. Una vez identificado el problema el siguiente paso es su correccin, pasando
a continuacin al siguiente error hasta que no haya ninguno ms.
2.7. ALGORITMOS
Anteriormente hemos indicado que un programa funciona aplicando un o una serie de algoritmos. Esto es
importante, para que un ordenador pueda llevar a cabo una tarea determinada (resolver un problema) debe
indicrsele de forma precisa cmo realizarla. sta es la misin de los algoritmos. Lo primero es adquirir
una nocin clara de qu es un algoritmo. Recordando la definicin dada previamente, tenemos que:
Un algoritmo es un conjunto finito, ordenado de reglas o instrucciones bien definidas tal que
siguindolas paso a paso se obtiene la respuesta a un problema dado.
Esta definicin nos indica que no todos los mtodos de solucin de un problema son susceptibles
de ser utilizados por un computador. Como el ordenador slo interpreta instrucciones, el procedi-
miento debe:
a) Estar compuesto de acciones bien definidas.
b) Constar de una secuencia finita de operaciones.
c) Acabar en un tiempo finito.
Estos tres puntos indican que el algoritmo debe estar definido en trminos que el ordenador pue-
da entender, es decir, en funcin de acciones que se puedan realizar con el ordenador. Tambin, un
algoritmo para ser til debe tener un nmero finito de operaciones. De nada servira un algoritmo que
para resolver un problema necesitara realizar un nmero infinito (o tremendamente grande) de opera-
ciones. Por la misma razn, el algoritmo debe poder ejecutarse en un tiempo finito y no que la secuen-
cia de acciones implique una cantidad de tiempo que aunque no sea infinita sea muy larga. La idea
bsica es que un algoritmo debe ser una receta prctica para resolver un problema.
Para resolver un mismo problema se pueden utilizar muchos algoritmos. Nos basta con usar un
algoritmo cualquiera o hay algn criterio para escoger un algoritmo entre varios? Lgicamente, siem-
pre se debe intentar seleccionar el mejor algoritmo. El punto clave es cmo definir el mejor algo-
ritmo. El problema se soluciona a partir de los, previamente mencionados, conceptos de eficacia y
eficiencia. Todo algoritmo debe ser eficaz (debe resolver el problema), pero no todos son igualmente
Elementos de programacin y lenguajes 33
eficientes (no usan los mismos recursos para resolver el problema). La eficiencia de un algoritmo est
siempre referida al problema concreto que resuelve el algoritmo. Se puede definir algn ndice de efi-
ciencia que nos sirva para comparar distintos algoritmos para el mismo problema. Cmo se determi-
na la eficiencia de un algoritmo? ste es el problema del anlisis de algoritmos.
Los algoritmos se pueden evaluar segn diversos criterios. Frecuentemente estamos interesados en
la velocidad de crecimiento del tiempo o el espacio requeridos para resolver mayores y mayores casos
de un problema (tiempo de cmputo y espacio de almacenamiento). Para ello se asocia un entero con
el problema que se denomina tamao del mismo. El tamao es una medida de la cantidad de datos de
entrada en el algoritmo. Este tamao puede ser el nmero de elementos a manejar. Por ejemplo, en la
multiplicacin de matrices el tamao podra ser la dimensin de la mayor matriz que se pueda tratar.
En un algoritmo de ordenacin podra ser el mayor nmero de elementos que se puedan ordenar.
El tiempo empleado por un algoritmo como funcin del tamao del problema se denomina com-
plejidad temporal del algoritmo. El comportamiento lmite de la complejidad con el aumento del
tamao se denomina complejidad temporal asinttica del algoritmo. Similares parmetros pueden deri-
varse con respecto al espacio (memoria): complejidad espacial y complejidad espacial asinttica.
La complejidad asinttica es la que determina en la prctica el tamao, n, de los problemas que
puede tratar el algoritmo. El tiempo de trabajo del algoritmo se puede considerar como una funcin
del tamao, tiempo5f(n). Dentro de esa funcin se considera como complejidad del algoritmo, u orden
del algoritmo, el trmino que crece ms rpido con n de todos los que tenga la expresin. Sera el tr-
mino que determina el resultado en el lmite de n . Si un algoritmo procesa una entrada de tamao n
en un tiempo cn2, para una constante dada c, decimos que la complejidad temporal del algoritmo es
O(n 2) (orden n 2). La notacin O es un tipo de notacin asinttica y se usa mucho (Rawlins, 1992).
Por ejemplo, supongamos cinco algoritmos con distinta complejidad temporal, vase la Tabla 2.1.
Analicemos cuntos elementos se pueden procesar en 1 segundo y en 1 hora dependiendo de las com-
plejidades del algoritmo dadas en Tabla 2.1. Suponiendo que un elemento se procesa en 1 ms, el pri-
mer algoritmo podra procesar en un segundo un tamao de n51000 (n*1ms51s). En los otros casos
tenemos los datos que se presentan en la Tabla 2.1. Observemos la tremenda diferencia entre el pri-
mer caso (complejidad lineal con n) y el ltimo (complejidad exponencial con n). El nmero de ele-
mentos que se pueden procesar es muchsimo mayor en el primer caso. Para los algoritmos listados
en la Tabla 2.1, el primero es el ms eficiente de todos.
6
Otro problema en el campo de la ingeniera del software es cmo definir el concepto de calidad en un producto soft-
ware. Slo con una definicin precisa y, a poder ser, en trminos de magnitudes cuantificables, podramos controlar el nivel
de calidad de un producto software.
Elementos de programacin y lenguajes 35
contexto de un ciclo de vida que consta de una serie de etapas o fases. Desde un punto de vista gene-
ral, todo software pasa por tres etapas fundamentales: Desarrollo, Uso y Mantenimiento, vase la Figu-
ra 2.7. Consideremos estas tres etapas por separado.
a) Desarrollo. Inicialmente la idea para un programa es concebida por un equipo de desarrollo 7
de software o por un usuario con una necesidad particular. Este programa nuevo se construye
en la denominada etapa de desarrollo. En esta etapa tenemos varios pasos a realizar: anlisis,
diseo, implementacin y pruebas. Al final, generamos un programa operativo.
b) Uso. Una vez desarrollado el programa y despus de considerar que est completo, que es
operativo, se pasa a los usuarios. La versin del programa que van a utilizar los usuarios se
denomina release o versin del programa.
c) Mantenimiento. Durante el uso, utilizamos el programa en su entorno normal de trabajo y
como consecuencia de la aparicin de errores, del cambio de algn elemento de entorno (soft-
ware o hardware) o de la necesidad de mejorar el programa, ste se modifica. Esto es el man-
tenimiento. Casi siempre los usuarios descubren problemas en el programa. Adems, hacen
sugerencias de mejoras en el programa o de introduccin de nuevas caractersticas. Estos
defectos y nuevas ideas los recibe el equipo de desarrollo y el programa entra en fase de man-
tenimiento. Estas dos ltimas etapas, uso y mantenimiento, se entremezclan y, de hecho, slo
se habla de etapa de mantenimiento desde el punto de vista de la ingeniera del software.
La anterior clasificacin es muy general. En particular, la etapa de desarrollo consta de una serie de
actividades o etapas bien definidas. Para cualquier herramienta software las etapas de su ciclo de vida
son las mismas y se recogen en la Figura 2.8. Vamos a considerar cada una de estas etapas por separa-
do.
7
Cuando nos refiramos a un equipo de desarrollo no debe entenderse necesariamente como un grupo de personas, depen-
diendo de cada caso puede tratarse de una sola.
36 Introduccin a la programacin con orientacin a objetos
organizados en la etapa anterior. Es importante obtener un buen diseo, ya que muchos de los
problemas en el software se pueden atribuir a un mal diseo inicial. Para ello se deben explo-
rar distintas alternativas. A menudo, el primer intento de diseo no es el mejor. En esta etapa,
una de las partes ms importantes es la definicin del algoritmo y de las estructuras de datos
a usar en el programa. En la aproximacin orientada a objetos, el diseo incluye la determi-
nacin de las clases, sus propiedades y mtodos, los objetos y la relacin entre ellos. El diseo
del programa se debe revisar varias veces. Despus de que el diseo se ha desarrollado y refi-
nado se pasa a la fase de implementacin.
c) Implementacin o codificacin. Una vez realizado el diseo hay que implementarlo, generan-
do el cdigo fuente. Se trata de traducir el diseo a un lenguaje de programacin determinado.
Muchos programadores se centran slo en la implementacin cuando realmente sta es la eta-
pa menos creativa del proceso de desarrollo de software. Tanto es as que existen generadores
de cdigo, programas capaces de escribir el cdigo fuente en un lenguaje determinado a partir,
claro est, de una indicacin de diseo. La implementacin se centra en los detalles de la codi-
ficacin, como el uso de un estilo adecuado o el desarrollo de la correspondiente documenta-
cin. Una vez concluida la implementacin, el programa debe probarse de manera sistemtica.
d) Pruebas. Lo primero de todo es tener claro que el objetivo de las pruebas es descubrir erro-
res, no demostrar que el programa funciona. Las pruebas deben disearse con la intencin de
encontrar errores. De esta forma, una prueba con xito es la que descubre un error. Para rea-
lizar las pruebas y localizar errores se ir viendo como responde el programa, o los mdulos
o componentes del programa, a distintas entradas de informacin. Encontrando y fijando erro-
res se incrementa la precisin y fiabilidad del programa. Las pruebas deben disearse de tal
forma que cubran todas las posibilidades lgicas dentro del programa, para asegurarse de que
cumple el propsito deseado y que satisface todos los requisitos. Una indicacin general es
que los casos de prueba deben acabar cubriendo todos los arcos del grafo de flujo de control
del programa 8. Esto se consigue evaluando como verdaderas y como falsas todas las condi-
ciones (explcitas o implcitas en bucles) que aparecen en el cdigo.
e) Mantenimiento. Es la etapa que va desde la obtencin de una herramienta operativa, al final
de la etapa de desarrollo, hasta la retirada del programa por obsolescencia. El mantenimien-
to de software es el proceso de modificar un programa para mejorarlo o corregir problemas.
Los cambios se hacen sobre una copia del programa, as el usuario puede utilizar la versin
original mientras el programa est siendo mantenido. Cuando es necesario realizar una modi-
ficacin importante del programa, el resultado es una variante importante del mismo, una
nueva versin del programa. Cuando se considera que ya no es til seguir realizando el man-
tenimiento de un programa sino desarrollar uno nuevo, el programa est obsoleto y se retira
de su uso activo. En este momento concluye el ciclo de vida (desarrollo, uso, mantenimien-
to y finalmente retirada). Su duracin vara segn cada programa. Sin embargo, s podemos
indicar de forma general que la etapa de mantenimiento es la ms larga, la que ms esfuerzo
implica en el ciclo de vida. El esfuerzo de mantenimiento representa alrededor del 80% del
esfuerzo total para todo el ciclo de vida. En la Figura 2.9 vemos la distribucin tpica del
esfuerzo total asociado al ciclo de vida en esfuerzo de desarrollo y esfuerzo de manteni-
miento.
El equipo de mantenimiento no suele, ni tiene por qu, ser el mismo que el equipo de desarrollo
del programa. El xito del mantenimiento depende de la habilidad de la/s persona/s encargada/s de l
para entender el programa, determinar cul es el problema y corregirlo. Por tanto, todo lo que ayude
a realizar un mantenimiento ms fcil es tremendamente til. Esto indica que la generacin de docu-
mentacin adecuada en la etapa de desarrollo es de gran importancia, as como la aplicacin de tc-
8
Esto se relaciona directamente con los estudios de complejidad ciclomtica del grafo de flujo de control realizados por
McCabe (McCabe, 1976).
Elementos de programacin y lenguajes 37
nicas estandarizadas. La capacidad para leer y entender un programa depende de lo bien que est ana-
lizado, diseado y documentado, es decir, del esfuerzo implicado en la etapa de desarrollo. Cuando
los requisitos no estn claros, o adecuadamente organizados, o no estn documentados, o incluso
cuando el diseo es pobre porque el programa se entiende obtenemos una fuente de problemas.
Conseguiremos un software innecesariamente complejo y difcil de entender. Tendremos muchas
complicaciones para corregir errores o introducir modificaciones posteriormente. Cuanto ms com-
plejo es un programa ms fcil es introducir errores durante el desarrollo, y ms difcil eliminarlos
cuando se encuentren. Cuanto ms pronto se encuentren los errores, ms fcil y menos costoso ser
corregirlos.
Pretender crear un programa sin una planificacin cuidadosa es como pretender construir una casa
sin disearla. La casa se caer una y otra vez mientras vamos corrigiendo problemas, hasta que acer-
temos con el diseo. Incluso pequeos cambios en la etapa de desarrollo se reflejan en grandes cam-
bios en la etapa de mantenimiento. Es mejor emplear ms tiempo y ms cuidado en la etapa de
desarrollo, porque ello ahorra tiempo de mantenimiento y reduce el esfuerzo de todo el ciclo de vida.
El lector interesado en un estudio detallado del proceso de ingeniera del software puede consultar el
texto de Pressman (Pressman, 2002).
EJERCICIOS PROPUESTOS
Ejercicio 1.* Segn el nivel de abstraccin, cite cuatro tipos de lenguaje de pro-
gramacin (desde el nivel ms alto al ms bajo).
Ejercicio 6.* Cules son las etapas del ciclo de vida del software?
38 Introduccin a la programacin con orientacin a objetos
Ejercicio 7.* Cul es la etapa del ciclo de vida que ms esfuerzo implica?
Ejercicio 11.* En qu etapa del ciclo de vida del software se escogen las estruc-
turas de datos ms apropiadas para el problema a resolver?
REFERENCIAS
ARNOW, D. y WEISS, G.: Introduction to Programming using Java. An Object Oriented Approach, Addison-Wes-
ley, 1998.
GIBBS, W. W., Softwares Chronic Crisis, Scientific American, 72-81, September 1994.
KAMIN, S. N.; MICKNUNAS, M. D. y REINGOLD, E. M., An Introduction to Computers Science Using Java, WCB
McGraw-Hill, 1998.
MCCABE, T. J.: A Complexity measure, IEEE T. Software Eng., 308-320, SE-2 (4), December 1976.
PRATT, T. W. y ZELKOWITZ, M. V.: Programming Languages. Design and Implementation, 3td Edition, Prentice
Hall, 1996.
PRESSMAN, R. S.: Ingeniera del Software, McGraw-Hill, 5. Edicin, 2002.
RAWLINS, G. J. E., Compared to what? An introduction to the analysis of algorithms, Computer Science Press,
1992.
YOURDON, E.: Modern Structured Analysis, Prentice-Hall, 1989.
3
Introduccin a la programacin
Sumario
3.1. INTRODUCCIN
Todo lenguaje contiene constructores especficos (palabras reservadas, elementos) que permiten reali-
zar las operaciones bsicas en dicho lenguaje. En este captulo se van a presentar los constructores
bsicos de la programacin imperativa, como son los tipos de datos, las variables y los operadores.
Tambin presentaremos las instrucciones bsicas y las usaremos para mostrar cmo se trabaja con un
lenguaje de programacin. Consideraremos adems, los operadores que permiten realizar operaciones
distintas de la asignacin. En este captulo expondremos todos los conceptos desde un punto de vista
genrico. El lector debe entender que los diferentes conceptos expuestos son generales y no particula-
res de un lenguaje determinado. Por tanto, el corazn del captulo son consideraciones semnticas.
Lgicamente, los conceptos genricos se llevarn a la prctica en un lenguaje determinado, en este
caso Java. Dicho de otra manera, una vez expuestas las consideraciones semnticas, las consideracio-
nes sintcticas se expondrn en dicho lenguaje. Al concluir el tema el lector estar en condiciones de
escribir programas en Java simples, pero completos y operativos.
Para construir un programa es necesario conocer cmo construir dichas sentencias en un lenguaje
determinado. A su vez, las sentencias se pueden considerar constituidas por tres elementos:
a) Datos.
b) Instrucciones.
c) Operadores.
A lo largo del tema vamos a ir considerando en detalle cada uno de estos elementos.
jes que aplican una gestin estricta de tipos de denominan de tipos fuertes o de tipos estrictos. Java es
un lenguaje de tipos estrictos.
De forma ms rigurosa, un tipo de dato queda definido como un conjunto de valores junto con un
conjunto de operaciones para crearlos y manipularlos. Cada valor almacenado en memoria se asocia
con algn tipo de dato en particular. Los tipos de datos simples, predefinidos, que encontramos en un
lenguaje se denominan tipos de datos primitivos. Las caractersticas de estos tipos de datos primitivos
se corresponden con caractersticas disponibles del hardware del computador. Dicho de otra forma, su
representacin y manipulacin se relaciona directamente con las caractersticas fsicas del sistema (una
serie de bits con un significado concreto). Cuando las caractersticas atribuidas al dato se simulan por
software se habla de estructuras de datos (por ejemplo, un nuevo tipo de datos constituido por una
combinacin de datos simples).
a) 4 tipos de enteros.
b) 2 tipos de reales.
c) 1 tipo carcter.
d) 1 tipo lgico.
Cada tipo tiene su nombre especfico. Java distingue entre maysculas y minsculas 1 as que es
necesario respetar con cuidado las palabras reservadas que identifican cada tipo primitivo, ocurriendo
lo mismo con los nombres de las variables 2. Por lo que respecta a los tipos, cada uno presenta un con-
junto de literales. Un literal representa uno de los posibles valores del tipo considerado, como 3 5
para un tipo entero. Vamos a considerar los diferentes tipos primitivos en Java.
a) Tipos enteros
Existen cuatro tipos de datos primitivos para los enteros, que difieren por la cantidad de memoria que
requieren para ser almacenados. Cada tipo tiene un intervalo de valores que son los que se pueden
representar con l. Ms adelante veremos cmo se realiza la representacin de valores. El nombre del
tipo, la cantidad de bits usados para representarlo, y el valor mximo y mnimo que se puede repre-
sentar se recogen en la Tabla 3.1.
b) Tipos reales
Tenemos dos tipos de reales que sirven para representar (simular) nmeros reales (nmeros con parte
decimal). Estos tipos se denominan tambin de punto flotante, haciendo referencia al punto que usan
los anglosajones para indicar la parte decimal. Los tipos reales en Java y sus caractersticas se recogen
en la Tabla 3.2. Con float tenemos 7 dgitos significativos y con double 15 dgitos significativos.
Estos dos tipos de reales son tpicos en los lenguajes de programacin. El primer tipo, float en
Java, se denomina a veces real de precisin simple y el segundo, double en Java, se denomina real
en doble precisin. Debemos conocer cul es el intervalo de variacin y el orden de magnitud de nues-
tros valores para usar un tipo adecuado. Por lo que respecta a los tipos reales, y dada la cantidad de
memoria en los sistemas actuales y el funcionamiento de los mtodos matemticos, en Java es reco-
mendable usar el tipo double.
1
Muchas caractersticas del lenguaje Java se entienden considerando que es un descendiente del lenguaje C. La distin-
cin entre maysculas y minsculas es una de ellas.
2
Existen identificadores que son propios del lenguaje como class o main. El programador no puede usar estos iden-
tificadores como nombres de variables. Estos identificadores se denominan palabras reservadas, tienen significado y utilidad
especfica y no pueden usarse para otra cosa.
Introduccin a la programacin 43
c) Tipo carcter
El tipo carcter almacena un smbolo alfanumrico (uno slo) y se identifica como char. Un valor
char almacena un carcter simple del conjunto de caracteres Unicode. Un conjunto de caracteres es una
lista ordenada de caracteres. El conjunto de caracteres Unicode utiliza 16 bits para representar cada
carcter, pudiendo representar, por tanto, 216 (65536) caracteres diferentes. Unicode es un conjunto
internacional de caracteres y contiene los caracteres y smbolos provenientes de muchas lenguas del
mundo, como los caracteres latinos, rabes, chinos o cherokees, entre otros muchos (Unicode, 2002).
Otro conjunto de caracteres muy extendido es el ASCII. El ASCII original usaba 7 bits por carc-
ter y, por tanto, soportaba 27 (128) caracteres. Esto era suficiente para el conjunto de caracteres usa-
dos en ingls. Para reflejar otros alfabetos, el conjunto de caracteres ASCII se extendi para usar 8 bits
por carcter y el nmero de caracteres a representar se dobl a 256 (28). Sin embargo, se queda corto
para representar caracteres provenientes de los distintos idiomas que hay en el mundo por lo que el
equipo de desarrollo de Java escogi el conjunto de caracteres Unicode. Unicode est definido de for-
ma que el ASCII sea un subconjunto suyo.
En los conjuntos de caracteres encontramos:
Letras maysculas: A, B, C, ...
Letras minsculas: a, b, c, ...
Signos de puntuacin: (.) punto, (;) punto y coma, ...
Dgitos: 0, 1, 2, ...
Smbolos especiales: #, &, |, \, ...
Caracteres de control: retorno, tab, ...
En particular, los caracteres estn en orden alfabtico, con el nmero de cdigo aumentado en el
propio orden alfabtico. As se puede luego ordenar alfabticamente usando algoritmos apropiados.
El blanco tambin es un carcter y se usa memoria para representarlo, como para cualquier otro
carcter. Los caracteres de control tienen un significado especial para el ordenador, pero no para el
usuario. Ejemplo de estos caracteres es el que indica una tabulacin en un texto o el que le indica al
sistema que debe saltar a una lnea nueva. Dichos caracteres no se visualizan, por lo que a veces se
denominan caracteres no imprimibles o caracteres invisibles. En cualquier caso, tienen asignado un
cdigo especfico que los representa, y son tan vlidos como cualquier otro carcter.
El tipo carcter almacena un solo carcter. Un literal de tipo carcter se denota con comillas sim-
ples como, A, B. Cuando se trata de varios caracteres literales formando una frase, una palabra, en
programacin se habla de cadenas de caracteres. En algunos lenguajes existe un tipo primitivo cade-
na. En Java no es as, no existe el tipo primitivo cadena. Para representar las cadenas alfanumricas
existe una clase 3 denominada String (cadena en ingls). Como veremos ms adelante, una cadena
de caracteres se delimita con dobles comillas como en la siguiente palabra, Ejemplo. Es necesario
recordar este punto: con comillas simples se delimitan caracteres, con comillas dobles cadenas.
d) Tipo lgico
Este tipo primitivo se usa para representar los dos posibles valores lgicos: verdadero (true) o falso
(false) 4. El nombre del tipo en Java es boolean y los dos nicos posibles valores son true o fal-
se. Estrictamente hablando slo se necesitara un bit para almacenarlo (con los dos valores 0 y 1 se
pueden representar las dos posibilidades de true y false).
3
El concepto de clase es el elemento central en orientacin a objetos. Una clase es mucho ms general que un tipo pri-
mitivo. Como ya veremos ms adelante, una clase puede contener uno o ms datos de tipo primitivo y contener tambin pro-
cedimientos para manipular dichos datos.
4
Las palabras reservadas true y false (verdadero y falso en ingls) se usan generalmente en los lenguajes de progra-
macin para representar los dos posibles valores lgicos.
44 Introduccin a la programacin con orientacin a objetos
a) Variables
Una variable es el nombre que asignamos para una posicin (posiciones) de memoria usada para alma-
cenar un valor de cierto tipo de datos. Las variables deben declararse (definirse) antes de usarse. Cuan-
do se declara una variable estamos reservando una porcin de memoria principal para almacenar
valores correspondientes al tipo de la variable. La declaracin de las variables implica el dotarlas de
un nombre denominado identificador de la variable. El valor que almacena una variable se puede
modificar a lo largo del programa.
La sintaxis de la declaracin de una variable en Java es:
tipo_de_dato nombre_de_la_variable;
int total;
Aqu declaramos una variable denominada total que puede almacenar valores enteros de tipo int.
Se pueden declarar varias variables en una misma sentencia,
En este caso total, cuenta y suma son los nombres (identificadores) de tres variables de tipo ente-
ro.
Las variables pueden inicializarse (darles un valor inicial) en la propia declaracin:
Se dice que total vale 0, cuenta 20 y precioUnitario 49.32. Es decir, estas variables estn
almacenando esos valores en su posicin de memoria correspondiente. Las variables se pueden enten-
der como contenedores de valores. La variable puede existir a lo largo de todo un programa, pero se
puede modificar el valor que almacena.
b) Constantes
En programacin una constante es una entidad similar a una variable, pero con la diferencia de que
tras su asignacin el valor que contiene no se puede cambiar en el programa. Se puede considerar
como un tipo especial de variable donde despus de asignarle un valor, dicho valor no se puede modi-
ficar. Si se intenta modificar el valor, se produce un error.
La sintaxis de la declaracin de una constante en Java es muy similar a la declaracin de una varia-
ble, pero en este caso hay que aadir al principio la palabra reservada final:
Introduccin a la programacin 45
En el ejemplo se declara una constante de tipo real (en doble precisin, double) que almacena el
nmero e, la base de los logaritmos naturales.
Respecto a las variables, es importante conocer en un lenguaje cul es la parte del programa don-
de estn definidas. Esto es, si una variable una vez declarada se puede usar en cualquier parte del pro-
grama o si su alcance est limitado a alguna zona. De una forma u otra los lenguajes estn organizados
en bloques. Dependiendo del lenguaje, estos bloques pueden estar claramente indicados con algn
smbolo o palabra reservada o pueden estar definidos implcitamente. En Java los bloques de cdigo
se indican entre llaves ({ }). Es importante sealar que en Java una variable queda definida nicamente
dentro del bloque (seccin entre una pareja de smbolos { }) en el que se ha declarado. Dicho de otra
forma, su alcance 5 es el bloque en el que se ha declarado. Si se intenta usar la variable fuera del blo-
que en el que se ha declarado, se producir un error de variable no declarada (este error indica que en
la parte del programa donde se pretende usar, la variable es inexistente). La situacin se ilustra en el
Programa 3.1 6.
1class Alcance{
2 public static void main(String [] args) {
3 int numero=100;
4 if (numero<103){
5 int dentroAmbito=3;
6 System.out.println(Dentro del bloque +dentroAmbito);
7 } // Cierra el bloque del if
8 System.out.println(Fuera del bloque solo existe numero
10 +numero);
11 } // Cierra el bloque del main
12} // Cierra la clase
En el Programa 3.1 hemos incluido el nmero de lnea como ilustracin. En Java no se numeran
las lneas, as que si deseramos probar el Programa 3.1 deberamos eliminar los nmeros de lnea. En
el programa se define una clase denominada Alcance (lnea 1) dentro de la cual aparece un bloque
de instrucciones llamado main (principal) en la lnea 2. Este bloque es un ejemplo de un mtodo en
Java. Un mtodo es un bloque de instrucciones identificado por un nombre que se puede llamar des-
de otra parte de un programa y que se maneja como una unidad. De momento y hasta que se exponga
el tema de la definicin de clases, los ejemplos constarn de una sola clase donde se define el mtodo
main. El mtodo main es el punto de arranque de todo programa en Java, es por l por donde empie-
za a ejecutarse el programa.
En el programa se declara una variable llamada numero (lnea 3) cuyo alcance es todo el mtodo
main y que se inicializa a 100. A continuacin, aparece una sentencia if (si condicional) que pregunta
si numero es menor que 103, en cuyo caso se declara una variable llamada dentroAmbito (lnea 5)
5
El concepto de alcance o mbito (en ingls scope) de un elemento es general en programacin y siempre hace referen-
cia a la zona donde un elemento en cuestin existe y puede usarse.
6
Debido a que la salida estndar por pantalla no soporta los acentos, stos no se incluirn en las sentencias
System.out.println () y System.out.print ().
46 Introduccin a la programacin con orientacin a objetos
con valor 3. Luego se imprime por la pantalla con la llamada a System.out.println () el valor de
la variable dentroAmbito, y en la lnea 7 se cierra el bloque if. Dentro del bloque if existen las varia-
bles numero y dentroAmbito. En la lnea 8, ya fuera del bloque if, se imprime la variable numero.
Aqu no se puede imprimir dentroAmbito pues no existe fuera del bloque if.
Un concepto relacionado con el de constante es el de literal. Un literal (o constante literal) es una
constante cuyo nombre (identificador) es la representacin escrita de su valor. As, el literal A es la
representacin escrita del carcter A (los caracteres literales se indican entre comillas). Los literales
pueden pertenecer a cualquier tipo de dato. As, 2 es un literal que corresponde al entero dos.
Respecto a los identificadores en Java, stos pueden construirse con letras, dgitos, el carcter de
subrayado (_) y el signo de dlar ($). Un identificador no puede comenzar con un dgito, pero puede
tener cualquier longitud. Recordemos que Java distingue entre maysculas y minsculas, con lo cual
si se declara una variable llamada Datos sera distinta de otra llamada datos. Estas reglas para la
construccin de identificadores se aplican en Java a cualquier entidad, no slo a variables.
a) Tipo entero
Los tipos enteros representan tanto valores negativos como positivos. Para representar el signo se usa
un bit en cada tipo denominado bit de signo. Si el bit de signo es 1, el nmero es negativo; si es 0, el
nmero es positivo. As, para representar el valor absoluto del nmero se usa un bit menos de los dis-
ponibles. Para los distintos tipos enteros en Java la situacin se ilustra en la Figura 3.2.
En todos ellos el primer bit es el usado para indicar el signo. Este bit, el primero, el colocado ms
a la izquierda, es el bit de signo.
En Java (y en muchos otros lenguajes) los tipos enteros estn almacenados en formato comple-
7
En castellano el trmino correcto es descodificar. Sin embargo, se encuentra muy frecuentemente el trmino decodifi-
car que es una mala traduccin del ingls.
Introduccin a la programacin 47
mento a dos con signo. El complemento de un nmero es en cierto sentido la imagen especular del
nmero original. Por ejemplo, en decimal son tpicos el complemento a nueve y a diez. Como ilustra-
cin, consideremos en decimal la obtencin del complemento a nueve de 63:
299
263
236
2: se suma 1
136
101
137
2111111
2101010
2010101
2: se suma 1
1010101
1000001
1010110 <....... complemento a dos
1: Invertir 1 y 0: 010101.
2: Sumar 1. Es el mismo caso que en el paso 2o anterior, obtenemos 010110
00011001
Para representar 225, primero se invierten todos los bits
11100110
despus se aade 1
11100111
Observe que el bit del signo se invierte, indicando que el nmero es negativo.
Usando esta tcnica, las sumas y restas quedan reducidas a sumas y se reduce la complejidad de
los circuitos de la unidad aritmtico-lgica (no hacen falta circuitos especficos para restar). El lector
interesado en una introduccin al diseo de sistemas digitales puede consultar el captulo cuarto de
(Prieto et al., 1995).
b) Reales
En Java, como ya vimos, y en muchos otros lenguajes hay dos tipos de datos reales (de punto flotan-
te) los simples y los dobles, llamados en Java float y double. La representacin tpica de un nme-
ro en punto flotante es la siguiente:
Un valor decimal (base 10) en punto flotante se define con la siguiente expresin:
signo * mantisa * 10 exponente
donde:
signo es 1 21.
mantisa es un entero positivo que representa los dgitos significativos del nmero.
exponente es un entero que indica cmo se coloca el punto decimal con respecto a la mantisa.
Se usan los tres componentes para representar un nmero cualquiera. Veamos dos ejemplos:
Los nmeros en punto flotante se pueden representar en binario de la misma manera, excepto que
la mantisa y el exponente son nmeros binarios y la base es 2 en vez de 10, es decir:
En Java se usa el estndar IEEE 754 para representar nmeros en punto flotante. En esta norma la
representacin es binaria y se usa un bit para el signo. La norma IEEE 754 establece para precisin
Tabla 3.3. Convenio establecido por la norma IEEE 754 para preci-
sin simple o doble
Overflow ocurre cuando un nmero se hace demasiado grande para entrar en su espacio asig-
nado.
Underflow ocurre cuando un nmero se hace demasiado pequeo para entrar en su espacio
asignado.
Como ejemplo ilustraremos la situacin de overflow en enteros en el apartado 3.4.2, tras introdu-
cir las sentencias de entrada/salida.
c) Caracteres
En Java, como vimos, los caracteres se representan en cdigo Unicode (Unicode, 2002), con 16 bits.
Un carcter Unicode est representado como un entero de 16 bits sin signo. Como no se usa ningn
signo, los 16 bits contribuyen a codificar cada carcter. Por eso, se pueden representar 216 (65536)
caracteres, aunque en la actualidad slo se usan aproximadamente la mitad. Por ejemplo, el carcter
z tiene el valor Unicode 122, el cual se representa con 16 bits como:
0000000001111010
Los caracteres Unicode se almacenan como un conjunto de 16 bits. La conversin directa de los
16 bits al sistema decimal da un nmero que corresponde al valor decimal del carcter Unicode repre-
sentado. Al almacenarse como nmeros, Java nos permite realizar algn procesamiento aritmtico
sobre los caracteres. Por ejemplo, como A se almacena con el valor Unicode 65, la sentencia
char car = A + 5;
d) Lgico
Es el tipo de dato usado para las condiciones (verdadero/falso). Para optimizar el tiempo de acceso a
8
Es muy habitual encontrar la palabra rango en lugar de intervalo en la literatura tcnica. Esta equivalencia es absolu-
tamente incorrecta. El error proviene de una mala traduccin del trmino ingls range que significa intervalo y no rango. En
castellano, rango refiere a la posicin dentro de un conjunto u organizacin.
50 Introduccin a la programacin con orientacin a objetos
memoria se usa ms de un bit (slo hara falta uno) para representarlo. Por ejemplo, en la Sun JVM
(Java Virtual Machine) todos los tipos enteros (el boolean se considera como tal) menores de 32 bits
se promueven a 32 bits cuando se colocan en la pila (stack) de datos durante la ejecucin de un pro-
grama (van der Linden, 1999).
De ensanchamiento o promocin.
De estrechamiento o contraccin.
Las conversiones de ensanchamiento transforman de un tipo a otro con el mismo o mayor espacio
para almacenar informacin. En este caso no se pierde informacin, pero puede perderse precisin al
convertir de tipo entero a real, ya que algunos de los dgitos menos significativos pueden perderse.
Las conversiones de estrechamiento transforman de un tipo a otro con menos espacio para alma-
cenar informacin. En una conversin de contraccin es probable perder informacin. Un ejemplo tpi-
co es el de pasar de un real a un entero. Aqu, a no ser que la parte decimal sea cero se perder en el
cambio.
Las conversiones de ensanchamiento o promocin, en Java, pueden realizarse de la forma mostra-
Origen Destino
byte short, int, long, float, double
short int, long, float, double
char int, long, float, double
int long, float, double
float float, double
double
long double
da en la Tabla 3.4.
Por otro lado, las conversiones de estrechamiento o contraccin son ms peligrosas, porque cam-
bian de un espacio de almacenamiento determinado para el tipo original a un espacio menor en el tipo
destino. Se corre el riesgo de perder o alterar informacin. Las conversiones de contraccin entre los
Introduccin a la programacin 51
Origen Destino
byte char
short byte, char
char byte, short
int byte, short, char
long byte, short, char, int
float byte, short, char, int, long
double byte, short, char, int, long, float
se convierte el valor 82 de euros a 82.0 (valor real, con decimal) cuando se almacena en dinero. El
valor en euros no se cambia, se mantiene el original. Lgicamente, a travs de la asignacin slo se
permiten conversiones de ensanchamiento.
b) Promocin aritmtica
Ocurre automticamente cuando se realiza una operacin aritmtica como la suma o la divisin.
En este caso, los operadores aritmticos modifican los tipos de sus operandos para realizar correcta-
mente la operacin. As, si dividimos una variable real por una entera, el operador promueve la ente-
ra a real para realizar la operacin. Por ejemplo, si resultado es una variable de tipo float, suma
es tambin una variable de tipo float, y contador es una variable de tipo int, la sentencia
52 Introduccin a la programacin con orientacin a objetos
convierte internamente el valor entero en contador a un float y despus hace la divisin, produ-
ciendo un resultado en punto flotante. El valor en contador no se cambia. La promocin aritmtica
es siempre de ensanchamiento.
c) Moldes (Casting 9)
En este caso, se utiliza un mecanismo especfico para realizar la transformacin. A tal efecto, los
lenguajes de programacin proporcionan alguna instruccin que realiza la conversin. ste es el mto-
do ms seguro, pues la instruccin permite realizar conversiones de promocin o contraccin mante-
niendo, en lo posible, la informacin original. El molde es la instruccin que produce la conversin de
tipo. En Java, el molde es un operador que se especifica como un nombre de tipo colocado entre parn-
tesis a la izquierda del dato a convertir.
La sintaxis para el uso de moldes es:
(Tipo) variable_a_convertir
donde tipo es el nombre del tipo al que se quiere convertir. En el siguiente ejemplo se hace una con-
versin de estrechamiento. Se trata de convertir el valor real almacenado en una variable denominada
dinero a un valor entero que se va a almacenar en una variable entera llamada euros. Lgicamente,
el valor decimal se trunca, perdindose la parte fraccionaria del valor en punto flotante.
int euros;
double dinero=30.2;
euros = (int) dinero;
El valor en dinero no cambia. La variable euros almacena ahora el valor entero 30 ya que la
conversin al tipo entero pierde la parte decimal. En Java, si una conversin es posible, puede hacer-
se a travs de un molde.
Los moldes son tiles en muchas situaciones donde temporalmente necesitamos tratar un valor
como de otro tipo. Por ejemplo, si quiero dividir el valor entero total por el valor entero contador
y conseguir un resultado de tipo float que almacenaremos en la variable resultado, podra hacer:
resultado=(float) total/contador;
Primero el operador de molde devuelve la versin float del valor en total. Esta operacin no
cambia el valor almacenado en total. Despus contador se convierte a float va promocin
aritmtica. El operador divisin hace la divisin en punto flotante y produce el valor buscado. Si el
operador de molde no se usa, se hubiera hecho la divisin entera y truncado la respuesta antes de asig-
nar el valor a resultado. Obsrvese que se ha indicado que el molde se aplica en primer lugar y que
luego se realiza la divisin. Esto es as porque el molde tiene precedencia sobre la divisin. Este com-
portamiento es un ejemplo de precedencia de operadores, problema que consideraremos ms adelan-
te.
Una conversin de tipo no altera el valor de las variables convertidas. Recordemos que una varia-
ble acta como un contenedor para el valor almacenado. No se puede sacar el valor, slo se puede
poner uno nuevo machacando, borrando el anterior, vase la Figura 3.3. Por ejemplo, consideremos
tres sentencias como las siguientes,
9
Cast en ingls se traducira en el presente contexto como molde. La idea es que nosotros cambiamos de tipo,
moldeando el tipo antiguo para adaptarlo al nuevo.
Introduccin a la programacin 53
Tras realizar el cociente, total y contador siguen siendo enteras, su tipo no se altera. Lo que ocu-
rre es que se hace una copia del valor de total y se convierte a float. El valor original sigue siendo
entero.
3.4. INSTRUCCIONES
Como vimos, un programa resuelve un problema dado usando un o una serie de algoritmos determi-
nados. El programa se escribe en un lenguaje concreto y en l se van indicando las distintas acciones
a travs de determinadas instrucciones. Dichas instrucciones son generales, aunque su sintaxis espec-
fica depende del lenguaje. Vamos a ver los distintos tipos de instrucciones y los particularizaremos en
lenguaje Java. Es decir, vamos a considerar las instrucciones bsicas que soportan todos los lenguajes
desde el punto de vista semntico.
Es importante recordar que el signo = no representa una igualdad, sino la colocacin del valor
determinado en la variable. Cuando no se desea usar un lenguaje determinado, la asignacin se sim-
boliza con una flecha i. As, en el ejemplo anterior tendramos:
total i5.0
Este simbolismo representa ms claramente lo que ocurre en realidad. El signo 5 puede causar
confusin si se considera como una igualdad. Por ejemplo, la sentencia:
total=total+1.0;
no tiene sentido si se considera como una igualdad. Para interpretarlo se debe entender que total
representa una porcin de memoria. As, la expresin anterior significa: Toma el valor almacenado en
la variable total, smale 1 y el resultado gurdalo en la variable total. Lgicamente, el valor anti-
guo de total se pierde al rellenarla con el nuevo valor.
La sintaxis general de asignacin en Java es:
Nombre_de_variable=expresin;
donde expresin puede ser un valor literal, como en los ejemplos anteriores, o bien representar una
o unas operaciones cuyo resultado es el que se almacena en la variable.
Dependiendo del lenguaje de programacin as se permite la conversin implcita de un tipo de
datos a otro. Java es un lenguaje de tipos estrictos, lo que significa que no permite que se asigne un
valor a una variable que sea inconsistente con el tipo declarado para esa variable. Por ejemplo, no se
puede asignar un valor entero a una variable lgica y viceversa. La compatibilidad entre tipos se com-
prueba en tiempo de compilacin.
Ahora que ya hemos presentado la instruccin de asignacin ilustramos la estructura de un pro-
grama en Java con el Programa 3.2. Nuevamente, por claridad, se numeran las lneas, aunque esto no
se hace en Java.
1 class Ejemplo {
4 // Declaracin de variables
8 // Operacin
9 total=total+suma;
El Programa 3.2 es un ejemplo muy sencillo, que aunque no tiene mucha utilidad (realiza una tarea
Introduccin a la programacin 55
pero no genera informacin) nos servir para analizar la estructura genrica mnima de un programa
en Java. Antes de comenzar es conveniente saber que Java usa formato libre. Esto quiere decir que el
cdigo puede empezar y terminar donde se desee. Lo normal es usar lneas de 80 caracteres, que es lo
que se puede visualizar bien en la anchura de una pantalla o en una hoja impresa. La lnea de 80 carac-
teres tiene una razn de ser. Los 80 caracteres eran el nmero de caracteres que entraban en una tar-
jeta perforable. Por eso la lnea de 80 caracteres se denomina imagen de tarjeta.
A nivel organizativo la entidad bsica en un lenguaje orientado a objetos es la clase, en Java iden-
tificada con la palabra reservada class. La lnea 1 del programa indica que estamos definiendo una
clase llamada Ejemplo. Puede observarse que la clase corresponde a un bloque de cdigo, pues des-
pus del nombre de la clase se abre una llave que no se cierra hasta la lnea 11. Una clase, como se
expondr ms adelante, se puede considerar como un tipo abstracto de datos, siendo los objetos varia-
bles de ese tipo de dato. Las clases se tratarn con detalle en los Captulos 5 y 7, aqu se explican los
conceptos bsicos para poder entender el ejemplo. Tradicionalmente, los lenguajes consideran inde-
pendientes los datos y las funciones o procesos que se realizan sobre ellos. En la programacin orien-
tada a objetos esto no es as, los objetos (que se definen a travs de una clase) contienen los datos y
tambin los procedimientos o funciones que se realizan sobre ellos (denominados mtodos en Java).
Se dice que un objeto encapsula datos y procedimientos. En Java existen clases predefinidas que pro-
veen de mtodos tiles. Podemos imaginarlo como una biblioteca de clases para distintas aplicacio-
nes. Estas bibliotecas estn organizadas como paquetes, que son conjuntos de clases relacionadas,
que deben ser importados por nuestro programa para poder usar los mtodos contenidos en sus cla-
ses. En Java, un conjunto importante de clases predefinidas son las que forman la API (Applications
Programming Interface). Estas clases estn organizadas en paquetes y poseen mtodos para la entrada
y salida de informacin.
De lo anterior se sigue que dentro de una clase se pueden definir mtodos. Los mtodos son frag-
mentos de cdigo que realizan una tarea y que pueden devolver un resultado. Si un mtodo no devuel-
ve ningn resultado, debe indicarse con la palabra reservada del lenguaje void. Que no devuelva un
resultado no quiere decir que no haga nada. Las instrucciones de entrada/salida estaran dentro del
mtodo, pero no sera necesario devolver nada a quien haya llamado (invocado) el mtodo en cues-
tin. En todo programa hay un mtodo, main, que representa el programa principal. En el Progra-
ma 3.1 el mtodo main comienza en la lnea 3. El mtodo main es el punto de partida del programa,
no se invoca desde ninguna parte del programa, sino que es el sistema operativo quien comienza a eje-
cutarlo. Como el mtodo main no tiene que devolver ningn valor, lleva la indicacin de void. Los
modificadores de public y static hacen referencia a ciertas facetas del comportamiento del mto-
do que no consideraremos en este momento. Este punto se tratar en detalle en el Captulo 7. A la dere-
cha del nombre del mtodo y entre parntesis se pueden indicar los datos que se pasan al mtodo para
trabajar con ellos. En el mtodo main siempre hay que indicar que es posible pasar una lista de cade-
nas, lo que se indica como String []. Al lado se indica el nombre que se desea para la lista. En el
programa se usa el identificador args como abreviatura de argumentos. De momento, baste conside-
rar que la lnea 3 del Programa 3.1 es la cabecera que usaremos siempre que escribamos el mtodo
main. Al final de la lnea 3 podemos observar que se abre el bloque correspondiente al mtodo. El blo-
que se cierra en la lnea 10.
Los bloques de cdigo se suelen sangrar para delimitar visualmente su alcance. sta es una nor-
ma de estilo a la que el lector debe habituarse desde el principio, puesto que le ayudar a estructurar
sus programas, depurarlos y sobre todo ayudar a la legibilidad de los mismos (vase el Apn-
dice D).
Dentro ya del mtodo main (al igual que en la lnea 2 del programa) encontramos una lnea de
comentario. Los comentarios son fundamentales en un programa, puesto que representan informacin
muy til para entender su funcionamiento. Los comentarios deben incluirse en el cdigo, pues con el
tiempo ser necesario modificar o adaptar el programa. Estas labores de modificacin o adaptacin del
software se denominan mantenimiento del software, y como vimos en el Captulo 2, tienen una gran
importancia en el ciclo de vida de un programa. Los comentarios nos indican la intencin del autor del
56 Introduccin a la programacin con orientacin a objetos
programa cuando lo escribi. Cuando se usa un programa durante muchos aos, y se le van haciendo
modificaciones, es buena poltica documentarlo de manera apropiada, no slo a travs de los manua-
les tcnicos y de usuario sino tambin a travs de texto dentro del propio cdigo. El computador igno-
ra los comentarios. stos no afectan a la ejecucin de los programas. Los comentarios en un programa
se denominan documentacin interna y se incluyen para explicar el propsito del programa o de los
pasos de procesamiento.
Bsicamente existen dos formas de escribir un comentario en Java, una es usando dos barras //,
y otra usando una barra y un asterisco /* al principio del comentario y un asterisco y una barra, */
al final del mismo.
La diferencia es que el primer tipo slo sirve para escribir una lnea. Cuando se necesita escribir
comentarios ms largos, de varias lneas, se utiliza el segundo tipo que delimita perfectamente dnde
comienza y dnde termina el comentario considerado.
En la lnea 5 del Programa 3.2, que estamos analizando, se puede observar una declaracin de
variables. Cuando se declara una variable se le asigna un valor por defecto. Este valor depende del sis-
tema, y normalmente es cero o el valor nulo. La asignacin de valores se realiza en las lneas 6 y 7 del
programa. La lnea 9 modifica el valor de la variable total. La lnea 10 cierra el bloque delimitado
por el mtodo main. La llave de la lnea 11 indica el fin de la clase.
lenguaje como tal) no hay instrucciones de lectura/escritura. Lo que hay son mtodos para ello provis-
tos por las clases de la API de Java. Vamos en este apartado a explicar algunas nociones de la entrada
y salida en Java para poder empezar a hacer ejemplos donde se lean datos y se impriman resultados.
En Java todos los tipos de entradas y salidas se realizan por streams (corrientes o flujos) y se
habla de corrientes de entrada o de salida. Un stream es un flujo de datos, independientemente de
donde venga o vaya. Una definicin ms precisa es la siguiente: Un stream es una secuencia orde-
nada de datos de longitud indeterminada (Harold, 1999). Es una abstraccin de fuentes y destinos
externos de datos que permiten leer y escribir de ellos independientemente del tipo exacto de fuente
o destino, vase la Figura 3.4. Este mecanismo de abstraccin permite que la entrada y la salida se
10
El acrnimo io proviene del trmino ingls Input-Output (Entrada-Salida). No es raro verlo usado en castellano.
11
Como se coment con anterioridad, para usar un paquete de clases hay que realizar una operacin de importacin. El
paquete java.lang contiene clases con los mtodos para realizar las tareas ms comunes. Por eso, se importa automti-
camente en todos los programas, no hay que dar ninguna instruccin especial para ello. El resultado es transparente para el
usuario, para el cual los mtodos del java.lang parecen formar parte del lenguaje.
58 Introduccin a la programacin con orientacin a objetos
Sin embargo, esto no result til para convertir entre cdigos de caracteres externos y el Unicode
que usa Java internamente y que necesita 16 bits. Por eso, a partir de la versin 1.1 de Java se intro-
ducen las corrientes de 16 bits que son compatibles con Unicode. A tal efecto, se introducen dos enti-
dades denominadas:
Java proporciona mecanismos para adaptar las entradas de 8 bits a las de 16 (a efectos de compa-
tibilidad con cdigo desarrollado para streams de 8 bits). Estas corrientes de 16 bits tambin se deno-
minan corrientes de caracteres (por aquello de usar el cdigo Unicode). La ventaja de usar corrientes
de 16 bits es que es ms fcil escribir programas que no sean dependientes de una codificacin espec-
fica de caracteres.
No hay sentencias de entrada y salida en el lenguaje Java. La entrada y salida se realiza usando
bibliotecas de clases predefinidas. La mayora de las operaciones de entrada-salida estn definidas
en el paquete java.io de la API de Java 10. Sin embargo, los mtodos print y println que son
los ms comunes para salida, son parte de la clase System del paquete java.lang. Este paquete
se importa automticamente en todos los programas en Java. No es necesario importarlo explcita-
mente para usarlo 11. Las corrientes de lectura y escritura estndar se muestran en la Tabla 3.6 don-
de in, out y err son objetos de la clase System. Los streams System.in, System.out y
System.err son de 8 bits.
La salida estndar, a nivel bsico, es muy sencilla. Basta con usar los mtodos print() y
println() del stream System.out. La sintaxis completa es,
System.out.print();
System.out.println();
El primer mtodo produce una salida sin salto a la lnea siguiente y el segundo produce un salto a
la lnea siguiente tras escribir la salida. La diferencia del uso de print y println se ilustra en el Pro-
grama 3.3.
Introduccin a la programacin 59
Programa 3.3. Ilustracin de salida estndar con los mtodos print() y println()
class Escritura {
public static void main(String[] args) {
int i, j;
i=1;
j=3;
System.out.print(Sin salto de linea );
System.out.print(i: +i);
System.out.print( j: +j);
System.out.println(); //Salto de lnea
System.out.println(Con salto de linea );
System.out.println(i: +i);
System.out.println(j: +j);
} // Fin mtodo main
} //Fin clase Escritura
El resultado sera:
Como puede observarse, tras la impresin realizada con print() continuamos en la misma lnea.
Despus de usar println() se produce un salto a la lnea siguiente. En ambos mtodos observamos
que las cadenas alfanumricas se imprimen sin ms que colocarlas entre comillas dobles. Vemos tam-
bin que el contenido de una variable se imprime indicando el nombre de la variable precedido de un
signo 1. El signo 1 indica que el contenido de la variable se aade a la salida.
Los mtodos print() y println() aceptan las denominadas secuencias de escape. Una secuen-
cia de escape es una serie de caracteres con un significado especial precedida por una barra invertida
(backslash): \. Las secuencias de escape indican algn propsito especfico, como se muestra en la
Tabla 3.7.
La barra invertida implica que lo que viene detrs tiene algn significado para el sistema. Por
ejemplo, para el sistema la comilla simple indica principio o fin de carcter y la comilla doble princi-
pio o fin de cadena de caracteres. Esto quiere decir que si pretendemos imprimir una comilla simple o
doble no basta con escribirla en un print() o println(), pues el sistema no la entendera como un
carcter literal. Usando una secuencia de escape, s que el sistema lo interpreta literalmente. La barra
invertida, \, hace que estos smbolos aparezcan tal cual, como se puede observar en el Programa 3.4.
class Ejemplo {
public static void main(String [] args) {
System.out.println (El dijo: \ Fuera de aqui \);
} // Fin mtodo main
} // Fin clase ejemplo
El resultado es:
El dijo: Fuera de aqu
60 Introduccin a la programacin con orientacin a objetos
Usando la secuencia de escape \ hemos podido escribir las dobles comillas y evitar que el com-
pilador lo interprete como fin de cadena.
Una vez introducida la salida en Java, y tal y como se indic en el apartado 3.3.3, veamos un pro-
blema de overflow en el Programa 3.5.
Programa 3.5. Ejemplo adicional de salida de datos ilustrando un overflow con enteros
class Overflow {
// Ejemplo de overflow
public static void main(String[] args) {
/* La operacin anterior es
011111111 11111111+00000000 00000000=100000000 00000000
Pero el 1 est en el bit de signo y en complemento a dos el resultado
es
-32768 */
El resultado sera:
numero: 32766
numero: 32767
numero: -32768
numero: -32767
Lo primero es indicar que el uso del molde (short) es para trabajar con tipo short, pues el lite-
ral 1 se interpreta como de tipo int. La razn del comportamiento observado en la salida, es que pre-
tendemos almacenar un valor mayor que el permitido en el tipo short. Como valor positivo, el tipo
short slo permite representar hasta 32767. Esto corresponde al valor binario, 011111111 11111111.
Si sumamos uno ms obtenemos en binario 10000000 00000000. Ahora bien, este valor tiene el pri-
mer bit (bit de signo) con valor 1 y el sistema lo interpreta como un nmero negativo. Como un valor
negativo se interpreta en complemento a dos, el valor binario indicado corresponde a 232768. As lo
interpreta el sistema y va sumando uno a este valor. El problema aparece por pretender almacenar un
12
Big o little endian indica en qu orden estn almacenados los bytes para los tipos de datos. Por ejemplo, un entero
representado con cuatro bytes (byte1 byte2 byte3 byte4) puede estar almacenado con los cuatro bytes en el orden natural (con
los bytes ms significativos al principio: byte1 byte2 byte3 byte4). sta es la codificacin big endian. Otra posibilidad es que
el entero se almacene con los bytes menos significativos al principio (en el orden: byte4 byte 3 byte2 byte1). sta es la con-
vencin little endian. Java usa la convencin big endian y los PCs la little endian (vase van der Linden, 1999).
Introduccin a la programacin 61
valor positivo mayor que el que el tipo puede representar. ste es un ejemplo tpico de overflow de
enteros. Un overflow de reales usualmente lo que produce es un error del programa.
En Java, la salida hacia pantalla con println() o print() se realiza con buffer. Un buffer es un
almacenamiento intermedio. El uso de un buffer es til, pues hace ms eficiente el procesamiento.
Normalmente, el buffer se usa para adaptar dos entidades que trabajan a distinta velocidad. As, la
mas rpida, por ejemplo, enva la informacin al buffer, que la puede almacenar, mientras la ms len-
ta va procesando poco a poco dicha informacin. De esta forma la entidad rpida puede ocuparse de
otra tarea. En Java, la salida con buffer almacena la informacin en dicho buffer hasta que ste est
lleno, el programa se completa o se vaca de alguna forma (flush). El buffer puede ser explcitamente
vaciado usando el mtodo flush: System.out.flush(). Este mtodo fuerza a System.out a
mostrar todo lo que haba guardado en el buffer. Se puede usar despus del mtodo println() o
print() si interesa que se muestre en ese mismo momento el contenido del buffer. En las versiones
actuales de Java la gestin del buffer es eficiente y para las tareas habituales, como las presentadas en
este texto, no se precisa interaccionar directamente con l.
La salida de informacin es relativamente sencilla, pero la entrada no lo es tanto debido a la nece-
sidad de convertir desde la corriente (stream) de 8 bits a la de 16. Esto no es forzoso, pero se reco-
mienda en las aplicaciones actuales, a fin de trabajar directamente con corrientes Unicode (16 bits).
Con corrientes de 8 bits, se puede usar la clase DataInputStream para la lectura. Esta clase per-
mite leer datos en binario como corrientes de 8 bits, poseyendo mtodos para leer todos los tipos pri-
mitivos. Dichos mtodos son:
readBoolean()
readByte()
readShort()
readInt()
readLong()
readFloat()
readDouble()
readChar()
import java.io.*;
class Lectura {
public static void main(String [] args)throws IOException {
BufferedReader leer =new BufferedReader
(new InputStreamReader(System.in));
String mensaje;
System.out.print(Introduzca una cadena de caracteres: );
13
Como veremos ms adelante, para crear un objeto de una clase determinada se usa un mtodo de la propia clase deno-
minado mtodo constructor.
62 Introduccin a la programacin con orientacin a objetos
En el Programa 3.6 usamos la clase BufferedReader que provee de un mtodo para leer una
lnea entera de caracteres conectada a la nueva corriente. La clase BufferedReader no est en el
paquete estndar java.lang sino en el java.io. Por tanto, lo primero que hacemos es importar con
la palabra reservada import todas las clases del paquete java.io, indicando java.io.*. Los mto-
dos en las clases del java.io pueden producir errores recuperables de entrada/salida. Que un error
sea recuperable indica que, si se produce, es posible controlarlo desde dentro del programa. Como
veremos con detalle en el Captulo 9, Java usa el mecanismo de excepciones para representar y ges-
tionar estos errores recuperables. En nuestro ejemplo es posible generar la excepcin (error recupera-
ble) general de entrada/salida, denominada IOException. Java exige que se indique siempre qu se
debe hacer si se llega a producir una excepcin en un mtodo. El equivalente a decir que no se quiere
controlar la excepcin, es decir, que sta se arroja (throws). Esto se indica en la cabecera del mto-
do donde puede aparecer la excepcin. En nuestro caso, el mtodo main() usa la clase Buffere-
dReader, as que para no preocuparnos del manejo de la IOException indicamos throws
IOException como parte final de la cabecera del main().
Con la clase BufferedReader lo que hacemos es crear un objeto llamado leer para realizar la
lectura deseada. Analicemos la creacin del objeto leer:
Una vez vista la lectura de cadenas de caracteres el siguiente objetivo es la conversin de una cade-
na leda por teclado a formato numrico. Para ello usamos las clases contenedoras. De momento bas-
te saber que hay una clase contenedora asociada a cada tipo primitivo y que se denomina con el
nombre completo del tipo. As, para enteros existe la clase contenedora Integer y no int.
Para realizar la lectura numrica con corrientes de 16 bits habr que realizar dos tareas:
Programa 3.7. Programa que lee y suma dos nmeros enteros introducidos por teclado
import java.io.*;
class Sumita {
public static void main(String [] args) throws IOException {
int a, b, suma;
a=Integer.parseInt(leer.readLine()); /*Lectura y
conversin a
entero */
suma=a+b;
System.out.println(Suma: +suma);
} //Fin mtodo
} //Fin clase
En el programa anterior se hacen dos lecturas con readLine(), una para cada valor entero. La
lectura con readLine() devuelve una cadena de caracteres. As, si tecleamos como datos el entero
123 lo que el programa lee con readLine() no es el entero 123 sino la cadena 123. La cadena se
convierte al valor numrico que representa con el Integer.parseInt().
En el Programa 3.8 se muestra una conversin a tipo Double. El programa convierte grados cent-
grados a Fahrenheit teniendo en cuenta que la relacin entre ellos es, F=9/5 C 132.
import java.io.*;
64 Introduccin a la programacin con orientacin a objetos
class Fahrenheit {
// Declaracin de variables
double grados_C, grados_F;
final double NUEVE_QUINTOS=9.0/5.0;
final double TREINTAYDOS=32.0;
// Entrada de datos
System.out.println (Introduzca la temperatura en grados
+ centigrados:);
grados_C=Double.parseDouble(leer.readLine());
// Salida de informacin
System.out.println (La temperatura en Fahrenheit es:
+grados_F + F);
Para una entrada de 100 C el resultado del programa sera 212 F. Para una entrada de 0 C el resul-
tado sera 32 F.
El lector interesado en una visin ms detallada de la entrada/salida en Java puede consultar
Eckel, 2002; Naugton y Schildt, 1997; Harold, 1999.
a) Condiciones
Introduccin a la programacin 65
Las condiciones, tambin llamadas sentencias de seleccin o decisiones, evalan una condicin lgi-
ca y deciden qu fragmento de cdigo se ejecutar en funcin del resultado. Para realizar una decisin
se usa en la mayora de los lenguajes la sentencia if (si condicional ingls). En Java, la sintaxis com-
pleta de la sentencia if es:
if (condicin) {
---- bloque de sentencias ----
}
else {
---- bloque de sentencias ----
}
if (condicin) {
---- bloque de sentencias ----
}
Igual que en el caso anterior, cuando la condicin es cierta se realiza el bloque, si es falsa el flujo
de control salta el bloque y se ejecuta la sentencia que se encuentra despus de la llave que cierra el
if. Como hemos visto, en un if se evala una condicin. En dicha condicin, la relacin ms senci-
lla que podemos establecer es la de comparacin expresada con el operador = = (distinguir de la asig-
nacin, =). La ilustracin ms simple es la determinacin de la igualdad entre dos valores. Veamos un
ejemplo. Consideremos dos variables, valor1 y valor2. Podemos construir una condicin con ellas
tal como:
if (valor1 == valor2) {
---- sentencias para condicin verdadera
}
else {
---- sentencias para condicin falsa
}
Cuando el bloque de la clausula if o else slo contiene una sentencia las llaves se pueden omi-
tir, aunque el cdigo es ms claro mantenindolas. El Programa 3.9 muestra un ejemplo de la senten-
cia if-else.
import java.io.*;
class Condicion {
// Ejemplo de sentencia condicional if-else
public static void main(String[] args)throws IOException{
int x;
BufferedReader leer =new BufferedReader
(new InputStreamReader (System.in));
66 Introduccin a la programacin con orientacin a objetos
Programa 3.10. Programa que suma los enteros desde 1 hasta 4 usando un bucle while
controlado por contador (continuacin)
// Lectura de datos
System.out.println(Introduzca un entero);
x=Integer.parseInt(leer.readLine());
System.out.println(Entero introducido +x);
// Aplicacin de la condicin
if (x < 0) {
System.out.println(El numero es negativo);
}
else {
System.out.println(El numero es positivo);
} // Fin de la clusula else
b) Bucles
Otra forma de modificar el flujo de control por medio de una cierta ramificacin es usando bucles. Los
bucles son tiles porque frecuentemente es necesario repetir una sentencia o un bloque de sentencias
varias veces en un programa. Esto se consigue con la sentencia de repeticin, iteracin o bucle. En los
lenguajes de programacin suele haber varias.
Como ejemplo consideremos el bucle de tipo while (mientras) que repite un bloque de senten-
cias mientras se cumpla una cierta condicin. En Java el bucle while se implementa como una sen-
tencia, la sentencia while. La sintaxis de la sentencia while en Java es:
while (condicin) {
---- bloque de sentencias ----
}
El bloque de sentencias (puede ser una nica sentencia y entonces no haran falta las llaves) se
repetira mientras la condicin es verdadera. Si la condicin es falsa al principio, las sentencias den-
tro del bucle while no se ejecutan. En el momento en el que sea falsa la condicin, el flujo de con-
trol salta a la sentencia colocada despus del cuerpo del bucle while. El uso del bucle while se ilustra
en el Programa 3.10.
Programa 3.10. Programa que suma los enteros desde 1 hasta 4 usando un bucle while
controlado por contador
class Contador {
public static void main (String [] args) {
final int FIN=4;
int i=0;
while (i<=FIN) {
System.out.println(Valor del contador: +i);
Introduccin a la programacin 67
La salida sera:
Fijmonos en que el bucle se repite cinco veces, pero que el contador (i) vara de cero a cuatro.
ste es un ejemplo tpico de bucle controlado por contador. Tanto la ramificacin como los bucles se
tratarn en detalle en el siguiente captulo.
3.5. OPERADORES
A menudo, las sentencias de programacin involucran expresiones. Una expresin es una combi-
nacin de operadores y operandos usados para realizar un clculo. Un operador es una entidad que
realiza una operacin. Un operando es una entidad que experimenta el efecto de un operador. El
valor calculado no tiene por qu ser numrico, aunque a menudo lo es. Los operandos usados en
las operaciones pueden ser literales, constantes, variables u otras fuentes de datos. Los operadores
son diversos en un lenguaje de programacin y dependiendo del nmero de operandos sobre los
que actan, los operadores pueden ser unarios, tambin llamados monarios, si actan sobre uno, o
binarios si actan sobre dos operandos. Por ejemplo, el operador 1 puede ser binario si representa
suma, como en una adicin de dos enteros (212), o puede ser unario, si es el operador de signo
como en (12).
En los lenguajes de programacin los operadores son genricamente los mismos y admiten una
clasificacin en funcin del tipo de operacin que realizan. Vamos a considerar los distintos tipos de
operadores.
14
En algunos lenguajes este operador se denomina mdulo.
68 Introduccin a la programacin con orientacin a objetos
Los operadores aritmticos son operadores binarios que aplican las operaciones aritmticas. La sinta-
xis es prcticamente homognea en los distintos lenguajes de programacin. En particular en Java, la
sintaxis es la recogida en la Tabla 3.8.
La suma, resta y multiplicacin no merecen comentario especial. La divisin, por otro lado, mere-
ce ms atencin. Si ambos operandos, numerador y denominador, son enteros el resultado es entero y
se trunca la parte decimal. Este caso se denomina de cociente entero. Sin embargo, si alguno de los
operandos es real se promueve aritmticamente el otro operando y el resultado es real. El ltimo ope-
rador es el operador resto. La operacin resto 14 acta sobre operandos enteros y devuelve un entero
que es el resto del cociente entre los operandos. El resultado toma el signo del numerador. Ilustremos
el comportamiento de los operadores de divisin y resto con algunos ejemplos.
a) Supongamos que cociente_real es de tipo double y N,M son enteros. Entonces si hace-
mos:
N=9;
M=4;
cociente_real=N/M;
En primer lugar se evala el cociente entero de N/M. El resultado sera 2. Luego el 2 (entero) se asig-
na a cociente_real y se promueve a tipo real. El resultado sera 2.0 almacenado en cociente_real.
b) Supongamos que cociente_real y A son de tipo double y que B es una variable int.
Entonces si hacemos:
A=9;
B=4;
cociente_real=A/B;
el resultado final sera 2.25, almacenado en cociente_real.
obtendramos el valor 1 (el resto del cociente de 9 entre 4). Observemos que el signo es positivo, como
el del numerador. El operador resto en muy til para determinar si un valor es divisible por otro.
En un lenguaje de programacin, los operadores se aplican en un cierto orden, es decir, hay una
Introduccin a la programacin 69
precedencia. Es necesario conocer el orden de precedencia de los operadores y cmo se van aplican-
do (de derecha a izquierda o de izquierda a derecha). En Java los operadores se aplican de izquierda a
derecha. La precedencia de los operadores aritmticos es:
(* , / , %) > (1,2)
514/3
a) (514)/3
b) 51(4/3)
Segn las reglas de precedencia el cociente se realiza en primer lugar, as que estaramos en el
caso b).
Veamos otro ejemplo. Sea la expresin,
5112/5210%3
/, %, 1, 2
((5112)/5)210%3
evaluemos el resultado paso a paso.
a) En primer lugar se evala (5112), por lo que la expresin quedara como (17/5)210%3.
b) Ahora se evala el cociente entero (17/5) y la expresin resulta (3)210%3.
c) Se realiza ahora el resto de 10 entre 3, resultando 321.
d) El resultado final tras aplicar el operador resta es 2.
El operador de asignacin (5) tiene menor precedencia que los operadores aritmticos, por lo que
la asignacin se hace en ltimo lugar. As,
a 5 (312)/5
contador++;
valor--;
son equivalentes a:
Tabla 3.10. Efecto como sufijo y prefijo de los operadores de incremento y decremento al apli-
carlo sobre un valor inicial de contador=5
contador=contador+1;
valor=valor-1;
Estos operadores pueden usarse como prefijos o como sufijos. Cuando actan sobre una sola
variable, como en el ejemplo anterior, el resultado es el mismo. As;
decrementa siempre. Sin embargo, cuando estos operadores se usan en una expresin, el resultado es dife-
rente si aparecen como sufijos que como prefijos. Por ejemplo, sea el siguiente fragmento de cdigo,
contador=5;
valor=contador++;
System.out.println(valor: +valor);
System.out.println(contador: +contador);
el resultado sera:
valor: 5
contador: 6
contador=5;
valor=++contador;
System.out.println(valor: +valor);
System.out.println(contador: +contador);
valor: 6
contador: 6
imprimir:
Relacin Sintaxis
Igual 55
Distinto !5
Mayor .
Menor ,
Mayor o igual .5
Menor o igual ,5
72 Introduccin a la programacin con orientacin a objetos
30 32 32 32
Programa 3.11. Efecto de los operadores de incremento y decremento sobre el tipo char
class Operador{
public static void main(String [ ] args) {
char letra;
letra=a;
System.out.println(letra es igual a: +letra);
++letra;
System.out.println(letra es igual a: +letra);
Significado Operador
y lgico &&
o lgico ||
no lgico !
letra es igual a: a
letra es igual a: b
X Y X&&Y
V V V
V F F
F V F
F F F
letra es igual a: bc
letra es igual a: a
letra es igual a: b
letra es igual a: bc
letra es: b
X Y X Y
V V V
V F V
F V V
F F F
Por ejemplo, dadas dos variables valor1 y valor2, seran expresiones vlidas:
a) valor1!=valor2
b) valor1 >valor2
c) valor1<=valor2
Estos operadores son de importancia fundamental a la hora de construir expresiones lgicas como
las usadas en los if o en los bucles.
X !X
V F
F V
Hay tres operadores lgicos que son: el y lgico, el o lgico y el no lgico. La Tabla 3.12
muestra cmo se representan dichos operadores en Java.
El y y el o lgicos son operadores binarios y el no es unario. La accin de estos operado-
res se representa claramente usando las tablas de verdad. Una tabla de verdad o de operacin propor-
74 Introduccin a la programacin con orientacin a objetos
ciona el resultado de la accin del operador lgico en funcin de todos los posibles valores (verdade-
ro, V, o falso, F) de los operandos. Las tablas de verdad son una forma sencilla de exponer el efecto
de los operadores lgicos. Veamos las tablas de verdad de los tres operadores considerados.
a) En la Tabla 3.13 encontramos la tabla de verdad del y lgico. Como se puede ver, las dos
condiciones X e Y deben ser verdaderas para que la interseccin lgica (el y) sea verdadera.
Es decir, se tiene que cumplir una condicin y la otra para obtener un resultado verdadero.
Veamos un ejemplo de y lgico,
int i=1;
int j=2;
if (i==1 && j==2) {
System.out.println (La condicion se cumple);
}
else {
System.out.println (La condicion no se cumple);
}
El resultado sera,
La condicion se cumple
b) La tabla de verdad del o lgico se encuentra en la Tabla 3.14. En este caso, una de las dos
condiciones X e Y debe ser verdadera para que la unin lgica (el o) sea verdadero. Se tie-
ne que cumplir una condicin o la otra para que el resultado sea verdadero.
Veamos un ejemplo de o lgico,
int i=1;
int j=2;
if (i==1 || j==3) {
System.out.println (La condicion se cumple);
}
else {
System.out.println (La condicion no se cumple);
}
La condicion se cumple
if (!etiqueta) {
System.out.println (La condicion se cumple);
}
else {
System.out.println (La condicion no se cumple);
Introduccin a la programacin 75
El resultado sera:
La condicion no se cumple
Los operadores lgicos tienen distinta precedencia. El operador lgico NO es el de ms alta pre-
cedencia, el siguiente es el Y, y despus el O es decir, la precedencia es:
if (a==3 || 4) {
---- bloque de acciones ----
}
if (a==3 || a==4) {
---- bloque de acciones ----
}
Los operadores lgicos && y || aplican la evaluacin cortocircuitada de operaciones. Esto quie-
re decir que si la primera condicin ya ha determinado el resultado de toda la expresin, no se evala
la segunda. Por ejemplo,
Existen varios operadores que combinan una operacin bsica con la asignacin. La idea es simpli-
ficar la operacin habitual de realizar una operacin sobre una variable y almacenar el resultado en
76 Introduccin a la programacin con orientacin a objetos
Tabla 3.16.
Introduccin a la programacin 77
Como puede observarse, la sintaxis de estos operadores es Operacin5. Al usar estos operado-
res no estamos limitados a tener que usar una sola variable en el lado de la derecha de la igualdad.
Podemos usar expresiones. En este caso, la expresin en el lado derecho se evala primero y luego se
combina con la variable de la derecha. Dicho de otra forma, todos estos operadores evalan comple-
tamente la expresin de la parte derecha en primer lugar, y luego usan el resultado como el operando
derecho de otra operacin. Veamos un ejemplo:
a/=Valor/5.0+Total;
es equivalente a:
a=a/(Valor/5.0+Total);
Fijmonos en que los parntesis en el segundo caso abarcan toda la expresin que estaba en el lado
de la derecha en el primer caso.
EJERCICIOS PROPUESTOS
Ejercicio 1.* Cul es el resultado del siguiente programa?
class Ejemplo {
public static void main(String [] args) {
int a=1, b=4, c=2, d=1;
int x=a+b/c+d;
System.out.print(x + x);
}
}
Ejercicio 2.* Suponga que b es una variable lgica (boolean). Cul es el resul-
tado de las siguientes expresiones?
a) b==true
b) b=true
Ejercicio 3.* Suponiendo que las variables total y num son de tipo entero y que
inicialmente contienen los valores 2 y 3, respectivamente Cul es
el valor que adquieren total y num despus de las siguientes sen-
tencias?
a) total=++num;
b) num=total++;
c) total=++num + num++;
Ejercicio 4.* Suponga que r1 y r2 son dos nmeros reales. Escriba el cdigo
necesario para determinar si son iguales suponiendo que la precisin
de la representacin numrica es p.
Ejercicio 5.* Escriba un programa en Java que acepte por teclado el radio de una
circunferencia y evale su permetro y su superficie. Nota: para el
nmero utilice la constante PI de la clase Math (Math.PI).
78 Introduccin a la programacin con orientacin a objetos
Ejercicio 6.* Escriba un programa que calcule la suma de los cuadrados de los
nmeros enteros comprendidos entre 1 y N donde N es un entero
que se lee por teclado.
class Alcance {
public static void main(String [] args) {
int i=3;
{
int j=4;
}
System.out.println(j: +j);
System.out.println(i: +i);
}
}
Ejercicio 8.* Para una disolucin de un cido dbil, HA, cuya constante de aci-
dez es Ka, el pH viene dado por la expresin (aproximada):
pH . (1/2)(pKa2log[HA])
class Ejercicio {
public static void main(String[] args) {
char probador;
probador=c;
System.out.println(probador: + probador);
++probador;
System.out.println(probador:+probador);
System.out.println(probador:+ probador++ +
probador
+probador-- + probador);
}//del main
}// de la clase
Introduccin a la programacin 79
class Ejercicio {
public static void main(String[] args) {
int indice;
indice=20;
System.out.println(++indice + +indice++ + +
indice);
}
}
REFERENCIAS
ECKEL, B.: Piensa en Java, Segunda Edicin, Prentice Hall, 2002.
HAROLD, E. R.: Java I/O, First Edition, OReilly, 1999.
NAUGHTON, P. y SCHILDT, H.: Java Manual de Referencia, Osborne/McGraw-Hill, 1997.
PRATT, T. W. y ZELKOWITZ, M. V.: Programming Languages. Design and Implementation, Third edition, Prenti-
ce Hall, 1996.
PRIETO, A., LLORIS, A. y TORRES, J. C.: Introduccin a la Informtica, Segunda Edicin, McGraw-Hill, 1995.
Unicode: www.unicode.org ltima visita realizada en junio de 2002.
VAN DER LINDEN P.: Just Java. 1.2, Fourth Edition, Sun Microsystems Press, 1999.
4
Programacin estructurada
Sumario
4.1. INTRODUCCIN
Hasta la dcada de los aos sesenta del siglo XX los computadores posean unos recursos muy limita-
dos. Era responsabilidad del programador formular los algoritmos de forma que fueran utilizados de
la manera ms eficiente posible en una mquina dada. Como indica Wirth (Wirth, 1974), la esencia de
la programacin era la optimizacin de la eficiencia de mquinas particulares ejecutando algoritmos
particulares. Segn creca la potencia de los ordenadores y aumentaba la complejidad de los progra-
mas, el objetivo dej de ser la necesidad de ahorrar bits y microsegundos de cmputo. En su lugar, el
problema devino en la gestin del desarrollo de programas grandes y complejos, es decir, la gestin
de la complejidad del software. Con las tcnicas artesanales de la poca estos sistemas eran difciles
de disear, codificar y probar, y prcticamente imposibles de entender y mantener. Se requeran mejo-
res tecnologas y, como ya hemos visto, es en este contexto donde se acua el concepto de Ingeniera
del Software. La idea clave que surgi en esta poca es que ningn nivel de eficiencia es til si el pro-
grama no es fiable de antemano.
Como parte de estos esfuerzos por disciplinar el desarrollo de software se realizaron estudios sobre
la estructura lgica del cdigo. Ya en esta poca, se reconoca que el salto incondicional (la sentencia
goto) muy usado en los lenguajes de aquel entonces, era una sentencia que produca ms problemas
que los que solucionaba. Los estudios de Bhm y Jacopini sobre la estructura del cdigo (Bhm y
Jacopini, 1966) demostraron que en condiciones normales era posible construir o reescribir cualquier
programa, consiguiendo el mismo objetivo, sin usar la sentencia goto. Los problemas engendrados
por la sentencia goto desde el punto de vista del desarrollo de software, fueron tratados por E. W.
Dijkstra en un famoso trabajo (Dijkstra, 1968), y es a l al que se atribuye el acuamiento de la expre-
sin programacin estructurada.
Por programacin estructurada entendemos un estilo de codificacin libre del salto incondicional,
que refuerza la inteligibilidad y la fiabilidad del cdigo. A pesar de lo que se entiende normalmente,
la programacin estructurada no es simplemente codificacin sin goto, sus objetivos son ms amplios
y variados (Martin y McClure, 1988):
Hoy por hoy la programacin estructurada es un tema que todo programador debe conocer desde
los cursos ms elementales de programacin. La programacin estructurada debe ser el estilo normal
de programacin para todo profesional. En este captulo vamos a presentarla formalmente. Para ello,
comencemos con un elemento que, aunque no forma parte de la programacin estructurada, es conve-
niente conocer (aunque no se recomienda su uso). Se trata del salto o ramificacin incondicional.
cada con la etiqueta. Este salto se produce inevitablemente ya que no hay ninguna condicin en fun-
cin de la cual se pueda dar o no. Por esta razn, este salto forzoso se denomina salto incondicional,
por contraposicin a otras sentencias donde tambin se produce una ramificacin del flujo lgico pero
dependiendo de una condicin.
En Java el goto como tal no existe, as que veamos un ejemplo de su uso en otro lenguaje, por
ejemplo, en el clsico Fortran 77. Consideremos el siguiente fragmento de cdigo:
if (A.NE.1) goto 10
A=A+1
goto 20
10 A=A-1
20 Write (6, *) A
import java.io.*;
class Ejercicio {
public static void main(String [] args) throws IOException {
int num=1;
int dato;
BufferedReader leer = new BufferedReader
(new InputStreamReader(System.in));
while (num !=20) {
System.out.println(\nIntroduzca un numero: );
dato=Integer.parseInt(leer.readLine());
if (dato ==12) {
break;
}// Fin del if
82 Introduccin a la programacin con orientacin a objetos
No es una buena costumbre de programacin usar las sentencias break para acabar bucles. Siem-
pre se puede hacer lo mismo sin necesidad de usarlas. Para ello basta con incluir la condicin que se
usara para disparar el break en la condicin de finalizacin del bucle, realizando un producto lgico
de condiciones (usando el y lgico). Por ejemplo, el caso anterior se podra reescribir usando como
condicin de finalizacin del bucle que num sea distinto de 20 al mismo tiempo que dato es distinto
de 12, vase el Programa 4.2. Cuando dato sea igual a 12 la condicin de control del while se vuel-
ve false y el bucle termina sin necesidad de usar break.
import java.io.*;
class Ejercicio {
public static void main(String [] args) throws IOException {
int num=1;
int dato=0;
La sentencia break hace que el flujo del programa salte de un sitio a otro y, normalmente, no es
necesario usarlo. La excepcin es la sentencia switch donde break es necesario porque, como vere-
mos, no hay otra forma de conseguir el funcionamiento correcto.
La sentencia continue tambin produce un salto incondicional. Su comportamiento se puede
ilustrar con un bucle. Un continue dentro de un bucle produce un salto desde el punto donde est
hasta el final del bucle, pero sin salir de l. El bucle contina realizando iteraciones, vase el Progra-
ma 4.3. En este caso, cuando dato=12, se salta al final del while, no ejecutndose las sentencias
System.out.println y num++. Sin embargo, el bucle no acaba, se realiza una nueva iteracin y el
bucle contina hasta que num valga 20.
Programacin estructurada 83
import java.io.*;
class Ejercicio {
public static void main(String [] args) throws IOException {
int num=1;
int dato=0;
El Programa 4.3 se puede modificar de forma que el cdigo haga lo mismo pero sin usar la sen-
tencia continue, vase el Programa 4.4. Como podemos observar, la sentencia continue no es
necesaria, se puede conseguir el mismo objetivo sin ella. Por la misma razn que la sentencia break,
la sentencia continue debe evitarse.
import java.io.*;
class Ejercicio {
public static void main(String [] args) throws IOException {
int num=1;
int dato=0;
break y continue admiten el uso de etiquetas alfanumricas para producir un salto a la senten-
cia (o bloque) identificada con dicha etiqueta. La sintaxis es:
etiqueta: {
- bloque de sentencias -
}
La diferencia entre break y continue con respecto a las etiquetas es la siguiente. La sentencia
break con etiqueta se usa para saltar desde un bucle o switch (o en realidad desde cualquier sen-
tencia de bloque como el if) a la sentencia que se encuentra despus del bloque que lleva en su cabe-
cera la etiqueta. Cuidado, la sentencia de bloque (lo normal es un bucle o switch) lleva la etiqueta al
principio pero se salta al final. Un ejemplo sera:
meses:while (m<=12) {
d=1;
while (d<=31) {
if (coste > presupuesto) break meses;
d++;
}
m++;
}
Si se pone slo break, sin etiqueta, se terminara de ejecutar el while interno, pero seguira eje-
cutndose el while externo (el de m<=12). Con break etiqueta, se dejan de ejecutar el interno y
el externo. En nuestro caso, al ejecutarse break meses, se salta al final del bloque de sentencias eti-
quetado como meses. Resumiendo, para finalizar un bucle o bloque externo se debe etiquetar la sen-
tencia externa y usar el nombre de la etiqueta en la sentencia break colocada en el interior.
Igual que break, continue tambin puede portar etiqueta. La sentencia continue con etiqueta
permite saltar hasta el final del bloque que lleva la etiqueta. La diferencia con el break es que el sal-
to no implica la finalizacin de la estructura de control en la que estemos inmersos. Por ejemplo, con-
sideremos varios bucles anidados,
meses:while (m<=12) {
m++;
d=1;
while (d<=30) {
if (m==2 && d==29) continue meses;
System.out.println (m+ +d);
d++;
}
}
Con continue sin etiqueta se saltara a la siguiente iteracin del bucle ms interno (el de d<=30).
Por otro lado, con continue etiqueta se controla el bucle al que se salta, en este caso al externo.
En nuestro caso, el continue enva el control al final del bloque etiquetado con meses. En otras pala-
bras, enva el control al final de la presente iteracin del bucle con m<=12 pero el bucle contina en la
siguiente iteracin. Un continue con etiqueta saldr de todos los bucles internos hasta llegar al bucle
etiquetado. En este caso, se comprobar la condicin y si es posible continuarn las iteraciones.
Es conveniente conocer el funcionamiento del salto incondicional para poder realizar labores de
mantenimiento en software ya existente. Sin embargo, como norma de programacin se debe evitar el
uso de cualquier salto incondicional. Siempre es posible realizar el mismo trabajo sin necesidad de
recurrir a su uso.
Programacin estructurada 85
Teorema de estructura: Todo programa con un nico punto de entrada y un nico punto
de salida, cuyas sentencias se alcancen todas en algn momento y que no posea bucles infinitos
(programa propio) se puede construir con tres constructores elementales: secuencia,
seleccin y bucle.
El trabajo de Bhm y Jacopini demuestra que estos tres constructores forman un conjunto sufi-
ciente para construir cualquier algoritmo concebible. El salto incondicional no es un constructor nece-
sario. Si se usan slo los tres constructores elementales, el cdigo queda organizado de forma arriba-
abajo. Esto quiere decir que cuando se produzca una ramificacin (no iterativa) del cdigo, el flujo de
control se desplaza hacia abajo en el cdigo. La lgica del diseo resultante es fcil de seguir y enten-
der. Esto simplifica, entre otras, las labores de pruebas y de mantenimiento. Las condiciones que defi-
nen un programa propio no son exigentes ni extraas, se dan en prcticamente todos los programas o
secciones de cdigo. Un buen programador debe aplicar un estilo de codificacin estructurado como
algo absolutamente normal en sus programas. En otras palabras, la programacin estructurada debe ser
el estilo natural de todo programador.
El hecho de poder construir cualquier programa propio de forma estructurada permite la reestruc-
turacin de cdigo. Es sta una labor de mantenimiento 1 del software consistente en transformar cdi-
go no estructurado en estructurado. En esencia, se trata de la modelizacin de la lgica del programa
usando el lgebra de Boole con las tcnicas propuestas por Warnier (Warnier, 1974). Para ms deta-
lles sobre las tcnicas de reestructuracin (vase Yourdon, 1975).
El hecho de poder reestructurar cdigo no estructurado no quiere decir que esto pueda hacerse
usando el mismo nmero de constructores y sin variables adicionales. Muy frecuentemente es necesa-
ria la duplicacin de parte del cdigo, aunque ste es un precio pequeo a pagar por el aumento de la
manejabilidad del cdigo. Veamos un ejemplo que ilustra la posibilidad de reestructurar un programa
no estructurado. Consideremos el caso mostrado en la Figura 4.1. Es un diagrama donde cada bloque
indica una serie de sentencias. Consideremos el bloque E. Este bloque recibe el flujo de control (las
flechas) por dos sitios distintos. ste es el efecto tpico del salto incondicional, alcanzamos el cdigo
por dos puntos diferentes segn se salte o no se salte con el goto. Es fcil imaginar los problemas que
se pueden producir cuando se llega al mismo cdigo desde dos puntos diferentes del programa, pues
los valores de las variables dependern del camino recorrido. Para reestructurar el cdigo deberamos
evitar la entrada en E por dos puntos. Esto se puede hacer si duplicamos el bloque E tal y como se
muestra en la Figura 4.2. Obsrvese que ahora todos los bloques presentan un solo punto de entrada.
1
En realidad se tratara de labores de reingeniera del software. El lector interesado puede encontrar ms informacin
sobre este tema en el texto de Pressman (Pressman, 2002).
86 Introduccin a la programacin con orientacin a objetos
visto algunos de ellos y el resto los veremos en este captulo. Consideremos cada constructor por sepa-
rado.
4.4.1. SECUENCIA
La secuencia de acciones no tiene un constructor especfico, viene determinada de forma natural por
el flujo de control del programa. La secuencia de acciones est implcita en el orden en el que apare-
cen las sentencias en el programa, por ejemplo:
int valor=0;
valor=valor+1;
System.out.println(valor);
Programacin estructurada 87
4.4.2. SELECCIN
El mecanismo de seleccin bsico es la sentencia if-else. En sta se evala una condicin lgica y
se elige qu suceder en funcin del resultado. Estas sentencias se llaman sentencias de seleccin o
sentencias condicionales o simplemente decisiones. La sintaxis de la sentencia if-else en Java ya se
expuso en el captulo anterior.
Se pueden encadenar sentencias if sin problemas. Cuando enlazamos varias de ellas (o en gene-
ral varias estructuras de control) tenemos dos formas de hacerlo:
Consideremos dos if controlados por una condicin cada uno. La Figura 4.4 ilustra el caso de una
concatenacin de nuestras dos estructuras condicionales.
Como puede observarse, tenemos las dos condiciones del ejemplo colocadas una a continuacin
de la otra. Independientemente de si la primera condicin es verdadera o falsa se llega siempre a la
segunda condicin. Como ejemplo consideremos un programa con dos variables denominadas pre-
cio y cantidad. Si precio es mayor de 10 e se hace un descuento del 5%. Por otro lado si can-
tidad es mayor de 5 se aade un artculo ms de regalo. El cdigo correspondiente sera,
88 Introduccin a la programacin con orientacin a objetos
if (cantidad >5) {
cantidad++; // Regalo de un artculo ms
}
b) Anidamiento incluyendo la clusula else (las condiciones estn unas dentro de otras)
En este caso vamos a considerar tres condiciones. La Figura 4.5 muestra un ejemplo de organiza-
cin anidada de estas condiciones.
En este caso, tenemos condiciones dentro de condiciones. As, dentro de la parte de la sentencia
if con la condicin1 se encuentra un nuevo if con la condicin2. A su vez, dentro de la parte
del else del if con la condicin1 se encuentra otro if con la condicin3. Ahora los if no son
independientes. Por ejemplo, si condicin1 es verdadera no se llega al if con la condicin3 y si
condicin1 es falsa no se llega al if con condicin2. Las flechas usadas indican el alcance de
cada estructura. El hecho de que los bloques delimitados por las flechas queden unos dentro de otros
y que las lneas de alcance no se corten es una consecuencia de la programacin estructurada. Como
ejemplo de estructura condicional anidada consideremos un ejemplo similar al anterior con las varia-
bles precio y cantidad. Como antes, si precio es mayor de 10 e se hace un descuento del 5%. Sin
embargo, consideremos ahora que slo si precio es mayor de 20 e se aade el artculo de regalo a
cantidad. En este caso, el cdigo sera:
if (precio >10.0) {
precio=precio*0.9; // Descuento del 10%
Programacin estructurada 89
if (precio >20.0) {
cantidad++; // Artculo de regalo
} // Fin if interno
} // Fin if externo
Obsrvese ahora cmo pasar o no por la segunda condicin depende del resultado de la primera.
En los lenguajes modernos existe la posibilidad de seleccin mltiple. En Java, esto se consigue
con la sentencia switch. Se trata de una sentencia de seleccin que permite elegir una entre varias
posibilidades. As, en funcin de un nico valor podemos seguir uno entre varios caminos. La senten-
cia switch evala una expresin y compara el resultado con una serie de valores. La ejecucin se
transfiere a la lista de sentencias asociada con el primer valor que coincide. El comportamiento es
equivalente a una serie de if anidados. La sintaxis de esta sentencia es:
switch (expresin) {
case literal 1:
bloque 1
break;
case literal 2:
bloque 2
break;
...
default:
bloque n
}
90 Introduccin a la programacin con orientacin a objetos
La expresin debe producir un valor de tipo int o char y el literal debe ser del mismo tipo que
el resultado de la expresin. No es necesario colocar llaves para delimitar los bloques de cada caso. La
sentencia break se coloca como ltima sentencia despus de cada case y hace que la sentencia
switch se acabe y se ejecute la siguiente sentencia despus de ella. Si no se usa break, se siguen
ejecutando las sentencias de los siguientes case hasta llegar al final del switch, o hasta que en algn
case haya un break. La necesidad de usar una sentencia break es una caracterstica poco elegante
de Java. La clusula default representa todos los casos no considerados en los case anteriores, es
decir, todos los valores de la expresin que no se correspondan con algn literal de los case, produ-
cirn un salto al default. La sentencia default no es necesaria a no ser que queramos que se eje-
cute algo cuando no se haya alcanzado ninguno de los casos. Si no se usa, se sale del switch y se
ejecuta la siguiente sentencia.
El Programa 4.5 muestra el uso del switch y adems ilustra el uso de la lista de parmetros en la
lnea de rdenes.
Programa 4.5. Ilustracin del uso de la sentencia switch y de lectura por lnea de rdenes
class Switch {
public static void main(String [] args) {
int numero;
numero=Integer.parseInt(args[0]);
switch (numero) {
case 1:
System.out.println(El numero es un 1);
break;
case 2:
System.out.println(El numero es un 2);
break;
default:
System.out.println(El numero no era ni 1 ni 2);
} // Fin switch
Antes de explicar la salida del programa consideremos el uso de la variable args. La cadena args
que hemos visto habitualmente en la cabecera del mtodo main, es en realidad una lista de objetos de
clase cadena (String). Esta lista empieza a enumerarse por cero (0) y contiene las cadenas de carac-
teres que se coloquen a la derecha del nombre del programa cuando se ejecuta. La primera cadena es
la nmero cero (args[0]), la siguiente la uno (args[1]), etc. Por ejemplo, consideremos un progra-
ma con la clase Ejemplo, contenida en el fichero Ejemplo.java y compilada para dar un fichero
Ejemplo.class que contiene el bytecode. Si ahora interpreto el bytecode con el intrprete del JDK
podra hacer:
Los tres nmeros separados con blanco al lado del nombre del fichero .class se almacenan
automticamente como cadenas alfanumricas en los elementos args[0], args [1] y args [2],
de forma equivalente a las asignaciones:
args [0]=23.4;
Programacin estructurada 91
args [1]=33.9;
args [2]=54.8;
Obsrvese que lo que se lee son cadenas de caracteres y que si queremos su equivalente numrico
hay que convertirlos al formato numrico correspondiente usando la clase contenedora necesaria.
En el caso del ejemplo que estamos considerando (Programa 4.5), se lee un nmero entero de la
lnea de rdenes y se mira su valor. Si es 1 2, se indica y si no, se salta al caso defecto (default)
indicndose que no era ni un 1 ni un 2.
Qu ocurrira si se elimina el primer break y se introduce 1 por la lnea de rdenes? Al llegar al
switch se producira un salto al caso 1 y se escribira:
El numero es un 1
El numero es un 2
Al encontrar aqu un break se saltara al final de la sentencia switch. Por lo tanto la salida obte-
nida sera:
El numero es un 1
El nmero es un 2
El numero es un 1
El numero es un 2
El numero no era ni 1 ni 2
Adems de las sentencias if y switch en Java existe un operador condicional que es un opera-
dor ternario, pues usa tres operandos. Las sintaxis es:
if (condicin){
expresin1;
}
else {
expresion2;
}
4.4.3. ITERACIN
Otra forma de modificar el flujo de control por medio de una ramificacin es usando bucles. Los
bucles son tiles porque frecuentemente es necesario repetir una sentencia, o un bloque de sentencias,
varias veces en un programa. En los lenguajes de programacin suele haber varias sentencias de repe-
ticin o bucle. Recordemos el tipo de bucle que podemos considerar como el ms directo, el bucle
while, cuyo efecto, como ya vimos, es que se repita un bloque de sentencias mientras se cumpla una
cierta condicin. Tal y como se indic en el Captulo 3, la sintaxis de la sentencia while en Java es:
while (condicin) {
bloque de sentencias
}
Las llaves ({}), que indican el final y el principio del bucle while, no son necesarias cuando el
cuerpo del while est formado por slo una sentencia. El bloque de sentencias se repetira mientras
la condicin sea verdadera. Si la condicin es falsa desde el principio, las sentencias dentro del whi-
le no se ejecutan. Esto es importante, un bucle while se repite cero o ms veces. En algn momen-
to el bloque de sentencias debe alterar la condicin para que se vuelva falsa y entonces se acabe el
bucle. En el momento en el que sea falsa el proceso contina con la sentencia colocada despus del
cuerpo del bucle while.
Otro tipo de bucle es el que comprueba la condicin al final del bloque de sentencias. En Java se
denomina sentencia do-while. La sintaxis de esta sentencia es:
do {
bloque de acciones
} while (condicin);
El bucle do-while se ejecuta hasta que la condicin es falsa. Un bucle do-while se ejecuta al
menos una vez, ya que la condicin se evala al final. Este tipo de bucle es especialmente til cuando
se procesa la seleccin de un men, ya que siempre se desea que el bucle del men se ejecute al menos
una vez. Para ilustrar su uso, veamos la utilizacin de un bucle do-while, para la obtencin del fac-
torial de un entero mayor que cero.
Como sabemos, el factorial de un entero N . 0 viene dado como un producto de factores,
N
N! 5 1 ? 2 ? ... ? N 5 P i
i 5 1
Para evaluar el factorial, podemos implementar el productorio con un do-while de la forma mos-
trada en el Programa 4.6.
import java.io.*;
class Factorial {
public static void main(String [] args) throws IOException {
int i,n;
double factorial;
BufferedReader leer =new BufferedReader
(new InputStreamReader(System.in));
En el programa anterior se lee el valor n por el teclado y se repite un do-while hasta que el con-
tador i alcanza el valor n. El ejemplo es simple y no distingue el caso n=0, pero sirve para indicar que
un sumatorio o un productorio se simulan con un bucle. As, para un sumatorio como,
N
6i
i 5 1
el algoritmo, suponiendo que hemos declarado una variable suma como double, una i como int
(para usarla como contador) y una n que indicara el lmite superior del sumatorio, sera:
// Inicializacin de variables
suma=0.0;
i=0;
while (i<n) {
i=i+1;
suma=suma+i;
}
En este caso, hemos ilustrado el ejemplo con un while. Fijmonos en que la variable que acumu-
la el valor se inicializa a uno para un productorio y a cero para un sumatorio. stos son ejemplos de
bucles controlados por contador. Dichos bucles son muy frecuentes y existe una forma abreviada de
implementarlos. Dicha forma se entiende como un nuevo tipo de bucle, el bucle for que no es sino
un while controlado por contador. El for se usa cuando se conoce el nmero de veces que se tiene
que repetir el bucle. En Java la sintaxis es:
for (inicializacin; condicin; incremento) {
-- bloque de sentencias --
}
suma=0.0;
for (i=1;i<=n;i=i+1) {
suma=suma+i;
}
Fijmonos en que en un bucle for el incremento del contador se hace despus de ejecutar el blo-
que de acciones. Por eso, si queremos que el bloque se ejecute incluyendo el valor lmite de la varia-
ble que controla el final del bucle (en este caso N) en la condicin hay que incluir un igual (en este
caso i<=n).
La variable contadora se puede declarar en el propio for. En este caso, debido a las reglas de
alcance de Java, dicha variable slo existe dentro del bucle for. Cuando salgamos del mismo, la varia-
ble no existir. Tambin es habitual el uso de los operadores de incremento o decremento para variar
la variable de control del bucle. Con todo esto, la sintaxis habitual de un for sera, en el ejemplo del
sumatorio,
suma=0.0;
for (int i=1;i<=n;i++) {
suma=suma+i;
}
En cualquier caso, siempre deben colocarse en la cabecera del for los punto y coma (;) de cada
componente, aunque no se incluya alguno de ellos. Por ejemplo:
for ( ;i<100; ){
System.out.println(i es: +i);
i++;
}
El hecho de colocar dos variables (como i y j o a y b en los ejemplos anteriores) no implica que
tengamos bucles anidados. Esto se observa en las condiciones que controlan los bucles. En los ejem-
plos anteriores hay una sola condicin para cada bucle.
Puede ocurrir que en un bucle la condicin que lo controla nunca cambie a falsa y que por lo tan-
to, el bucle se cicle, entrando en un bucle infinito. Esto implica que si no se produce antes un error, el
bucle se ejecutara sin parar hasta que se interrumpa el programa. Este error lgico, el bucle infinito,
es muy frecuente y debe ser evitado. Veamos un ejemplo de bucle infinito,
int contador = 1;
final int LIMITE=25;
El valor de contador empieza en 1 y se va decrementando, as que nunca llega a 25, con lo cual,
el bucle nunca terminara de ejecutarse.
Figura 4.7. Obsrvese que en la decisin se indica qu rama es la del s y cul es la del no. Tambin
se ha usado la flecha i para representar la asignacin en las variables Suma y Nmero. ste es un sim-
bolismo muy comn. Obsrvese tambin que el diagrama de flujo representa un bucle do-while, con
su condicin al final del bloque de acciones.
En el ejemplo anterior se muestra cmo representar un bucle do-while. El diagrama de flujo de
control que representa al bucle while se recoge en la Figura 4.8.
En este caso la condicin aparece al principio del bloque de sentencias a repetir. Observamos tam-
bin que en ambos ejemplos, el bucle se repite mientras la condicin es verdadera (rama del s).
Los diagramas de flujo de control no se consideran una herramienta estructurada y, en todo caso, su
uso se limita a programas pequeos. La principal desventaja es que son una herramienta demasiado
detallada para usarse como instrumento de diseo o incluso de documentacin de programas. A pesar
de ello, y por la fuerza de la costumbre, los diagramas de flujo se emplean para ilustrar algoritmos o
fragmentos de algoritmos. Por ello, son una herramienta que debe ser conocida por todo programador.
4.5.2. PSEUDOCDIGO
El pseudocdigo o lenguaje estructurado se puede considerar como un lenguaje de especificacin de
algoritmos. Su primer uso fue como una alternativa a los diagramas de flujo de control para promover
la metodologa estructurada. Como originalmente estos desarrollos se hicieron en Norteamrica, el
lenguaje usado era ingls, y como ingls estructurado se puede ver denominado en la literatura anglo-
Programacin estructurada 97
Figura 4.7. Diagrama de flujo de control que muestra la suma de los diez primeros enteros
distintos de cero
Bloque 1
No
Condicin
Bloque 2
sajona 2. El pseudocdigo es una herramienta til para representar y refinar algoritmos en una notacin
que se corresponde de forma casi inmediata con los constructores de un lenguaje de programacin. No
existe un estndar para el pseudocdigo, por lo que se pueden encontrar distintas variantes del mismo.
El pseudocdigo permite representar las tres estructuras principales de secuencia, seleccin y bucle.
Veamos una propuesta de pseudocdigo en castellano que podamos usar a lo largo de este texto.
a) Secuencia
No se representa con ningn formalismo especial. La secuencia se considera implcita en el orden occi-
dental de lectura; de arriba a abajo y de izquierda a derecha.
b) Seleccin
El if(condicin)-else se representa como tal en ingls y como Si(condicin)entonces-
Si_no en castellano.
El switch se representa como:
c) Bucle
Los bucles while y do-while se representan como Mientras y Haz-Mientras. El for lo pode-
mos representar como:
El operador de asignacin en pseudocdigo se suele representar por una flecha que apunta hacia
la variable en la que se coloca el valor, tal y como se muestra a continuacin,
Variable i 3.1416
Los bloques de sentencias se marcan a veces con start-end en ingls o Inicio-Fin en caste-
llano. A veces se usan los delimitadores del lenguaje que se acostumbre a manejar. El fin de los if o
de los bucles se marca con End_if, End_while, End_for o en castellano Fin_Si, Fin_Mientras,
Fin_Para.
El pseudocdigo se plante en principio como una herramienta independiente del cdigo. Es muy
frecuente que si un lenguaje de programacin es el ms usado en un entorno de trabajo, las palabras
clave usadas en el pseudocdigo sean las de ese lenguaje. Como ilustracin, veamos un ejemplo de
utilizacin de pseudocdigo. Establezcamos el pseudocdigo del programa que suma los 10 primeros
2
Estrictamente hablando no es lo mismo pseudocdigo que lenguaje estructurado, (vase Martin y McClure, 1988).
Programacin estructurada 99
Obsrvese que el pseudocdigo generado se puede traducir con facilidad a un lenguaje de progra-
macin.
a) Secuencia
La secuencia de acciones, representada por un bloque de sentencias que se van ejecutando una detrs
de otra, se indica por un smbolo de apertura de corchete, vase la Figura 4.9.
{
---bloque de sentencias---
}
b) Seleccin
La seleccin simple se representa como la secuencia, vase la Figura 4.10.
if (condici n) {
---bloque de sentencias---
}
La sentencia if-else es una variante del smbolo anterior, vase la Figura 4.11.
if (condici n) {
--- bloque de sentencias 1 ---
}
else {
--- bloque de sentencias 2 ---
}
switch (valor) {
case 1:
--- bloque de sentencias 1 ---
break;
case 2:
--- bloque de sentencias 2 ---
break;
case 3:
--- bloque de sentencias 3 ---
}
c) Bucle
Para representar cualquier tipo de bucle se usa un corchete, como en la secuencia, que abarca todo el
bucle pero que en la parte superior tiene una doble barra, vase la Figura 4.13.
while (condici n) {
--- bloque de sentencias ---
}
A nivel de codificacin los diagramas de accin son muy tiles pues se adaptan totalmente a un
estilo estructurado y delimitan muy bien los constructores que se estn utilizando y su alcance.
Existen multitud de tcnicas adicionales basadas en diagramas para representar la estructura del
cdigo a alto nivel de abstraccin. El lector interesado puede consultar el texto de Martin y McClure
(Martin y McClure, 1988).
EJERCICIOS PROPUESTOS
switch (opcion) {
case 1:
System.out.println(Uno);
break;
case2:
System.out.println(Dos);
break;
case 3:
System.out.println(Tres);
break;
default:
System.out.println(Otros);
}
Ejercicio 3.* Usando bucles como nicas estructuras de control y una nica sen-
tencia de impresin para el asterisco, escriba un programa en Java
que imprima la siguiente salida:
*****
***
*
i=0;
sum=0;
102 Introduccin a la programacin con orientacin a objetos
while (i<=n) {
i=i+1;
sum=sum+i;
}
Ejercicio 5.* Utilizando bucles como nicas estructuras de control escriba un pro-
grama que imprima lo siguiente:
*****
****
***
**
*
while (i < n) {
j=objetoX.valor(i);
if (j==-1) break;
i++;
}
Ejercicio 7.* Usando bucles como nicas estructuras de control escriba un pro-
grama que imprima lo siguiente:
*******
******
*****
****
***
**
*
class EjemploSwitch {
public static void main(String [] args) {
for (int i=0; i<=7; i++)
switch (i) {
case 0:
case 1:
case 2:
System.out.println(i+ es menor que 3);
break;
case 3:
case 4:
case 5:
System.out.println(i+ es menor que 6);
break;
Programacin estructurada 103
default:
System.out.println(i+ es 6 o mayor);
}
}//fin main
}//fin clase
class Ejercicio {
public static void main(String [] args) {
int s,x=0;
switch (x) {
case 0:
s=0;
default:
if (x<0)
s=-1;
else
s=1;
}
System.out.println(s);
}//fin main
}//fin clase
Ejercicio 10.* Reescriba el siguiente fragmento de cdigo sin usar la sentencia con-
tinue pero manteniendo la funcionalidad del cdigo.
}
}
Ejercicio 11.* Una lnea de autobuses cobra un mnimo de 20 e por persona y tra-
yecto. Si el trayecto es mayor de 200 km, el billete tiene un recar-
go de 3 cntimos por km. Sin embargo, para trayectos de ms de
400 km el billete tiene un descuento del 15%. Por otro lado, para
grupos de 3 o ms personas el billete tiene un descuento del 10%.
Con las consideraciones anteriores, escriba en Java un programa
estructurado que lea por teclado la distancia del viaje a realizar, as
como el nmero de personas que viajan juntas y que con ello cal-
cule el precio del billete individual.
Ejercicio 12.* Escriba un programa estructurado en Java que lea por teclado una
serie de nmeros reales y que calcule su media, mostrando el resul-
tado en pantalla. Utilice un bucle for para solicitar los datos y para
104 Introduccin a la programacin con orientacin a objetos
REFERENCIAS
BHM, C. y JACOPINI, G.: ACM, 366-371, 9(5), 1966.
DIJKSTRA, E. W.: Go To Statement Considered Harmful, Comm. ACM, 147-148, 11(3), 1968.
JOYANES AGUILAR, L.: Fundamentos de Programacin, Segunda Edicin, McGraw-Hill, 1996.
MARTIN, J. y MCCLURE, C.: Action Diagrams, Second Edition, Prentice-Hall, 1989.
MARTIN, J. y MCCLURE, C.: Structured Techniques. The Basis for CASE, Prentice-Hall, 1988.
PRESSMAN, R. S.: Ingeniera del Software, McGraw-Hill, Quinta Edicin, 2002.
WARNIER, J. D.: Logical Construction of Programs, Van Nostrand Reinhold, 1974.
WIRTH, N.: On the Composition of Well-Structured Programs, Computing Surveys, 247-259, 6(4), 1974.
YOURDON, E.: Techniques of Program Structure and Design, Prentice-Hall, 1975.
5
Abstraccin procedimental
y de datos
Sumario
5.1. INTRODUCCIN
Como ya hemos comentado en captulos anteriores los avances tecnolgicos de los aos sesenta del siglo
XX hicieron posible abordar el desarrollo de programas cada vez ms complejos. Con el objetivo de que
los programas fueran ms fciles de disear, codificar y probar surgi la programacin estructurada, as
como otra tcnica clsica relacionada con el tratamiento de la complejidad: la programacin modular.
Esta ltima se centra en la descomposicin de un problema complejo en subproblemas ms pequeos
que se puedan resolver por separado. De esta forma slo hay que tratar en cada momento con problemas
sencillos y manejables resueltos por medio de bloques de cdigo independientes. Estos bloques se deno-
minan mdulos y en este captulo vamos a presentar, en primer lugar, el concepto de programacin
modular. Una vez construido un mdulo para resolver una tarea, se puede usar sin ms que conocer la
funcin que realiza y la informacin que hay que suministrarle para trabajar. De esta forma se alcanza
una abstraccin procedimental, de tal manera que los detalles internos (cdigo) del mdulo se vuelven
innecesarios para utilizarlo. ntimamente relacionada con la abstraccin procedimental est la abstrac-
cin de datos, que se considerar en la segunda parte del captulo. As, se presentarn los tipos abstrac-
tos de datos y, a travs de ellos, se introducirn los conceptos de clase y objeto. Como ilustracin se
expondrn tres tipos de clases predefinidas en Java: las cadenas, las matrices y las clases contenedoras.
1
Esta afirmacin merece matizacin. Considerar que la solucin de los subproblemas nos da la solucin del problema
global es una simplificacin reduccionista. Esto es equivalente a suponer que el todo es la suma de las partes. En muchos casos
esto es una muy buena aproximacin. Sin embargo, siempre debemos tener en cuenta el efecto de la interaccin entre las par-
tes, lo que supone un factor adicional a tener en cuenta.
Abstraccin procedimental y de datos 107
Bloque de
sentencias
Programa
principal
Estos subprogramas generalizan la nocin de un operador. Con los subprogramas se pueden defi-
nir las operaciones necesarias para un trabajo sobre operandos que no tienen por qu ser tipos primi-
tivos. Otra ventaja es que de esta manera se puede encapsular, aislar, un algoritmo, colocando como
una unidad todas las sentencias relevantes para una parte concreta del programa (Aho et al., 1987).
Una ventaja de la encapsulacin es que de esta forma sabemos dnde acceder para realizar cambios en
los algoritmos. Siempre lo haremos en esas secciones encapsuladas del problema. Por lo tanto, los pro-
gramas son ms fciles de probar y mantener.
La creacin de estos subprogramas implica la realizacin de una autntica abstraccin procedimental.
El subprograma realiza una tarea y una vez programado lo podemos usar tantas veces como haga falta,
sin ms que conocer qu datos necesita para trabajar y qu resultado produce. En otras palabras, despus
de crearlo nos podemos abstraer totalmente de su implementacin, slo necesitamos saber cmo se usa.
Otra ventaja de los subprogramas es que nos evitan repetir el mismo cdigo mltiples veces. Ima-
ginemos un subprograma que ordena una serie de nmeros. Si en un problema determinado necesita-
mos ordenar cinco veces, resuelto con un programa monoltico tenemos que repetir cinco veces el
mismo cdigo. En un programa modular haramos un nico subprograma para ordenar y despus lo
llamaramos, invocaramos, cinco veces sin necesidad de repetir el cdigo.
Estos bloques de cdigo o subprogramas reciben distintas denominaciones en los diferentes len-
guajes. En particular, en los lenguajes no orientados a objetos, la divisin tradicional distingue entre:
a) Subprogramas, subrutinas o procedimientos, si pueden devolver ms de un valor.
b) Funciones, si devuelven un nico valor a travs de su identificador (como las funciones
matemticas).
En los lenguajes orientados a objetos los dos conceptos se funden dando lugar a los procedimien-
tos (o mtodos) que pueden aplicar los objetos. Dada la orientacin a objetos de este texto, hablare-
mos aqu exclusivamente de mtodos. Los mtodos 2 siempre realizan, aslan una tarea concreta. Una
definicin genrica de mtodo sera:
2
En orientacin a objetos, de forma genrica se usa ms el trmino procedimiento que mtodo. Aqu, por claridad, usa-
remos mtodo ya que es el trmino utilizado en Java.
108 Introduccin a la programacin con orientacin a objetos
En orientacin a objetos, un mtodo se asocia con una clase particular. Cada mtodo contiene el
cdigo que se ejecutar cuando el mtodo se invoque. Cuando se llama a un mtodo, el flujo de con-
trol del programa se transfiere al mtodo y se ejecutan una a una las sentencias del mismo. Cuando se
ha ejecutado el mtodo, el control se devuelve a la localizacin desde donde se hizo la llamada y el
programa contina en la siguiente sentencia, vase la Figura 5.2.
Como podemos ver, un mtodo siempre debe ser invocado desde algn punto (otro mtodo). Lgi-
camente debe haber un mtodo especial que arranque cuando el programa comience su ejecucin. En
Java ste es el mtodo main (principal). El mtodo main comienza a ejecutarse cuando se pone a fun-
cionar el programa y lgicamente no necesita ser invocado desde dentro del propio programa. Es el
sistema operativo, en ltima instancia, quien invoca al mtodo main. Es importante resaltar que un
mtodo puede invocar a su vez a otros mtodos.
Un mtodo acepta como entrada una serie de parmetros que le pasa el mtodo que lo invoca y
puede devolver un valor a travs de su identificador. Este valor puede ser de un tipo primitivo o un
objeto. Si no devuelve nada, se indica con la palabra reservada void. En Java, la sintaxis de un mto-
do es:
nombre_objeto.mtodo(parmetros);
Esta forma de actuar es la que se utilizaba en captulos previos cuando, por ejemplo, se invocaba
al mtodo readLine() para leer un dato introducido por el teclado. Sin embargo, cuando usemos un
mtodo dentro de la propia clase en la que est definido no hace falta poner el nombre de un objeto,
slo invocar el mtodo.
Sentencia 1; Sentencia 1;
Sentencia 2; Sentencia 2;
Ejecutar mtodo; .
Sentencia 3; .
. .
. Sentencia n;
.
Sentencia m;
Mtodo
Programa principal
Los mtodos pueden devolver un valor. Por esta razn, es necesario decir de qu tipo es el valor
que se devuelve al ejecutar un mtodo (puede no devolver ninguno). Esto se indica en la cabecera del
mtodo. Por ejemplo en el caso siguiente,
se indica que el mtodo factorial va a devover un entero de tipo int. Si por el contrario el mto-
do no devolviera ningn valor, el tipo de retorno sera void y la cabecera del mtodo sera:
A su vez, dentro del mtodo debe indicarse qu es lo que ste devuelve, como por ejemplo el
valor de una variable que almacena el resultado final de un algoritmo. Esto se consigue por medio
de la sentencia return. La sentencia return indica lo que se devuelve y puede tomar una de dos
formas,
return;
o
return expresin;
En el primer caso no se devuelve ningn valor, por lo que debe usarse la palabra reservada void
como tipo_a_devolver en la cabecera del mtodo (tipo void). En el segundo, se devuelve el resul-
tado de la expresin. Siempre se debe especificar un tipo en la cabecera del mtodo (void o el del
valor devuelto). La sentencia return hace que el control se devuelva inmediatamente al punto en el
que se invoc el mtodo. Si no hay sentencia return, el proceso contina hasta que se alcanza el final
del mtodo y entonces se devuelve el control al mdulo invocante. Si hay una sentencia return, el
proceso se acaba cuando se ejecuta la sentencia return. Lo normal es que slo haya una sentencia
return al final del mtodo, aunque en casos excepcionales podramos encontrar varias.
Vamos a ver un ejemplo de uso de mtodos con una variante del Ejercicio 5 propuesto del Cap-
tulo 3, que calculaba el permetro y la superficie de un crculo, dado un radio. Vamos a modularizar
el problema definiendo dos mtodos para estas dos tareas (vase el Programa 5.1).
Programa 5.1. Demostracin del uso de mtodos. Clculo del rea y del permetro de un
crculo
class Circulo {
public static void main(String[] args) {
double radio, perimetro, superficie;
// Leyendo el radio
radio=Double.parseDouble(args[0]);
System.out.println(El radio es: +radio+ unidades);
// Salida de resultados
System.out.println(El perimetro es: +perimetro
110 Introduccin a la programacin con orientacin a objetos
Programa 5.1. Demostracin del uso de mtodos. Clculo del rea y del permetro de un
crculo (continuacin)
+ unidades);
System.out.println(La superficie es: +superficie
+ unidades^2);
} // Fin de la clase
En el mtodo main hay dos llamadas a otros mtodos, concretamente al mtodo calcular_perime-
tro y calcular_superficie. Como ambos devuelven un valor, en el mtodo main se han declarado
dos variables llamadas perimetro y superficie utilizadas para recoger el resultado que devuelve cada
mtodo. Si no devolvieran nada, no hara falta asignar la salida del mtodo a una variable, bastara con
invocar al mtodo directamente. Lgicamente, las variables que reciben la salida de un mtodo deben
ser del mismo tipo que el tipo de dato que devuelve el mtodo. En el ejemplo anterior, los dos mto-
dos invocados reciben un parmetro (radio en el mtodo main) cuyo nombre en los mtodos es
dato_radio. Los mtodos realizan su labor y devuelven el resultado al mtodo main. Es conveniente
recordar que al ser declarados ambos mtodos como static no es necesario crear un objeto para invo-
carlos. Indiquemos por ltimo que para el clculo del rea se necesita el valor de la constante . En
Java dicha constante es pblica y est definida en la clase predefinida Math. Por tanto, para usar dicho
valor no tenemos ms que escribir Math.PI 3.
La estructura del programa en funcin de sus mdulos se puede representar con los denominados
diagramas de estructura. Los diagramas de estructura son herramientas tpicas del denominado diseo
estructurado y forman parte de una completa metodologa de trabajo. Estos diagramas son una repre-
sentacin en forma de rbol genealgico de la estructura del software, donde los diferentes mdulos
se representan como cajas rectangulares. La relacin de invocaciones de unos mtodos a otros se repre-
senta en estos diagramas por medio de flechas. Para mayor informacin se remite al lector a textos ms
especializados en ingeniera del software o diseo estructurado, vase (Mynatt, 1990; Martin y McClu-
re, 1988). Como ilustracin, en la Figura 5.3 se muestra el programa anterior en forma de diagrama de
estructura. Slo se recoge el simbolismo bsico de estos diagramas, con los mdulos representados por
bloques conectados definiendo una relacin jerrquica. El sentido de las flechas representa el orden de
invocacin de los mdulos.
En general cada mdulo de cdigo debera realizar una sola y concreta tarea. Es decir, todos
3
De acuerdo a la filosofa de orientacin a objetos, nunca debemos acceder directamente a los datos de un objeto. El
mismo efecto se puede obtener usando los denominados mtodos de consulta o retorno que devuelven los valores de dichos
datos. Toda la interaccin con un objeto debe realizarse a travs de sus mtodos y no directamente a travs de sus datos. Sin
embargo, en el caso de constantes (al no poder ser modificadas) a veces se relaja esta recomendacin.
Abstraccin procedimental y de datos 111
Programa Principal
Calcular_permetro Calcular_superficie
a) Variables locales
Las variables locales slo existen en un mbito determinado del programa, por ejemplo en un subpro-
grama o en un bloque de sentencias. En Java, ya hemos indicado que las variables slo son accesibles
dentro del bloque de cdigo en el que estn definidas. Segn esta regla, las variables declaradas en los
mtodos son locales al mtodo en el que se han declarado, no conocindose fuera de l. As, si dentro
de un mtodo declaramos una variable con el mismo nombre que otra de otro mtodo no hay ningn
problema pues las variables son distintas, correspondiendo cada una a reas de memoria diferentes.
b) Variables globales
Por otro lado, las variables globales son las que son accesibles desde cualquier punto del programa y
se pueden usar desde cualquier mdulo o subprograma. Esto conlleva la posibilidad de posibles efec-
tos colaterales 4 indeseados, pues la variable puede usarse en cualquier parte del programa (su alcance
engloba todo el programa) y su valor se puede alterar incontroladamente. Si posteriormente es nece-
sario usar la variable en otra parte del programa con su valor original, tendremos un error. El punto
donde se da el error es fcil de localizar, pero no lo es tanto el origen del mismo (la modificacin ori-
ginal de la variable). Este tipo de efectos colaterales produce errores cuyo origen es difcil de trazar y
4
El concepto de efecto colateral (en ingls side effect) es ubicuo en programacin y va asociado al tipo de problemas
engendrado por variables globales.
112 Introduccin a la programacin con orientacin a objetos
class Global {
static int global=1; // Variable global y static porque no se
// crea ningn objeto
Por la misma razn que algunos mtodos se declaran static, las variables globales a nivel de la
clase que contiene el mtodo main deben ser static. En el ejemplo anterior, los dos mtodos tienen
acceso a la variable global e imprimiran el mismo valor: 1.
Para finalizar, indiquemos que en programacin orientada a objetos se recomienda que la clase que
lleva el mtodo main tenga tan pocos mtodos como sea posible, idealmente slo el mtodo main.
Lgicamente, en los ejemplos que vamos a considerar hasta empezar a crear ms de una clase, en el
Captulo 7, se va a relajar esta recomendacin. Sin embargo, se retomar cuando se exponga la pro-
gramacin orientada a objetos.
5
Estrictamente hablando esto no son variables globales, pues su alcance est limitado a la clase en la que estn defini-
das, y un programa real en orientacin a objetos consta de ms de una clase.
6
La denominacin de actual es una errnea traduccin del ingls donde estos parmetros se denominan actual parame-
ters, literalmente parmetros reales, no actuales.
Abstraccin procedimental y de datos 113
pueden ser literales, variables o expresiones que se evalan, y cuyo resultado es el que se pasa al
correspondiente parmetro formal. Por ejemplo, en el Programa 5.1, radio es el parmetro actual
ya que es el parmetro que se usa al invocar a los mtodos. El parmetro formal es dato_radio
que es el que aparece en la cabecera de cada mtodo. En este caso el parmetro formal y el actual tie-
nen distinto nombre, pero no pasara nada si los nombres fueran iguales. Es importante tener en cuen-
ta que los parmetros actuales y formales que se corresponden por posicin deben ser del mismo tipo,
y que debe haber el mismo nmero de parmetros actuales que formales. Por ejemplo, en la Figura 5.4
tiene que haber tres parmetros formales y tres actuales, y el tipo de actual_1, debe ser el mismo de
formal_1, el de actual_2 igual al de formal_2, y el de actual_3 igual al de formal_3.
En relacin con los parmetros, una cuestin clsica, que es necesario conocer en cualquier lengua-
je, es cmo se realiza el paso de los parmetros actuales a los formales. ste es el problema del paso de
parmetros. Hay dos posibles formas, por valor o por referencia. Consideremos cada una por separado.
Parmetro Zona de
actual memoria
Parmetro
formal
Figura 5.5. En un paso por referencia el parmetro formal y el actual devienen en sinnimos
de la misma zona de memoria donde se almacena el dato
114 Introduccin a la programacin con orientacin a objetos
donde se almacena dicho valor, vase la Figura 5.5. Por esa razn, al trabajar dentro del mtodo con
la entidad pasada por referencia estamos manipulando el mismo valor que se utiliza fuera. A efectos
prcticos, si hacemos una modificacin de ese valor dentro del mtodo, la modificacin se mantiene
al salir del mismo. Obsrvese la diferencia con el paso por valor, donde el mtodo maneja una copia
del valor original, por lo que las modificaciones realizadas dentro del mtodo no afectan a la variable
externa (que de hecho es otra distinta). En Java, los objetos se pasan por referencia, es decir, que el
parmetro formal deviene en alias (sinnimo) del mismo objeto. Veremos un ejemplo cuando acabe-
mos la parte de matrices, puesto que stas, al ser objetos, se pasan por referencia.
tenemos cuatro mtodos con el mismo identificador (nombre) pero distinta firma, hay por lo tanto
sobrecarga. Sin embargo, en los dos mtodos siguientes,
obtendramos un error de compilacin indicando que un mtodo no puede ser redefinido con un tipo
de retorno diferente (int en el primer caso y double en el segundo).
El compilador usa la firma del mtodo para enlazar la invocacin del mtodo con la definicin
apropiada. Para usar distintos mtodos con el mismo nombre la firma debe ser distinta. Esta tcnica es
til cuando se necesita hacer operaciones similares (la misma tarea) sobre diferentes tipos de datos. Un
ejemplo tpico de sobrecarga de mtodos que trataremos ms adelante es el uso de varios mtodos
constructores sobrecargados para inicializar de forma diferente un objeto cuando ste se crea 7.
7
En algunos lenguajes (por ejemplo Fortran 90) existe tambin la sobrecarga de operadores. As, es posible controlar el
efecto de un operador segn acte sobre un tipo de dato u otro. Java no permite al usuario la sobrecarga de operadores.
Abstraccin procedimental y de datos 115
Figura 5.6. Representacin grfica de la relacin entre los tipos primitivos, las estructuras y
los tipos abstractos de datos
8
En la literatura en castellano se encuentra frecuentemente el trmino instancia por ejemplar. sta es una mala tra-
duccin del trmino ingls instance que se puede traducir como ejemplar perteneciente a una clase o grupo.
116 Introduccin a la programacin con orientacin a objetos
Ejemplares de la clase A
Datos (Propiedades)
Procedimientos (Mtodos)
Objeto 3 (de la clase A)
raciones que se pueden realizar sobre ellos. En la jerga del campo se dice que los objetos encapsulan
datos y procedimientos (mtodos). Es importante recalcar que los objetos se definen a travs de clases.
La clase es el modelo de los objetos de ese tipo, es como haber definido un nuevo tipo de datos. Una vez
que existe la clase se pueden declarar tantos objetos de esa clase como se necesiten, vase la Figura 5.7.
El programador debe construir las clases que necesite, indicando cules son los datos (propie-
dades) que le corresponden y los mtodos (procedimientos) que quiere aplicar a esos datos. Lue-
go, en el programa se crearn ejemplares (objetos) de esa clase y se usarn llamando a los
procedimientos que se han incorporado a la clase. Veamos un ejemplo que ayude a concretar los
conceptos tericos vistos hasta ahora. Definamos genricamente las propiedades y los procedi-
mientos de una clase automvil usada para definir las caractersticas de los automviles distribui-
dos por un concesionario. Supongamos que el concesionario slo necesita conocer la cilindrada,
potencia, modelo, tipo de motor, precio y disponibilidad en el almacn de cada tipo de coche. A su
vez, supongamos que lo que se necesita que el programa haga sea imprimir los datos del coche,
actualizar su precio e indicar si est disponible o no en ese momento. Podramos organizar una cla-
se Automvil de la forma siguiente.
Nombre clase:
Automvil
Propiedades:
Cilindrada
Potencia
Modelo
Tipo_de_motor
Precio
Disponibilidad
Procedimientos:
Imprimir_datos
Actualizar_precio
Determinar_disponibilidad
class Automovil{
//Caractersticas
Abstraccin procedimental y de datos 117
//Mtodos
public void imprimir_datos( ){
System.out.println(El modelo del coche es:+ modelo);
System.out.println(El precio del coche es :+ precio);
}
public void actualizar_ precio( double nuevo_ precio){
precio=nuevo_ precio;
}
public boolean determinar_disponibilidad( ){
return disponibilidad;
}
}//Fin de la clase
Las propiedades y los procedimientos que uno decide incorporar cuando crea una clase dependen
de lo que se persigue con el programa. En el ejemplo anterior el contenido de la clase corresponde a
la descripcin realizada del problema del concesionario. Si el programa fuera un juego de carreras de
coches, la clase no sera igual. Por ejemplo, tendramos mtodos como acelerar o frenar.
Al igual que es necesario declarar una variable indicando su tipo para poder usarla, debemos crear
los objetos indicando la clase antes de usarlos. Para crear un objeto la sintaxis en Java es:
El mtodo constructor es un mtodo especial que tiene el mismo nombre que la clase. Por ejem-
plo, un constructor de la clase Automovil tendra la siguiente estructura:
Automovil(){
bloque de sentencias
}
Para crear un objeto de la clase Automovil tendramos que escribir el siguiente cdigo:
donde coche1 es el nombre del objeto que acabamos de crear. Hemos supuesto que el constructor de
la clase Automovil no tiene ningn parmetro. Veamos otro ejemplo. Suponiendo que existe una cla-
se llamada Pieza_de_ajedrez, creemos un objeto llamado caballo:
coche1.actualizar_ precio(precio);
donde precio sera la variable que contendra el valor del nuevo precio.
Un concepto relacionado con el de objeto es el de herencia. Unos objetos se pueden construir a
partir de otros. De esta forma podemos aprovechar las propiedades y procedimientos de clases ya exis-
tentes aadiendo slo las propiedades y procedimientos especficos de la nueva clase. Supongamos
que tenemos la clase Vehculo (coches, aviones, trenes) que contiene las caractersticas comunes a
todos los vehculos, por ejemplo: velocidad mxima. Podemos usar la clase Vehculo para crear o
derivar la clase Automvil. As, Automvil contendra todas las caractersticas de un vehculo gen-
rico y nosotros podramos aadir los detalles especficos que hacen nico al automvil frente a los
otros vehculos. Usando una clase para formar la base de la definicin de otra clase, herencia, se pro-
mueve la reutilizabilidad. sta consiste en la utilizacin de bloques de software (los mismos bloques)
en distintos desarrollos, como si fueran piezas intercambiables. De esta manera se agiliza el proceso
de desarrollo de software al poder aprovechar trabajo ya realizado. Objetos, clases y herencia son tres
de los conceptos bsicos de la programacin orientada a objetos. Todos estos elementos se explicarn
en detalle en captulos posteriores. El lector interesado puede encontrar informacin ms detallada
sobre estructuras de datos y Java en (Weiss, 1998).
Aparte de definir clases nuevas, en un lenguaje orientado a objetos encontramos algunas clases
predefinidas. En este captulo vamos a considerar algunas de las que Java posee, tales como la clase
cadena (String), la clase matriz o las clases contenedoras.
Sin embargo, el uso de las cadenas es tan habitual que existe una forma abreviada de hacer lo mis-
mo que recuerda la declaracin de un tipo primitivo:
Una vez que un objeto String contiene un valor no se puede modificar (acortar, alargar o cam-
biar caracteres). Decimos que un objeto String es inmutable. Sin embargo, lo que s se puede
hacer es usar mtodos de la clase String que pueden devolver nuevas cadenas, resultado de modi-
ficar la cadena original. Para usar algunos de estos mtodos es necesario saber que en Java un
carcter en una cadena se refiere por su posicin o ndice en la cadena. El ndice del primer carc-
ter es el 0. Ejemplo:
Mtodo Significado
length() Devuelve la longitud de la cadena en caracteres
indexOf(carcter) Devuelve la posicin de la primera ocurrencia de carcter
lastIndexOf(carcter) Devuelve la posicin de la ltima ocurrencia de carcter
charAt(N1) Devuelve el carcter que est en la posicin N1
subString(N1, N2) Devuelve la subcadena comprendida entre las posiciones N1 y (N2-1)
toUpperCase() Devuelve la cadena convertida a maysculas
toLowerCase() Devuelve la cadena convertida a minsculas
equals(cadena) Compara dos cadenas y devuelve verdadero si son iguales
equalsIgnoreCase Como equals pero sin considerar maysculas y minsculas
(cadena)
valueOf(N1) Convierte el nmero N1 a cadena
Con esto podemos entender el funcionamiento de algunos mtodos tiles de la clase String,
como los recogidos en la Tabla 5.1.
En el Programa 5.3 se expone el uso de algunos de los mtodos.
class Cadenas {
public static void main(String[] args) {
String C1=Cadena 1 ;
String C2=Cadena 1 ; //Tiene un blanco al final
int N1, N2;
N1=C1.length();
N2=C2.length();
// Comparacin de cadenas
if (C1.equals(C2)) {
System.out.println(Las dos cadenas son iguales);
}
else {
System.out.println(Las dos cadenas no son iguales);
}
System.out.println(Conversion: +C2.valueOf(420.37));
System.out.println(C2: +C2);
N1=342;
System.out.println(Conversion: +C2.valueOf(N1));
}
}
Longitud cadena 1: 8
Longitud cadena 2: 9
Caracter en posicion 3 (cuarto): e
Las dos cadenas no son iguales
Conversion: 420.37
C2: Cadena 1
Conversion: 342
Observemos que las dos cadenas no son iguales, pues la cadena C2 tiene un blanco al final. El blan-
co es un carcter como cualquier otro, con su correspondiente cdigo Unicode y el sistema lo maneja
como tal.
Las cadenas se pueden unir, concatenar, usando el operador +. El resultado es una nueva cadena
unin de las concatenadas. Algunos operadores de asignacin hacen funciones particulares depen-
diendo de los tipos de operandos, como en sus contrapartidas normales (sin el =). En particular, si los
operandos del operador += son cadenas, entonces tambin se hace una concatenacin de cadenas.
9
Dada la existencia en Java de una clase Vector, para evitar confusin denominaremos en este texto a las matrices y
vectores ordinarios como matrices o arrays.
10
El trmino ingls es array que significa disposicin determinada. En espaol el trmino matriz se generaliz a partir
de los lenguajes, que como Fortran, usaban esta estructura de datos para representar matrices matemticas. Es tambin habi-
tual el uso del trmino tabla. Aqu usaremos el trmino matriz. En espaol americano es muy frecuente encontrar el trmino
arreglo.
Abstraccin procedimental y de datos 121
define por uno o varios ndices. Dichos ndices nos permiten hacer referencia a un elemento con-
creto. En funcin del nmero de ndices tendremos una estructura mono o multidimensional. Por
ejemplo,
aijk, i=0,N
j=0,M ---- caso tridimensional
k=0,L
Como ya hemos indicado, las matrices son, en los lenguajes de programacin, estructuras que se
usan para agrupar y organizar datos. Son estructuras simples pero muy potentes. Cuando escribimos
un programa que maneja una gran cantidad de informacin, tal como una lista de 100 valores enteros,
no es conveniente declarar variables separadas para cada elemento de datos. Si lo hiciramos, estara-
mos obligados a manejar las 100 variables independientemente. Las matrices resuelven este problema
permitiendo declarar una entidad (una sola) que puede contener muchos valores. La gran ventaja es
que la matriz se maneja como una unidad.
Como hemos visto, una matriz es una lista organizada de valores. Cada valor se almacena en una
posicin especfica y numerada de la matriz. El nmero correspondiente a cada posicin viene dado
por un ndice. En programacin, dependiendo del lenguaje, los ndices de las matrices pueden empe-
zar desde dos orgenes:
a) En cero (0-origen).
b) En uno (1-origen).
En Java, en particular, los ndices de las matrices empiezan en cero, igual que suceda con la nume-
racin de los caracteres de una cadena alfanumrica. Esto implica que si una matriz es de tamao N
los ndices van desde 0 a N-1. Por ejemplo,
Elementos: 4 5 6 7 3 2 0
ndice : 0 1 2 3 4 5 6
En Java las matrices son objetos. Los elementos que almacenan pueden ser de cualquier tipo como
int, double, etc., e incluso objetos, pero todos los elementos tienen que ser del mismo tipo. La sin-
taxis para declaracin de una matriz monodimensional en Java es:
donde dimensin_matriz puede ser un valor literal o una variable. Pongamos un ejemplo. La decla-
racin de una matriz de enteros llamada resultados con 20 posiciones se hara de la forma siguien-
te:
tipo[] nombre_matriz;
nombre_matriz =new tipo [dimensin_matriz];
122 Introduccin a la programacin con orientacin a objetos
Sin embargo, es ms legible el primer formato por comparacin con otras declaraciones. Por ejem-
plo, con la sentencia,
int n1,n2,n3;
declaramos 3 objetos de clase (tipo) matriz de enteros. En los dos casos los tipos se aplican a todas las
variables de la declaracin particular. Sin embargo, la segunda alternativa para las matrices, puede lle-
varnos a situaciones confusas. Por ejemplo, la declaracin:
int matriz1[], matriz2, matriz3[];
declara matriz1 y matriz3 de tipo matriz y matriz2 de tipo entero, es decir, que en una misma
declaracin declaramos entidades de distintos tipos. Ha cometido el programador un error? Por qu
lo ha hecho as? Esas dudas las evitamos usando la primera alternativa.
Una vez que existe la matriz podemos hacer referencia a sus elementos. Esto se hace usando el
ndice entre corchetes. Por ejemplo, recordando que los ndices de las matrices empiezan en cero, para
referirnos al cuarto elemento de la matriz monodimensional 11 resultados haramos:
resultados [3]
El Programa 5.4 muestra un ejemplo de manejo de una matriz usando bucles para recorrerla (es la
tcnica habitual).
class Matrices_1 {
public static void main(String [] args) {
final int LIMITE=5;
// Inicializando la matriz
// Escribiendo la matriz
11
Las matrices monodimensionales se suelen denominar vectores. Para evitar confusin, aqu no usaremos esta nomen-
clatura pues en Java existe una clase Vector, con sus propiedades especficas.
Abstraccin procedimental y de datos 123
} // Fin mtodo
} // Fin clase Matrices
Creamos una matriz que puede contener un mximo de 5 elementos, inicializamos esos cinco ele-
mentos con unos valores y luego los escribimos. Fijmonos en que la dimensin se establece con una
constante. As, para cambiar la dimensin basta con cambiar la constante. Otro punto a comentar es
que puesto que en Java las matrices son 0-origen, en los bucles for el contador no debe pasar del valor
LIMITE-1. Por eso la condicin en los for es i menor que LIMITE y no menor o igual. El ejemplo
ilustra un proceso muy habitual, el recorrido de una matriz. El resultado del Programa 5.4 es,
0 10 20 30 40
No debemos hacer nunca referencia a un elemento cuyo ndice est fuera del intervalo de la matriz.
Si dimensionamos una matriz a 10 elementos, slo podremos referirnos a los elementos cuyos ndices
estn comprendidos entre 0 y 9. Si nos referimos al elemento de la posicin 10 estamos fuera de los
lmites de la matriz y pueden pasar dos cosas:
a) Que el lenguaje que usemos no genere error con lo que el resultado es indefinido.
b) Que el lenguaje detecte el error, como es el caso de Java. El operador [] de Java hace un con-
trol de lmites automtico, produciendo un error si el ndice que usamos est fuera del inter-
valo para el cual se ha declarado la matriz. En este caso, se lanza una excepcin
(ArrayOutofBoundsException) que es posible capturar para actuar en consecuencia.
Si las dimensiones se especifican en tiempo de compilacin, como en el caso anterior, se reserva la
memoria necesaria para almacenar toda la matriz, aunque en el programa no se usen todos sus elementos.
Por otro lado, en estas circunstancias tendremos que conocer el nmero mximo de elementos que pue-
den aparecer en condiciones normales y dimensionar la matriz a ese valor. Esta forma de dimensionar en
tiempo de compilacin se denomina dimensionamiento esttico de matrices. Sin embargo, en Java, al igual
que en muchos lenguajes modernos, la memoria necesaria para cada matriz se puede reservar dinmica-
mente. Esto quiere decir que se puede reservar memoria en tiempo de ejecucin, no en tiempo de compi-
lacin, por lo que es posible asignar una u otra dimensin en funcin de una variable leda. En este caso
tenemos el dimensionamiento dinmico de matrices. A pesar de esto, las matrices son de longitud fija. Una
vez creadas (asignada una cantidad de memoria) no se puede modificar su tamao. En el Programa 5.5 se
presenta una variante del Programa 5.4 en la que la dimensin de la matriz se lee desde el teclado.
Programa 5.5. Ejemplo de manejo de una matriz donde su dimensin se lee desde teclado
import java.io.*;
class Matrices {
public static void main(String [] args) throws IOException {
int limite;
BufferedReader leer =new BufferedReader
(new InputStreamReader(System.in));
// Lectura del tamao de la matriz
} // Fin mtodo
} // Fin clase
En el ejemplo se observa que cuando se recorre la matriz con el bucle for, la condicin de finali-
zacin es que la variable contadora, i, llegue hasta el valor limite-1. Esto es lgico, ya que la varia-
ble i representa el ndice y si tenemos un nmero N de elementos, al empezar el ndice en 0, el ltimo
valor ser N-1. En el ejemplo anterior hemos usado un solo bucle para inicializar la matriz y escribir
el resultado.
En Java podemos conocer en cualquier momento la dimensin de una matriz (el nmero de ele-
mentos que tiene) usando la constante pblica length de la clase matriz. Para ello bastara indicar
nombre_matriz.length. As, por ejemplo, para conocer la longitud de la matriz lista del ejem-
plo anterior y salvarlo en una variable entera llamada dimension, haramos
dimension = lista.length;
Por otro lado, en Java se puede usar una lista para crear e inicializar una matriz. En este caso la
sintaxis es:
donde lista es una serie de valores separados por comas. Por ejemplo:
As, creamos la matriz valores con 5 elementos. Es decir, en la inicializacin por lista, la matriz
se dimensiona al nmero de elementos en la lista. Fijmonos que al crear la matriz usando una lista no
hace falta el operador new.
Una vez introducidas las matrices monodimensionales, podemos entender la cabecera del mtodo
main. Por defecto, en el mtodo main debemos especificar una matriz de clase cadena (String). Es
lo que encontramos habitualmente en la cabecera como: String [] args,
Los elementos de la matriz args recogen las cadenas de caracteres que se introduzcan en la lnea
de rdenes al invocar el programa. El elemento 0 recoge la primera, 1 la segunda, etc. El Programa 5.6
muestra un ejemplo.
class Matrices {
public static void main(String [] args) {
int N1, N2;
N1=Integer.parseInt(args[0]);
N2=Integer.parseInt(args[1]);
} // Fin mtodo
} // Fin clase
El resultado sera:
Hasta ahora los ejemplos usados han ilustrado el uso de matrices monodimensionales. Sin embar-
go, es posible trabajar con ms de una dimensin generando matrices multidimensionales. Todos los
lenguajes permiten de una u otra forma el uso de matrices multidimensionales. Tcnicamente hablan-
do Java no las soporta. Sin embargo, de manera prctica s, puesto que una matriz monodimensional
puede tener una matriz como elemento. Por ejemplo, una matriz cuyo tipo de elemento sea matriz de
enteros es, esencialmente, una matriz bidimensional de enteros. Es decir, en Java las matrices multi-
dimensionales son matrices de matrices.
Veamos la sintaxis de la definicin de matrices multidimensionales en Java. Para representar cada
dimensin de la matriz se usan corchetes []. Por ejemplo:
declara dosD en la prctica como una matriz bidimensional de 4 por 5 elementos. Se puede usar una
lista de inicializacin para crear la matriz, donde cada elemento sea a su vez una lista de inicializacin
de una matriz. Como cada matriz es un objeto separado, las longitudes de cada fila podran ser dife-
rentes, como veremos en el siguiente programa. Cada dimensin tiene como ndice inicial cero. sta
es una forma de crear una matriz en la que una de las dimensiones no tenga siempre el mismo nme-
ro de elementos. Como con las matrices monodimensionales, hay que tener cuidado de no salirse de
los lmites de cada dimensin. A tal efecto, es muy til la constante length que contiene el tamao
de cada matriz individual.
4 filas
0 1 Esta fila es una matriz de 1 elemento
1 2 3 Esta fila es una matriz de 2 elementos
2 4 5 6 Esta fila es una matriz de 3 elementos
3 7 8 9 10 Esta fila es una matriz de 4 elementos
Vamos a ver un ejemplo de matriz multidimensional inicializada por lista y donde se usa la cons-
tante length para controlar su recorrido. Empecemos por la inicializacin de la matriz:
126 Introduccin a la programacin con orientacin a objetos
En este caso, tenemos una matriz de matrices. Hay una matriz principal, que siempre es la de las
filas, que tiene 4 filas, y a su vez cada una de las cuatro filas es una matriz de 1, 2, 3 4 elementos,
respectivamente. En forma tabular, lo que tendramos sera,
Veamos el uso de la matriz tabla en el Programa 5.7. Se trata de crear la matriz anterior, reco-
rrerla fila a fila, imprimirla y sumar los elementos de cada fila imprimiendo tambin el resultado.
class Matrices {
public static void main(String [] args) {
int [][] tabla ={{1},{2,3},{4,5,6},{7,8,9,10}};
int suma;
// Imprimiendo la matriz
// tabla.length es el nmero de filas
// tabla[i].length es la longitud de cada fila. Cada fila
// (elemento de la matriz) est compuesta por una matriz
} // Fin mtodo
} // Fin clase
Fijmonos en que la primera dimensin corresponde a las filas y la segunda a las columnas. La
constante length da el nmero de elementos, as que el ndice mximo ser length-1. Por eso, en
el bucle for el lmite se indica con menor que length y no con menor e igual. Tngase en cuenta
que tenemos una matriz (filas) de matrices (columnas). Por eso, en el bucle controlado con la variable
j (la de las columnas) usamos tabla[i].length para saber cuntas columnas hay en cada fila. El
resultado sera 1 para i=0, 2 para i=1, 3 para i=2 y 4 para i=3. La salida del Programa 5.7 sera:
1
2 3
4 5 6
7 8 9 10
Abstraccin procedimental y de datos 127
Fila: 1 Suma: 1
Fila: 2 Suma: 5
Fila: 3 Suma: 15
Fila: 4 Suma: 34
Para acabar el apartado de matrices y una vez vistas stas, podemos ilustrar en la prctica el paso
de parmetros por valor y por referencia. Para ello, preparemos un ejemplo (Programa 5.8) con un
mtodo que acepte unas variables y una matriz como parmetros, y que modifique los valores de estos
elementos. Observaremos la diferencia entre el paso por valor (para las variables de tipo primitivo) y
el paso por referencia (para la matriz que es un objeto).
class Parametros {
public static void main(String [] args) {
int num1;
double num2;
double [] num3 =new double [1];
num1=1;
num2=4.5;
num3 [0]=5.1;
System.out.println(Valores originales);
System.out.println(num1+ + num2+ + num3[0]);
cambiar(num1,num2,num3);
System.out.println(Valores tras los cambios);
System.out.println(num1+ + num2+ + num3[0]);
} // Fin clase
Valores originales
1 4.5 5.1
Valores dentro del metodo
5 7.2 9.1
Valores tras los cambios
1 4.5 9.1
La matriz es un objeto y por lo tanto se pasa por referencia. Por esta razn, lo que se ha cambiado
dentro del mtodo queda cambiado al salir de l. Las variables de tipo primitivo se pasan por valor y
lo que cambiamos dentro del mtodo no afecta a su valor fuera (lo que se pasa al mtodo es una copia
12
En ingls estas clases se denominas wrappers, es decir, envolventes. En castellano se usa el trmino clase envolvente
o contenedora.
128 Introduccin a la programacin con orientacin a objetos
del valor contenido, no una referencia a la zona de la memoria donde se almacena el valor).
Programa 5.9. Ejemplo del uso de las constantes MAX_VALUE y MIN_VALUE de la clase Float
class Ejemplo {
public static void main(String [] args) {
System.out.println(Float.MAX_VALUE);
System.out.println(Float.MIN_VALUE);
}
}
3.4028235E38
1.4E-45
EJERCICIOS PROPUESTOS
Abstraccin procedimental y de datos 129
Ejercicio 1.* Escriba un programa que simule el lanzamiento de un dado con ayu-
da del mtodo random() de la clase Math. Defina un mtodo que
simule el comportamiento del dado.
Ejercicio 2. Como variante del ejercicio anterior escriba un programa que gene-
re combinaciones de la lotera primitiva (6 nmeros enteros elegidos
al azar en el intervalo de 1 a 49).
Ejercicio 4.* Escriba un programa que posea dos mtodos con el mismo nombre
para determinar el mximo de dos o de tres nmeros introducidos
como argumentos en la lnea de rdenes.
n
1 2
c (n,k) 5 n!/(k! (n-k)! ) 5
con k # n
class Ejercicio {
public static void main(String [] args) {
int [] a={0,1,2};
int [] b=a;
b[1]=3;
a=metodo1(a ,b);
for (int i=0; i<a.length;i++)
System.out.print(a[i]+ +b[i]+ );
}//fin main
class Ejercicio {
metodo1(Valores1 [4]);
metodo1(Valores2 [2]);
metodo2(Valores2);
Valores1=metodo3(Valores2);
System.out.print(Valores1[0]);
System.out.print( +Valores1[i]);
}
Abstraccin procedimental y de datos 131
Ejercicio 9.* La matriz transpuesta de una dada se define como aquella que inter-
cambia filas por columnas (el elemento (i, j) pasa a ser el (j, i)). Escri-
ba un mtodo que transponga una matriz cuadrada.
E 0
p
sen (u) d u
Ejercicio 11. Se trata de una variante modular del Ejercicio 12 del Captulo 4.
Escriba un programa que use dos mdulos para calcular la media
aritmtica y la suma de una serie de valores introducidos por tecla-
do. Sugerencia: Almacene los valores, segn se vayan leyendo, en
una matriz y psela como parmetro a los mdulos.
Ejercicio 12.* Sea el siguiente algoritmo propuesto por Euclides en sus Elementos,
libro sptimo, para determinar el mximo comn divisor de dos
enteros, n y m, tal que n<m:
REFERENCIAS
AHO, A. V., HOPCROFT, J. E. y ULLMAN, J. D.: Data Structures and Algorithms, Addison-Wesley, 1987.
MARTIN, J. y MCCLURE, C.: Structured Techniques. The Basis of CASE, Prentice-Hall, 1988.
MYNATT, B. T.: Software Engineering with Student Project Guidance, Prentice-Hall, 1990.
SMITH, H. E.: Data Structures. Form and Function, Harcourt Brace Jovanovich, Publishers, 1987.
WEISS, M. A.: Data Structures and Problem Solving Using Java, Addison-Wesley, 1998.
6
Recursividad
Sumario
6.1. INTRODUCCIN
La recursividad o recursin es una tcnica muy potente en la que un mtodo 1 est parcialmente defi-
nido en trminos de s mismo. Esto parece una contradiccin si recordamos el aforismo de que el con-
cepto definido no debe formar parte de la definicin, para evitar situaciones como la del siguiente
ejemplo. Adorno: aditamento para adornar algo. La idea de recursividad transmite cierta sensacin
cclica, que en trminos prcticos se transformara en una definicin tautolgica como a=a. Como
veremos, la definicin recursiva no es tautolgica ya que siempre debe haber diferencia entre el mto-
do que estamos definiendo y la versin del propio mtodo que se usa en la definicin. Esto evita el
razonamiento circular.
Cuando se estudia por primera vez, la recursividad puede parecer difcil de entender, esotrica.
Esto es as porque a diferencia de otras tcnicas, la recursividad es un concepto no familiar en la
vida diaria que exige una nueva forma de pensar sobre los problemas. Dicho de otra manera, nues-
tra mente no funciona de forma recursiva. La recursividad es una tcnica de resolucin de proble-
mas complejos que se basa en reducir el problema en subproblemas, los cuales tienen la misma
estructura que el original pero son ms fciles de resolver. La recursividad es una poderossima
herramienta de programacin que permite muchas veces obtener algoritmos cortos y eficientes.
Existen muchos problemas cuya solucin ms eficiente es recursiva, aunque siempre se debe tener
cuidado en realizar apropiadamente dicha definicin para evitar la lgica circular y caer en un ciclo
infinito de llamadas.
Como un ejemplo de definicin recursiva tenemos la bsqueda de una palabra en un diccionario.
Abrimos el diccionario por la mitad y si la palabra est en esa hoja el problema est resuelto. Si la pala-
bra est antes, abrimos por la mitad de la seccin anterior a la que tenemos y si la palabra est detrs,
abrimos por la mitad de la seccin siguiente. Ahora lo que habra que hacer es exactamente lo mismo
que antes, es decir, si la palabra est en la hoja actual se acab la bsqueda. Si la palabra no est en la
hoja pero alfabticamente est antes, abrimos en la subseccin anterior del diccionario. Si est des-
pus, abrimos en la subseccin posterior. El proceso continuara hasta encontrar la palabra. Desde un
punto de vista formal, la presentacin de la recursividad debe comenzar en el principio matemtico en
el que se basa: el principio de induccin.
El principio de induccin o induccin matemtica es una tcnica para probar un teorema (Apostol,
1979), normalmente referido a nmeros enteros. El principio se aplica en dos etapas:
1
Una vez ms debe quedar claro que nos referimos a mtodos por coherencia con el lenguaje que estamos usando para
ilustrar las tcnicas: Java. En otros lenguajes hablaramos de funciones, procedimientos o subrutinas.
Recursividad 135
6 i 5 1 1 2 1 ... 1 N
i 5 1
N(N 1 1)
es igual a }}.
2
Prueba por induccin:
N
N(N 1 1)
6 i 5 }}2
i 5 1
N 1 1 N
6 i 5 6 i 1 (N 1 1);
i 5 1 i 5 1
con esto
N
N(N 1 1)
6 i 1 (N 1 1) 5 }}2 1 (N 1 1)
i 5 1
reorganizando
con esto,
N 1 1
(N 1 1)(N 1
6 i 5 }2
i 5 1
6 i 5 1;
i 5 1
1(1 1 1)2
}} 5 }} 5 1
2 2
con lo que se cumple para 1. Por lo tanto por a) tambin se cumple para 2 y al cumplirse para 2 por a)
tambin se cumple para 3, etc. Esto completa la prueba del teorema.
Como acabamos de ver, una prueba por induccin trabaja en dos etapas. Existe una en la que se
demuestra que el teorema se cumple para el caso ms pequeo, sta se denomina caso base. Por otro
lado, demostramos que si el teorema se cumple para un caso dado, se puede extender para incluir el
caso siguiente, sta es la componente inductiva.
a) Hay que incluir al menos un caso base (puede haber ms de uno) que se resuelva sin necesi-
dad de recursividad. El mtodo debe comprobar si debe realizar una nueva llamada recursiva
o si ya se ha alcanzado el caso base.
b) Todas las llamadas recursivas deben llevar hacia el caso base.
Lgicamente el caso base supone el final de las llamadas recursivas. Es la existencia del caso base
y la realizacin de llamadas recursivas que llevan a l lo que evita que entremos en un ciclo infinito
de llamadas. El caso en el que no se alcanza el caso base se denomina recursividad infinita y es un
error de programacin que debe ser evitado.
Para crear un mtodo recursivo es necesaria una definicin recursiva del problema. Como ejemplo
veamos el problema de la suma de los n primeros enteros con n1. Denotemos la funcin suma de los
n enteros como s(n). As, tendramos:
Se puede observar que el problema est definido de forma recursiva, puesto que para conocer la
suma de n nmeros se debe previamente conocer la suma de los n21 enteros anteriores. En el Progra-
ma 6.1 se muestra un ejemplo donde se implementa el resultado anterior usando un mtodo recursivo.
class Recursion {
public static void main(String [] args) {
int n, resultado;
n=Integer.parseInt(args[0]);
resultado= s(n);
System.out.println(Suma de los primeros +n+ enteros:);
System.out.println(resultado);
} // Fin mtodo main
En el Programa 6.1 se lee por la lnea de rdenes el valor n y se invoca al mtodo recursivo s. En
el mtodo s hay un nico caso base que corresponde a n=1. Cuando n es distinto de 1 alcanzamos el
caso inductivo y se vuelve a invocar al mtodo s pero con el argumento n21. Fijmonos en que en un
mtodo recursivo es necesaria una condicin para distinguir el caso base del inductivo.
Este mismo ejemplo nos sirve para ilustrar la robustez de un algoritmo recursivo. Recordemos
que en nuestro contexto robusto significa capacidad de respuesta a fallos. En el ejemplo usado (Pro-
grama 6.1) vemos una posible fuente de problemas. Qu ocurre si n # 0? En este caso no progresa-
mos hacia el caso base y el mtodo fallara. Para corregirlo deberamos comprobar que el nmero es
mayor o igual que uno, pero esto crea un problema, como podemos observar si programamos de la
siguiente forma el mtodo:
Programa 6.2. Uso de un driver para el mtodo recursivo que suma los N primeros nmeros
class Recursion {
public static void main(String [] args) {
int n;
n=Integer.parseInt(args[0]);
suma(n);
} // Fin mtodo main
// Mtodo driver
public static void suma(int n) {
if (n>0) {
System.out.println(Suma de los primeros +n+ enteros:);
System.out.println(s(n));
}
else
System.out.println(El numero no puede ser <1);
} // Fin mtodo suma
} // Fin mtodo s
} // Fin clase
Como vemos, ahora desde el mtodo main se invoca al mtodo suma que se encarga de todo el
proceso de comprobacin y que es el que invoca al mtodo recursivo s.
Como puede observarse en los ejemplos anteriores la sintaxis de un mtodo recursivo es sencilla.
La pregunta, sin embargo, es cmo se implementa la recursividad en el ordenador? En otras palabras,
cmo funciona la recursividad?
2
El concepto de driver es muy comn en programacin. Un driver es un fragmento de cdigo cuya misin es dirigir o
conducir (to drive en ingls) el funcionamiento de otro fragmento de cdigo. Los driver son normalmente mdulos de cdi-
go con entidad propia. En este caso, el driver sera un mtodo al que invocaramos y desde el que se invocara el mtodo recur-
sivo.
Recursividad 139
El factorial de cualquier otro nmero mayor que 0 se puede definir recursivamente como el pro-
ducto del nmero por el factorial del nmero anterior. ste sera el caso inductivo:
class Factorial_recursivo {
public static void main(String [] args) {
int n;
if (n >=0) {
System.out.println(El factorial de +n + es:);
System.out.println(factorial(n));
}
else
System.out.println(No se puede evaluar el factorial
+ si n<0);
} // Fin del main
Se ha usado tipo de retorno long en factorial porque el valor del factorial aumenta muy rpi-
damente y un int se queda corto enseguida.
Para entender cmo funciona la recursividad es necesario tener bien claro que en memoria no va
a existir una sola versin del mtodo recursivo. Cada vez que se invoque el mtodo se crea una nue-
va versin del mismo. La estructura de todas las versiones es la misma, pero no as los datos que con-
140 Introduccin a la programacin con orientacin a objetos
tiene cada una. Con esta idea bsica, analicemos lo que ocurre en el programa. Supongamos que se
invoca el programa Factorial_recursivo con un argumento n=3. Como n en este caso es mayor
que cero se escribira por pantalla la frase El factorial de 3 es: y se llamara al mtodo
recursivo factorial tomando n el valor de 3. Dentro del mtodo se comprueba que n no es igual a 0,
por lo que se realiza otra llamada al mtodo factorial. En este caso el argumento de factorial va
a valer n-1, es decir, 2. Otra vez se realiza la comprobacin n==0 y se vuelve a llamar al mtodo fac-
torial con
n-1, que en esta ocasin equivale a 1. Se vuelve a realizar la misma operacin, y se vuelve a invocar
al mtodo factorial con el valor n-1 que ahora sera 0. Al hacer factorial(0) el mtodo devuel-
ve 1, ya que entra en el caso base, terminando as el ciclo de llamadas recursivas y producindose una
vuelta atrs que va recogiendo los valores que van devolviendo todos los mtodos que han sido invo-
cados. En el diagrama de la Figura 6.1 se ilustra cmo se van realizando las llamadas recursivas (lnea
continua) y como, una vez terminadas stas, se realiza la devolucin de valores (lnea de puntos).
El ejemplo de la Figura 6.1 muestra cmo las llamadas recursivas se van produciendo hasta alcan-
zar el caso base. En ese punto se acaban las llamadas y empiezan las devoluciones de valores hasta lle-
gar al mtodo main, que fue quien hizo la primera invocacin del mtodo. Tenemos un movimiento
en dos sentidos. Primero hacia adelante hasta alcanzar el caso base. Segundo hacia atrs devolviendo
los resultados de cada llamada al mtodo. Las llamadas realizadas implican una estructura de pila.
Organizndolo en forma tabular obtendramos el resultado mostrado en la Tabla 6.1.
Main
n=3 devuelve 2 * 3 = 6
factorial (2) * 3
factorial
n=2 devuelve 1 * 2 = 2
factorial (1) * 2
factorial
n=1 devuelve 1 * 1 = 1
factorial (0) * 1
factorial
n=0 devuelve 1 = factorial (0)
factorial
IDA VUELTA
Llamada n Valor
a
1. 3 6
2.a 2 2
3.a 1 1
4.a (Caso base) 0 1
Recursividad 141
En cada llamada se realiza una copia del mtodo recursivo (hablando de manera correcta del espa-
cio de datos). Esto es importante, porque cada llamada implica una nueva copia de las variables del
mtodo. Esto consume memoria y si tuviramos un problema de recursividad infinita agotaramos la
memoria disponible y obtendramos un error.
Inicio
Recibir valor de la variable
Si (valor ms pequeo)entonces
Caso base
Si_no
Invocar al mtodo con un valor menor al inicial
Fin_Si
Proporcionar resultado
Fin
Caso base
Si entramos en el caso base, entonces no se hacen llamadas recursivas. El caso base suele ser el caso
ms sencillo. Por ejemplo, en el caso del factorial es n=0. En el algoritmo, el caso base devolvera el
resultado correcto para el valor ms pequeo. Este punto es importante, el caso base debe proporcio-
nar el resultado correcto para el caso ms sencillo del problema que estamos considerando.
Hiptesis inductiva
Supongamos que el algoritmo recursivo funciona para todos los valores mayores que el del caso base
(en el caso del factorial n>0). Esto implica que el algoritmo recursivo funcionar correctamente para
cualquier valor (por ejemplo, en el factorial para un valor n). Como el valor suministrado implica la
llamada recursiva, se volver a aplicar el algoritmo recursivo sobre un valor menor que el original (en
el caso del factorial se llama al algoritmo con un valor n-1). Sin embargo, como la parte inductiva
funciona para cualquier valor mayor que el caso base tambin funcionar para el nuevo valor. Si esto
es as, tambin funcionar para el siguiente valor, y el siguiente, etc. hasta llegar al caso base.
Lgicamente, en las condiciones anteriores el principio de induccin nos asegura que la solucin
recursiva para un caso arbitrario, general, es correcta. No es necesario representar la pila de llamadas
que se vayan haciendo para asegurar la validez de un algoritmo recursivo. A pesar de todo, es impor-
tante recalcar que este resultado no nos asegura que el algoritmo recursivo que estemos usando sea el
que resuelve el problema. Por ejemplo, si quiero calcular un factorial y utilizo un algoritmo recursivo
142 Introduccin a la programacin con orientacin a objetos
para sumar n nmeros, no estoy resolviendo el problema que me interesa. Lo importante de la demos-
tracin anterior es que asegura que para disear un algoritmo recursivo que resuelva un problema slo
hay que hacer dos cosas. Primero, identificar el caso base de mi problema, y segundo, identificar cmo
expresar mi problema para un tamao dado (en el ejemplo del factorial para un entero n) como funcin
del mismo problema para un tamao menor (en el factorial n21). Con slo hacer esto el principio de
induccin me asegura que el algoritmo es correcto y que resolver mi problema en el caso general.
ida
mtodo 1 mtodo 2 Llamada recursiva
vuelta
ida
ida
mtodo 1 mtodo 2 Llamada recursiva
vuelta
ida vuelta
ida
mtodo 1 mtodo 2 Caso base
vuelta
class Indirecta {
public static void main(String [] args) {
int n=6;
metodo1(n);
}
public static void metodo1(int n) {
Recursividad 143
El resultado sera,
En metodo2 con N: 6
En metodo3 con N: 5
En metodo2 con N: 4
En metodo3 con N: 3
En metodo2 con N: 2
En metodo3 con N: 1
En metodo1 con N: 0
En el Programa 6.4 hay un mtodo1 que recibe un valor entero y lo pasa a un metodo2 que lo
transmite a metodo3 quien vuelve a invocar a metodo1. El caso base se encuentra en metodo1 y
corresponde al valor n 5 0. Obsrvese que si n es par se alcanza el caso base. Sin embargo, si n es
impar se entrara en un caso de recursividad infinita, pues nos saltaramos el caso n 5 0. Aunque ste
es un caso sencillo obsrvese que la existencia de la recursividad no es evidente en metodo1. Es nece-
sario conocer cul es la secuencia completa de llamadas realizadas a los mtodos para poder seguir y
depurar, por ejemplo, el problema de recursividad infinita anteriormente mencionado.
Caractersticas comunes
a) Tanto la iteracin como la recursividad implican repeticin. La iteracin usa explcitamente
una estructura de repeticin mientras que la recursin logra la repeticin mediante llamadas
sucesivas al propio mtodo.
b) Tanto la iteracin como la recursividad requieren una prueba de terminacin. La iteracin ter-
144 Introduccin a la programacin con orientacin a objetos
mina cuando deja de cumplirse la condicin para terminar el ciclo y la recursin termina cuan-
do se reconoce un caso base.
c) Tanto la iteracin con repeticin controlada por contador como la recursividad, se aproximan
gradualmente a la terminacin. La iteracin contina modificando un contador hasta que ste
adquiere un valor que hace que deje de cumplirse la condicin de continuacin del ciclo. Por
otro lado, la recursividad sigue produciendo versiones ms sencillas del problema original
hasta llegar al caso base.
d) Tanto la iteracin como la recursividad pueden continuar indefinidamente. En la iteracin
ocurre un ciclo infinito si la prueba para continuar el ciclo nunca deja de cumplirse. A su vez,
tenemos recursin infinita si cada llamada recursiva no simplifica el problema llevndonos
hacia el caso base o si, an dirigiendonos al caso base, lo saltamos.
Diferencias
La recursividad presenta una desventaja frente a la iteracin: la invocacin repetida del mtodo. Por
tanto, se incurre en el gasto extra de las llamadas necesarias. Esto puede ser costoso tanto en tiempo
de procesador como en gasto de memoria. Cada llamada recursiva hace que se cree otra copia del
mtodo (en realidad slo de las variables del mtodo). Esto puede consumir una cantidad considera-
ble de memoria. La iteracin normalmente ocurre en el mismo mtodo, por lo que se omite el gasto
extra de las llamadas a mtodo y de la asignacin adicional de memoria. La diferencia en eficiencia
suele depender de cunto crezca la pila de datos en el caso recursivo. Adems, en principio, toda tarea
que pueda realizarse con recursividad puede tambin hacerse de otra manera. Siempre hay una solu-
cin iterativa para cualquier problema recursivo. Si esto es as, por qu escoger la recursividad? Se
escoge la recursividad cuando el enfoque recursivo refleja de forma ms natural el problema y produ-
ce un programa ms fcil de entender y depurar. Otra razn para escoger una solucin recursiva es
cuando una solucin iterativa no es viable.
La equivalencia de recursividad e iteracin se puede ilustrar con un ejemplo. Consideremos el caso
de la suma recursiva de 1 a n mostrada en el Programa 6.1. Se puede resolver el mismo problema ite-
rativamente de la siguiente forma:
suma =0;
for (int numero=1; numero<=n; numero++){
suma+=numero;
}
Esta solucin es ms clara que la recursiva. Este ejemplo se us para exponer la recursin porque
es un caso muy sencillo, no porque se use la recursividad en condiciones normales para resolver este
tipo de problemas. Como hemos indicado, la recursividad tiene el coste de mltiples invocaciones a
un mtodo, y en este caso presenta una solucin ms complicada que su equivalente iterativa. Como
ejemplo de cmo convertir una solucin recursiva en una iterativa veamos el clculo iterativo del fac-
torial ejemplificado en el Programa 6.5.
class Factorial_iterativo {
public static void main(String[] args) {
int n=0;
System.out.println( Calculo iterativo del Factorial ) ;
Recursividad 145
} // mtodo main
Podemos decir que la recursividad es la mejor manera de resolver algunos problemas, pero para
otros es ms complicada que la solucin iterativa. Un programador debe evaluar cada situacin para
determinar la aproximacin adecuada dependiendo del problema a resolver. Todos los problemas se
pueden resolver de manera iterativa, pero en algunos casos la versin iterativa es mucho ms compli-
cada. La recursividad en ciertas ocasiones permite crear soluciones relativamente cortas y elegantes.
Por ltimo, es conveniente comentar que no todos los lenguajes de programacin permiten la recursi-
vidad, como por ejemplo Cobol o Fortran 77.
6.5. APLICACIONES
La recursividad representa un papel muy importante en el diseo de distintos tipos de algoritmos como
son los de:
a) Divide y vencers.
b) Programacin dinmica.
c) Backtracking (vuelta atrs).
El tratamiento de estos tipos de algoritmos cae fuera del alcance de este texto. A tal efecto con-
sultnse las referencias (Weiss, 1998; Aho et al., 1987; Brassard y Bratley, 1997). Sin embargo, como
ilustracin de la potencia de la recursividad vamos a ilustrar un ejemplo de algoritmo de backtracking
utilizado para la resolucin de laberintos. Para ello, comencemos presentando brevemente en qu con-
sisten los algoritmos de backtracking.
A veces, nos enfrentamos con la tarea de encontrar la solucin de un problema, pero no hay nin-
guna teora aplicable que nos ayude a encontrar la solucin ptima sin recurrir a una bsqueda exhaus-
tiva. Pues bien, es posible realizar una bsqueda exhaustiva usando la tcnica denominada
backtracking (vuelta atrs). En el backtracking usamos la recursividad para probar sistemticamente
todas las posibilidades. El backtracking se utiliza para crear programas que realicen juegos de estrate-
gia, desde las tres en raya hasta el ajedrez. Vamos a aplicar esta tcnica a un problema clsico, la reso-
lucin de un laberinto.
146 Introduccin a la programacin con orientacin a objetos
Resolver un laberinto involucra un tratamiento de prueba y error. As, debemos seleccionar una
direccin, despus seguir ese camino y retornar al punto anterior si en algn momento no se puede
continuar y probar otras direcciones. Un laberinto puede resolverse si se realiza una exploracin sis-
temtica del mismo y a tal efecto puede utilizarse un backtracking. Como ilustracin de la tcnica y
del papel que representa la recursividad, abordemos la resolucin genrica de un laberinto en dos
dimensiones.
En primer lugar establezcamos las reglas que definen el laberinto (esto corresponde a la etapa de
anlisis). El laberinto consistir en un rectngulo de N posiciones horizontales y M posiciones verti-
cales. El smbolo * marcar una posicin prohibida (una pared) y un blanco indicar una posicin
accesible. Segn se vayan probando caminos, cada posicin probada se marcar con una x. El cami-
no de salida del laberinto debe indicarse con una serie de puntos, .. La posicin de salida se marca
con una s. Los movimientos permitidos en el laberinto son los de la torre de ajedrez (arriba, abajo,
derecha e izquierda) pero limitados a una sola casilla cada vez. Se comienza la bsqueda en una casi-
lla libre identificada por sus coordenadas (ndices), no se puede salir de los lmites del laberinto, y la
bsqueda concluye cuando se encuentra la salida o se comprueba que no la hay.
Determinemos ahora cmo se resuelve el problema (etapa de diseo). Como no existe ninguna for-
ma de deducir cul es la solucin de un laberinto, realizaremos una exploracin sistemtica del mis-
mo. As, si hay salida la encontraremos y si no la hay estaremos seguros de ello. A tal efecto,
disearemos un algoritmo recursivo que por un lado identifique si hemos acabado la exploracin y por
otro realice la exploracin en las cuatro direcciones de movimiento permitidas. De esta manera defi-
nimos un caso base (unos en realidad) y un caso inductivo. Para el caso inductivo basta con indicar la
estrategia para hacer un movimiento. Como vimos en el Apartado 6.3.3 el principio de induccin ase-
gura que la solucin es correcta.
a) Casos base
Identifiquemos como tales todas aquellas situaciones que implican que se deja de aplicar en ese
momento la exploracin sistemtica (se corta una lnea de llamadas recursivas). As tendremos:
b) Caso inductivo
Indiquemos cmo realizar la exploracin sistemtica. Lo que haremos ser, desde la casilla en la que
estemos, movernos hacia arriba. Si no se puede, intentaremos ir hacia abajo. Si tampoco es posible,
iremos a la derecha y si esto tampoco es posible intentaremos ir a la izquierda. Para simplificar el algo-
ritmo recursivo colocaremos en un mtodo aparte la comprobacin de un movimiento vlido. Como
tal entenderemos el que no se sale de los lmites de la matriz y que alcanza una casilla vaca o la casi-
lla final.
El laberinto se definir en el mtodo main como una matriz NxM de caracteres. En el Progra-
ma 6.6 se presenta un ejemplo de backtracking para resolver laberintos genricos.
Programa 6.6. Programa que aplica un backtracking para resolver un laberinto bidimensional
class Laberinto {
public static void main(String [] args) {
Recursividad 147
Programa 6.6. Programa que aplica un backtracking para resolver un laberinto bidimensional
(continuacin)
int i_x=0, i_ y=3; // Punto de salida
char [][] labe= {{*,*,*, ,*},
{ , , , ,*},
{*,*, , , },
{*,*, , ,*},
{*,*,s,*,*}};
// Imprimiendo laberinto inicial
imprime(labe);
// Se intenta resolver el laberinto
if (resolver(labe, i_x, i_ y)) {
System.out.println(Laberinto resuelto);
imprime(labe);
}
else {
System.out.println(El laberinto no tiene solucion);
imprime(labe);
}
} // Fin mtodo main
System.out.println();
for (int i=0; i<N; i++) {
for (int j=0; j<M; j++) {
System.out.print(labe[i][j]);
}
System.out.println();
}
System.out.println();
} // Fin mtodo imprime
Programa 6.6. Programa que aplica un backtracking para resolver un laberinto bidimensional
(continuacin)
}
else {
labe[x][y]=x; // Se marca la casilla
fin=resolver(labe, x-1, y); // Arriba
if (!fin) {
fin=resolver(labe, x+1, y); // Abajo
}
if (!fin) {
fin=resolver(labe, x, y+1); // Derecha
}
if (!fin) {
fin=resolver(labe, x, y-1); // Izquierda
}
} // Fin del segundo if
if (fin) {
labe[x][y] = .;
}
} // Fin del primer if
return fin;
} // Fin mtodo resuelve
*****
*****
*****
*****
**s**
Laberinto resuelto
***.*
xxx.*
**x.*
**..*
**.**
Observemos que a partir de la llamada inicial comienza la exploracin del laberinto en el orden
arriba, abajo, derecha e izquierda. Cada vez que se llega a una posicin prohibida se corta ah la lnea
recursiva y se va hacia atrs (backtracking) en la pila de llamadas. Esta vuelta atrs se va realizando
hasta que se llega a una casilla desde la que hay un movimiento vlido, momento en el que comienza
otra serie de llamadas recursivas. En el momento en que lleguemos a la casilla de salida acaba el pro-
ceso recursivo y se inicia la vuelta atrs escribiendo un punto en cada posicin hasta que se deshace
la serie completa de llamadas y se llega a la casilla inicial. Es importante notar que si el laberinto tie-
ne varias salidas, se dar como solucin la primera que se encuentre ya que no se aplica ningn crite-
rio que implique la optimizacin de la solucin.
Recursividad 149
Fila 1
Fila 2
Fila 3
Fila 4
Las reglas del juego son muy simples. Hay dos contrincantes que juegan por turnos. En cada
movimiento se pueden quitar tantas fichas de una sola fila como se quiera, y gana el jugador que deja
el tablero vaco. Por ejemplo, si nos quedan dos filas con tres fichas, una en la primera fila y dos en
la segunda y nos toca jugar tomaramos una sola ficha de la fila con dos. As, nuestro contrincante
slo puede coger una ficha de alguna de las dos filas que quedan (slo hay una ficha en cada una y
no se pueden coger fichas de ms de una fila). El resultado es que queda una fila con una ficha que
cogeramos nosotros, dejando el tablero vaco y ganando la partida.
Con la idea de la existencia de jugadas buenas y malas podemos disear un algoritmo recursivo
que juegue al Nim. El caso base sera que el tablero estuviera vaco y el caso inductivo se organiza con
dos tareas. Una de ellas determina un buen movimiento como uno que es malo para el contrario y la
otra indica si un movimiento concreto es malo porque no es un buen movimiento. Con esta definicin
ya queda implcita la naturaleza recursiva de la solucin. La estrategia ms simple es explorar todas
las posibles jugadas, localizando la primera que sea buena. Si no se encuentra ninguna que sea buena,
se indicar de alguna forma, en nuestro caso con un valor centinela. Representaremos el tablero como
una matriz con tantos elementos como filas, almacenando en cada elemento el nmero de fichas en la
fila. El algoritmo recursivo (en pseudocdigo) para encontrar un buen movimiento sera:
Inicio
seguiriverdadero
Para ii0 mientras i<numero_filas y seguir incremento iii+1
150 Introduccin a la programacin con orientacin a objetos
Si sigue entonces
filai-1
Fin_Si
El resultado es el valor de la fila que debemos elegir y el nmero de fichas que debemos quitar de
ella. Cuando no hay ningn movimiento bueno se coloca en filas el valor 1 (no puede haber un ndi-
ce negativo en la matriz) como valor centinela para identificar luego la situacin.
El algoritmo anterior no est completo porque falta indicar cmo se determina si una posicin es
mala. Podemos conseguir este objetivo de la siguiente forma:
Inicio
finifalso
Si tablero_vaco entonces
finiverdadero
Si_ no
Ver si es un buen movimiento y devolver fila
Si filai-1 entonces
finiverdadero
Fin_Si
Fin_Si
Devolver fin
Fin
En este algoritmo encontramos el caso base, que es aquel en el que el tablero est vaco. Una mira-
da cuidadosa a los dos algoritmos revela que las variables para almacenar la fila y el nmero de fichas
a retirar deben ser accesibles a ambos. De momento, podemos solucionar este problema usando dos
variables static globales en el sentido usado en el Programa 5.2 3.
Construyamos ahora un programa en Java que implemente estos algoritmos para jugar al Nim con-
tra el ordenador. Abordemos el diseo del mismo. Consideremos los tres puntos principales: diagra-
ma de estructura, estructuras de datos y algoritmos.
a) Diagrama de estructura
Los dos algoritmos para la determinacin de la posicin buena y mala se implementarn en dos mto-
dos (movimiento_chachi y chungo) y se incluirn por comodidad un mtodo para pintar el table-
3
La forma de hacerlo sin este uso de variables globales es por medio de la definicin de una nueva clase que repre-
sente el tablero y todas sus propiedades. Diferiremos este asunto hasta el siguiente captulo dedicado a clases y objetos.
Recursividad 151
ro en la pantalla (pintaTablero) y otro para determinar si el tablero est vaco (fin). Con esto el
diagrama de estructura es el recogido en la Figura 6.4.
b) Estructuras de datos
La estructura de datos central es la usada para definir el tablero. Como antes hemos indicado usare-
mos una matriz de enteros con tantos elementos como filas tenga el tablero. Cada elemento almace-
nar un entero que indicar el nmero de fichas en esa fila.
c) Algoritmos
Los dos algoritmos principales son los que permiten determinar un movimiento bueno y uno malo y que
han sido descritos anteriormente. Cuando no se encuentre ningn movimiento bueno se har una jugada
aleatoria. As, se seleccionar la primera fila que tenga alguna ficha y de las fichas que tenga se selec-
cionar al azar un valor para descontar. Usando la misma tcnica que para el ejercicio 1 propuesto en
Captulo 5 relativo a la simulacin del lanzamiento de un dado usando nmeros aleatorios tendramos,
Inicio
otraiverdadero
Para ii0 mientras i<tamao_tablero y otra incremento iii+1
Si tablero(i) 0
otraifalso
filaii
fichasiparte entera de (1+random*tablero(i))
Fin_Si
Fin_Para
tablero(fila)itablero(fila)-fichas
Fin
Principal
chungo
fin
Figura 6.4. Diagrama de estructura para el programa del juego del Nim
152 Introduccin a la programacin con orientacin a objetos
En el pseudocdigo anterior se ha supuesto que random genera un nmero aleatorio entre cero y
uno, como en Java hace el mtodo random() de la clase Math.
Con todo esto, una propuesta de implementacin sera la mostrada en el Programa 6.7.
import java.io.*;
class Nim {
static int fila_m, fichas_m;
public static void main(String [] args) throws IOException {
int fila, n_fichas, tamagno;
boolean seguir=true;
int [] tablero;
// Definiendo el tablero
System.out.println(Bienvenido al juego del Nim);
System.out.println(Introduzca el numero de filas
+que desea: );
System.out.println();
System.out.println(Tablero inicial);
pintaTablero(tablero);
// Empieza el juego
while (seguir) {
// Mueve el humano
System.out.println(Usted mueve);
System.out.println(Fila: );
fila=Integer.parseInt(leer.readLine());
System.out.println(Fichas a eliminar: );
n_fichas=Integer.parseInt(leer.readLine());
tablero[fila]-=n_fichas;
System.out.println();
System.out.println(Tablero tras el movimiento);
pintaTablero(tablero);
if (fin(tablero)) {
System.out.println(Usted gano);
seguir=false;
}
else {
// Mueve la mquina
System.out.println(Mueve la maquina);
movimiento_chachi(tablero); // Algoritmo recursivo
Recursividad 153
if (fila_m == -1) {
// Si no hay posicin ganadora se juega al azar
boolean otra=true;
for (int i=0; i<tablero.length && otra; i++) {
if (tablero[i]!=0) {
otra=false;
fila_m=i;
fichas_m= (int)(1+Math.random()*tablero[i]);
System.out.println(fila_m+ +fichas_m);
}
}
}
tablero[fila_m]-=fichas_m; // Jugada de la mquina
pintaTablero(tablero);
if (fin(tablero)) {
System.out.println(Usted perdio);
seguir=false;
}
}
} // Fin del while
}//fin main
System.out.println();
for (int i=0;i<tablero.length;i++){
System.out.print(i+:);
for (int j=0; j<tablero[i];j++){
System.out.print(*);
} // Fin del for j
System.out.println(); // Saltando una lnea
}
System.out.println();
}//fin pintaTablero
int n_fichas=0;
boolean finito=false;
for (int i=0; i<tablero.length; i++) {
n_fichas+=tablero[i];
}
if (n_fichas==0) {
finito=true;
}
return finito;
}//fin mtodo fin
public static boolean chungo(int [] tablero) {
154 Introduccin a la programacin con orientacin a objetos
boolean kaput=false;
if (fin(tablero)) {
kaput=true; // Caso base
}
else {
movimiento_chachi(tablero);
if (fila_m ==-1) {
kaput =true;
}
}
return kaput;
El programa comienza preguntando por el tamao del tablero entendido como el nmero de filas
a colocar. El juego empieza con un movimiento del jugador humano y a partir de ah se va alter-
nando con la jugada de la mquina. El juego del Nim obedece a una estructura matemtica explica-
ble en trminos del o lgico exclusivo (xor) y notacin binaria. El lector interesado puede consultar
(Nim, 2002).
No queremos concluir este captulo sin indicar que una excelente exposicin de la recursividad y
sus aplicaciones puede encontrarse en (Roberts, 1986).
Recursividad 155
EJERCICIOS PROPUESTOS
Ejercicio 1.* Qu valor devolver el mtodo restados si le pasamos el valor 5?
Y si le pasamos 6?
int restados(int n) {
int valor=0;
if (n==2) {
valor=0;
}
else {
valor= n+restados(n-2);
}
return valor;
}
Ejercicio 4.* Sin utilizar ningn bucle, escriba un mtodo que acepte como par-
metros una matriz a de nmeros reales y un entero n 0. El mto-
do debe devolver la suma de los valores de la matriz a comprendidos
entre el primer elemento y el elemento n.
Ejercicio 5.* Cuenta la leyenda que en el gran templo de Benars existe una base
de bronce de la que sobresalen tres varillas de diamante. En el
momento de la creacin, Dios coloc 64 discos de oro ensartados en
156 Introduccin a la programacin con orientacin a objetos
Ejercicio 7.* Cul es el error del siguiente mtodo que pretende evaluar un
sumatorio? Cmo corregira el error?
Ejercicio 8.* Implemente un mtodo recursivo que imprima los elementos de una
matriz monodimensional.
class Indirecta{
public static void main(String [ ] args) {
metodoA(3);
} // Fin del main
System.out.println(B despues);
} // Fin del metodoB
}
Ejercicio 10.* Considere el algoritmo de Euclides para la determinacin del mxi-
mo comn divisor expuesto en el Ejercicio 12 del Captulo 5. Escri-
ba un mtodo recursivo que aplique el algoritmo de Euclides.
REFERENCIAS
AHO, A. V., HOPCROFT, H. E. y ULLMAN, J. D.: Data Structures and Algorithms, Addison-Wesley, 1987.
APOSTOL, T. M.: Calculus, vol. 1, Segunda Edicin, Revert, 1979.
BRASSARD, G. y BRATLEY, P.: Fundamentos de Algoritmia, Prentice Hall, 1997.
Nim: http://www.cut-the-knot.com/nim_theory.shtml ltima visita realizada en mayo de 2002.
ROBERTS, E. S.: Thinking Recursively, John Wiley & Sons, Inc, 1986.
WEISS, M. A.: Data Structures & Problem Solving using Java, Addison-Wesley, 1998.
7
Clases y Objetos
Sumario
7.1. INTRODUCCIN
En este tema abordamos de forma sistemtica la programacin orientada a objetos, partiendo de los
conceptos de clase y objeto. Todos los contenidos presentados hasta el momento y relativos a la pro-
gramacin imperativa y estructurada tradicional continuarn siendo vlidos. Esto se debe a que la pro-
gramacin imperativa se puede considerar como un subconjunto de la programacin orientada a
objetos (Arnow y Weiss, 2001). Como podr comprobarse a lo largo de este captulo, la programacin
orientada a objetos implica ms un cambio de filosofa en el planteamiento de la resolucin de los pro-
blemas que la introduccin de nuevos elementos de programacin. En cualquier caso, la intencin cen-
tral que nos ocupa en este momento es la fundamentacin terica de la programacin orientada a
objetos. Lgicamente, tendremos que incluir consideraciones sintcticas en este captulo. Sin embar-
go, aconsejamos que el lector se centre en comprender los conceptos, y la sintaxis la considere como
un medio para implementar dichos conceptos. Recordemos que el objetivo de este texto es presentar
el desarrollo de software aplicando el paradigma de orientacin a objetos y no simplemente ensear a
codificar en un lenguaje orientado a objetos. Esta aclaracin es importante, debemos distinguir entre,
7.2.1. OBJETOS
Un programa escrito segn el paradigma orientado a objetos consiste en una serie de objetos que inte-
raccionan entre s, pero la pregunta es qu es un objeto? Podemos exponer el concepto de objeto esta-
bleciendo una analoga con los objetos fsicos del mundo real, como si estuviramos pretendiendo
desarrollar un programa de simulacin.
Imaginemos un objeto del mundo real tal como una pelota. La pelota tiene ciertas caractersticas
como color, peso, dimetro o posicin. Estas caractersticas definen el estado de la pelota, y la varia-
cin de alguna de esas caractersticas altera el estado de la pelota. Aparte, la pelota puede realizar cier-
Clases y Objetos 161
tas tareas como rodar o botar. Estas tareas definen el comportamiento de la pelota. Con el estado
y el comportamiento podemos conocer qu le pasa a la pelota en un instante determinado. Desde este
punto de vista, cualquier objeto fsico queda definido por su estado y su comportamiento. Si imagina-
mos que tenemos que simular una pelota en un programa, necesitaremos datos (variables) para indicar
su estado, y mtodos que alteren esos datos para representar su comportamiento, vase la Figura 7.1.
Estado Variables
Comportamiento Mtodos
Si queremos programar un juego de baloncesto, la pelota se podra representar como un objeto que
posee variables para almacenar su tamao y posicin, y mtodos que la dibujan en la pantalla y cal-
culan cmo se mueve. Las variables y mtodos definidos establecen el estado y comportamiento que
son relevantes para el juego. Esto es importante, qu datos y qu mtodos vamos a usar depende del
programa que vayamos a escribir. De hecho, son los requisitos del programa los que indican los datos
y mtodos necesarios.
Con el estado y el comportamiento de la pelota podemos manejar este objeto en nuestro juego. Por
lo tanto, desde el punto de vista del juego, la pelota no es sino un conjunto de caractersticas o pro-
piedades, por un lado, y de tareas o mtodos (procedimientos), por otro.
Los programas pueden, o quiz sea ms preciso decir suelen, tener muchos objetos del mismo tipo,
pero cada objeto en concreto es nico. Para cada objeto, sus propiedades tendrn un valor y su com-
portamiento ser uno dado. Dos objetos similares pueden tener distinto o el mismo valor de sus pro-
piedades y el mismo posible comportamiento. En el hipottico programa para simular un partido de
baloncesto, por ejemplo, habra varios jugadores. Cada jugador se representara como un objeto dis-
tinto, cada uno con sus propiedades (nombre, posicin en el campo en cada momento, etc.). Los obje-
tos (dos pelotas, por ejemplo) son diferentes aunque sean del mismo tipo (tienen las mismas
propiedades y el mismo comportamiento) pero cada uno tiene su identidad y su nombre.
Consideremos otro ejemplo. Imaginemos una herramienta software de gestin de una universidad.
En este caso, dentro del programa, los alumnos se representaran como objetos con una serie de pro-
piedades, por ejemplo: nombre, carrera cursada, asignaturas cursadas, edad o notas. Cada objeto alum-
no almacenar informacin acerca de un alumno particular, es decir, cada alumno estar representado
por un objeto. Tambin podemos asociar ciertos comportamientos o mtodos (los que se necesiten en
el programa) a cada objeto alumno, como imprimir el nombre o calcular la nota media a partir de las
notas. La estructura de un objeto alumno podra ser,
Alumno:
Estado
Nombre
Carrera
Asignaturas
Edad
etc.
Comportamiento:
Imprimir_nombre
Calcular_nota_media
162 Introduccin a la programacin con orientacin a objetos
etc.
Este concepto genrico de objeto toma carta de naturaleza en el paradigma de la programacin
orientada a objetos. Los objetos son las entidades en las que se basa un programa orientado a objetos.
Estos objetos interaccionan (y el programa funciona) envindose mensajes unos a otros que indican
tareas a realizar (son solicitudes de servicios, llamadas, invocaciones, a mtodos). Los objetos soft-
ware no siempre se corresponden con objetos fsicos. Podemos manejar objetos abstractos como por
ejemplo mensaje_de_error con propiedades como codigo_error o gravedad_de_error y
mtodos como describir_error.
Un error muy comn entre los nefitos en la programacin orientada a objetos es el de definir obje-
tos 1 que constan slo de atributos o slo de procedimientos. En el primer caso tenemos una simple
estructura de datos sin los procedimientos para manipularlos. En el segundo caso tenemos una serie de
procedimientos sin datos sobre los que actuar. Estas dos situaciones son inconsistentes con el para-
digma de orientacin a objetos y deben ser evitadas.
Recordando el ejemplo de los distintos jugadores en el programa de baloncesto es fcil entender
que algunos objetos pertenecen al mismo tipo, aunque individualmente sean diferentes. Por ejemplo,
todos los jugadores tendrn la misma estructura (mismas variables y mtodos) aunque el estado de
cada uno sea diferente. Los objetos que tienen las mismas propiedades (aunque sus valores puedan ser
distintos) y el mismo comportamiento se agrupan en categoras llamadas clases como vamos a ver en
el siguiente apartado.
7.2.2. CLASES
Como se indicaba en el apartado anterior los objetos con las mismas propiedades y comportamiento
tienen o son del mismo tipo. El tipo del objeto define sus propiedades y su comportamiento, de forma
anloga a cmo el tipo de dato de una variable primitiva determina la naturaleza de datos que puede
contener y el intervalo de los mismos. Los objetos del mismo tipo seran elementos del mismo con-
junto. Dicho conjunto se denomina clase. La relacin entre la clase y los objetos es similar a la exis-
tente entre un tipo primitivo y las variables de ese tipo. Por ejemplo, sea en Java el tipo primitivo int.
Slo hay un tipo, pero podemos crear tantas variables de ese tipo como necesitemos. De la misma for-
ma, considerando una clase concreta, esa clase es nica, pero se pueden crear tantos objetos de ella
como se necesiten. Por ejemplo, si existiera una clase jugador podramos crear tantos jugadores (obje-
tos) como hagan falta. Cuando se hace un programa orientado a objetos no programamos cada objeto
por separado, pues todos los objetos de la misma clase tendran el mismo cdigo. Lo que programa-
mos es la clase y luego se crean tantos objetos de ella como se necesiten. Dicho de otra forma, para
crear un objeto es necesario haber escrito previamente una definicin de la clase del objeto. La clase
es el plano, el modelo, el patrn o plantilla para crear objetos. Es como el plano de una casa. El pla-
no es nico, pero a partir de l podemos crear casas diferentes, por ejemplo, con revestimientos exte-
riores distintos y localizadas en ciudades distintas. Los atributos (datos) y procedimientos (mtodos)
de una clase se denominan miembros de la misma. Desde un punto de vista formal, como ya comen-
tamos en el Apartado 5.3 del Captulo 5, una clase se puede entender como la realizacin de un tipo
abstracto de datos (TAD).
Una vez que hemos definido una clase podemos crear objetos de la misma. Se dice que creamos
un ejemplar, instancia de la clase. Cada objeto creado es nico porque cada uno tiene su propio espa-
cio de datos (estado) posiblemente con diferentes valores ocupando su propia zona de memoria.
Para el desarrollo de un programa orientado a objetos es til disponer de alguna tcnica grfica
1
Para ser estrictos deberamos decir clase y no objeto ya que donde se realiza la especificacin de los atributos y los
procedimientos es en la definicin de la clase.
Clases y Objetos 163
que permita representar las clases. En este texto usaremos la notacin del lenguaje unificado de mode-
lado, UML 2. En UML la clase se representa como un rectngulo dividido en tres secciones. La pri-
mera se usa para indicar el nombre de la clase, la segunda para recoger los atributos (datos) y la tercera
para los procedimientos (mtodos). El tipo (o clase) al que pertenecen los atributos, los parmetros de
los mtodos o el tipo de retorno de los mtodos se indica tras el identificador correspondiente con la
sintaxis:
atributo:tipo
o
mtodo(parmetro:tipo):tipo de retorno
Los objetos se representan con un rectngulo, donde se indica en subrayado el nombre del objeto
(si nos interesa indicarlo) seguido de dos puntos y el nombre de la clase (si nos interesa indicarla). En
esta notacin, la relacin clase-objeto quedara ilustrada tal y como aparece en la Figura 7.2.
Clase A
Objeto 1: Clase A
Estado
(propiedades,
variables)
Objeto 2: Clase A
Comportamiento
(procedimientos)
Registro Estudiante:
Campo 1: Nombre (Tipo cadena)
Campo 2: Apellido 1 (Tipo cadena)
Campo 3: Apellido 2 (Tipo cadena)
Campo 4: Edad (Tipo entero)
2
El UML (Unified Modeling Language) es un lenguaje grfico usado en el proceso de desarrollo de software orientado
a objetos y que representa un estndar actual (Booch et al., 2000; Rumbaugh et al., 2000). En este texto, la notacin utiliza-
da para el modelado de sistemas orientados a objetos ser la indicada en UML. El Apndice C recoge un resumen de esta
notacin para la creacin de diagramas de clase.
3
sta es una pregunta tpica de los estudiantes que conocen otros lenguajes como Fortran 90, Pascal o C.
164 Introduccin a la programacin con orientacin a objetos
7.3.1. ENCAPSULACIN
Un objeto (en realidad una clase) puede considerarse desde dos puntos de vista:
a) En tiempo de desarrollo
Aqu se tratara el problema de la definicin o diseo de la clase. El trabajo consistira en decidir
qu datos corresponden a la clase y qu mtodos vamos a necesitar para manipular esos datos. Nece-
sitaramos tambin disear los diferentes mtodos. En resumen, necesitaramos construir el interior
de la clase.
b) En tiempo de ejecucin
Cuando la clase ya existe, se crean objetos de la misma y se usan invocando los mtodos necesarios.
En realidad creamos objetos para usar los servicios que nos proporciona la clase a travs de sus mto-
dos. No necesitamos considerar cmo trabaja el objeto, ni qu variables usa, ni el cdigo que contie-
ne. Desde este punto de vista, el objeto se usa como una caja negra de la que slo necesitamos saber
lo que hay que darle para que nos proporcione un servicio determinado. Es la filosofa cliente-servi-
dor. El objeto es un servidor que proporciona servicios a los clientes que lo solicitan. Se usa el trmi-
no encapsulacin para describir el hecho de que los objetos se usan como cajas negras. Se dice que un
objeto encapsula datos y mtodos (atributos y procedimientos). Estos mtodos y datos estn conteni-
dos dentro del objeto. La encapsulacin es la idea bsica tras la filosofa o modelo cliente-servidor. El
conjunto de procedimientos (mtodos) que sirven para que los objetos de una clase proporcionen sus
servicios define la interfaz pblica de esa clase.
Los mtodos que definen los servicios que el objeto proporciona se denominan mtodos de servi-
cio y pueden ser invocados por un cliente. Puede haber mtodos adicionales en un objeto que no defi-
nen un servicio utilizable por un cliente, pero que ayudan a otros mtodos con sus tareas. Son los
denominados mtodos de soporte, vase la Figura 7.3.
Como ejemplo, imaginemos un mtodo que ordena una serie de valores. Supongamos que el algo-
ritmo de ordenacin es complejo y que se modulariza en tres mdulos. Uno de estos mdulos es el que
acepta la peticin de ordenacin y necesariamente tendr que poder ser invocado desde fuera del obje-
to para que pueda empezar el proceso de ordenacin. Este mtodo forzosamente formar parte de la
Clases y Objetos 165
Clase A
Atributos (datos)
Procedimientos Interfaz
pblicos (de servicio) pblica
Procedimientos
privados (de soporte)
interfaz pblica de la clase. Sin embargo, internamente este mtodo invoca a los otros dos para poder
aplicar el algoritmo de ordenacin. Estos dos nuevos mtodos no necesitan ser llamados desde el exte-
rior de la clase y no tienen que formar parte de la interfaz pblica. Estos dos mtodos seran privados
(de soporte) a la clase.
La encapsulacin es un mecanismo de control. Los datos de un objeto slo deben poder ser modi-
ficados por medio de un mtodo de ese objeto. Un cliente nunca debe ser capaz de acceder al estado
(datos) de un objeto directamente, cambindolos. Esto es importante:
El estado de un objeto slo debe ser modificado por medio de los mtodos del propio objeto.
Una clase no debe tener el equivalente a atributos (datos) pblicos que puedan ser directamente
accesibles desde el exterior. La modificacin de un atributo debe realizarse por medio de un mtodo
y la consulta del valor de un atributo debe realizarse por medio de un mtodo especialmente dedicado
para ello.
Llegados a este punto, hemos podido comprobar la utilidad del concepto de encapsulacin. Sin
embargo, dicho concepto se apoya sobre el de abstraccin. Consideremos este concepto en el siguien-
te apartado.
7.3.2. Abstraccin
En la mente humana, la memoria a corto plazo slo puede manejar grupos de aproximadamente 7 ele-
mentos (Miller, 1956). Sin embargo, cualquier construccin humana, excepto las ms simples, mane-
ja ms de 7 constituyentes. La forma de trabajar en estos casos consiste en agrupar elementos
relacionados y manejar estos grupos como una unidad. As, slo consideramos lo que la unidad hace
y no nos fijamos en los detalles internos. Este proceso se denomina abstraccin y se usa continuamente
en la vida diaria, como al conducir un coche. Cuando se conduce un coche no necesitamos conocer los
detalles de cmo al cambiar de marcha o mover el volante se manejan las piezas internas del autom-
vil, para conseguir ms potencia o cambiar de direccin. Nosotros nos abstraemos de esos detalles y
slo necesitamos conocer cmo interaccionar con el coche a nivel de usuario. En trminos de progra-
macin orientada a objetos diramos que slo necesitamos conocer su interfaz pblica. La encapsula-
cin es una forma de abstraccin. La encapsulacin es un mecanismo para llevar a la prctica la
166 Introduccin a la programacin con orientacin a objetos
abstraccin.
El nivel de abstraccin puede ser mayor o menor. A bajo nivel de abstraccin, en una clase esta-
remos manipulando los datos y los mtodos individualmente. A alto nivel de abstraccin, la clase (en
realidad los objetos creados a partir de ella) se considera una unidad y slo se usan sus servicios. El
nivel de abstraccin a aplicar depende del problema considerado. Una buena abstraccin oculta los
detalles en el momento correcto, para que as podamos enfocar la atencin en la direccin adecuada.
Todos los conceptos de orientacin a objetos estn basados en la abstraccin. Un ejemplo de uso de
abstraccin que hemos encontrado en Java est en el uso del mtodo println. Lo hemos utilizado
desde el principio sin necesidad de conocer qu tiene dentro. De hecho, no necesitamos conocer su
contenido para poder usarlo.
a) es-un o generalizaciones.
b) tiene-un o parte-de o asociaciones.
c) usa a o trabaja con o dependencias.
Una relacin se representa en UML como una lnea, usndose diferentes tipos de lnea para dife-
renciar los tipos de relaciones. En este texto usaremos la notacin UML elemental para representar las
tres relaciones, vase el apndice C. Consideremos uno a uno los tres tipos existentes.
verdadera programacin orientada a objetos y por este motivo le dedicaremos el captulo siguiente.
En este contexto, un error tpico es confundir lo que sera un objeto de una clase con una clase nue-
va que hereda de la anterior. Por ejemplo, si definiramos una nueva clase llamada Ciudad_Real que
heredase de una clase Ciudad sera errneo, porque Ciudad_Real es un ejemplar (objeto) de la cla-
se Ciudad. El error se evita si se tiene en cuenta que la relacin objeto-clase implica que el objeto
(elemento de un conjunto) es un ejemplar individual de la clase (el conjunto). Por otro lado, la rela-
cin de herencia entre la clase padre A y la clase hija B implica que todos los elementos (objetos) de
B son un caso particular de la clase A. En otras palabras, la herencia es una relacin entre dos con-
juntos y no entre un conjunto y sus elementos.
Las relaciones de herencia se representan en UML por flechas con la punta vaca apuntando a la
clase padre. La relacin de herencia genera jerarquas entre las clases como en el ejemplo ilustrado en
la Figura 7.4 donde tenemos un conjunto de personas entre las que hay estudiantes y empleados de una
empresa que son vendedores o secretarios.
Persona
Empleado Estudiante
Vendedor Secretario
En el ejemplo de la Figura 7.4 tenemos relaciones de herencia, pues cada subclase es un tipo
especial de la clase padre.
objetos (ejemplares de una clase) estn conectados en una relacin de asociacin. Esto define la mul-
tiplicidad. Para denotar la multiplicidad se usa la siguiente nomenclatura:
a) Si es un valor exacto se indica numricamente, por ejemplo si es uno: 1.
b) Si es un intervalo de posibles valores se indica con el valor mnimo... valor mximo, por ejem-
plo, si es entre dos y cuatro: 2..4.
c) Si son varios, en nmero indefinido, se usa un asterisco: *.
d) Cualquier otro caso se construye con las tres reglas anteriores.
Por ejemplo, entre una empresa y sus dos nicos empleados tendramos:
1 2
Empresa Empleado
Lo cual significa que una empresa debe tener exactamente 2 empleados, ni uno ms ni uno menos,
y que un empleado slo puede trabajar en una empresa.
Si los empleados pudieran ser entre 2 y 4 tendramos:
1 2..4
Empresa Empleado
1 *
Empresa Empleado
1 2..*
Empresa Empleado
Coche
1 1
1 1
1 1 4 1
1 1
1 1
Neumtico Tapacubos
vaco en la parte que corresponde al todo. Por ejemplo, una empresa compuesta por departamentos se
representara como: Figura 7.5. Ejemplo de relacin de asociacin
170 Introduccin a la programacin con orientacin a objetos
1 *
Empresa Departamento
Ducha Caera
Estudiante Asignatura
4
Una alta cohesin implica que todos los elementos del mdulo, en este caso los objetos, estn dirigidos hacia la mis-
ma misin. Todo lo que hay dentro del objeto es coherente consigo mismo, vase el texto de Lewis y Loftus (Lewis y Lof-
tus, 1998).
Clases y Objetos 171
Como comentario final podemos indicar que desde un punto de vista general y a nivel elemental,
a veces slo se distingue entre dos relaciones: la de herencia y la de uso. Esto se observa, por ejem-
plo, en la herramienta de desarrollo BlueJ (BlueJ, 2002).
Clase
Mtodos
Mtodos
Mtodos
Datos
Mtodos
La nica manera de acceder a los atributos (datos) es a travs de los procedimientos de servicio.
De esta forma se implementa el ocultamiento de informacin y se reduce el impacto de efectos cola-
terales provenientes de cambios incontrolados sobre los datos. Como los procedimientos manejan un
conjunto limitado de datos que le son propios obtenemos una alta cohesin 4. Por otro lado, como la
comunicacin slo se realiza a travs de los procedimientos de servicio, el acoplamiento con otros ele-
mentos del sistema est muy controlado. Como consecuencia de estos factores de diseo obtenemos
software de alta calidad, de acuerdo a los patrones de la ingeniera del software.
Cuando se programa siguiendo un paradigma orientado a objetos tambin se debe abordar una eta-
pa de anlisis y otra de diseo antes de la de codificacin. En la orientacin a objetos las etapas de an-
lisis y diseo se solapan an ms que en la tradicional aproximacin funcional o procedimental.
Consideremos estas dos etapas del ciclo de vida del software desde el punto de vista de la orientacin
a objetos (Larman, 1999).
Anlisis: En el anlisis orientado a objetos se pretende encontrar las clases, y las relaciones, rele-
vantes para la descripcin del problema. Se realiza una investigacin del problema, centrada en la
identificacin y descripcin de los conceptos (objetos) en el dominio del problema (de su definicin).
En este contexto, se habla de un modelo conceptual que recoge (entre otras cosas) los conceptos (obje-
tos), sus relaciones, sus atributos (datos) y sus procedimientos (mtodos). En esta etapa se identifican
los objetos que surgen como consecuencia de los requisitos.
Diseo: En el diseo orientado a objetos se parte del modelo de anlisis para crear un nuevo mode-
lo que sirva como patrn para la creacin del programa. Aqu, se definen los objetos lgicos que final-
mente sern implementados en un lenguaje de programacin orientado a objetos. En esta etapa,
aparecen objetos que no son consecuencia de los requisitos (el dominio del problema) sino de la solu-
172 Introduccin a la programacin con orientacin a objetos
cin propuesta (dominio de la solucin). Por ejemplo, imaginemos un programa que gestiona los alum-
nos de una universidad. Con esta informacin podramos plantear dos clases, Universidad y Alum-
no relacionadas estructuralmente (la Universidad est formada por Alumnos). Si pensamos en cmo
(diseo) implementar el programa podramos decidir organizar los alumnos como una lista de personas
y definir una nueva clase Lista. Esta nueva clase no surge de los requisitos (dominio del problema)
sino de la forma en que vamos a solucionar el problema (dominio de la solucin). La etapa de diseo
continuara hasta disponer de un diagrama de clases para cada una de las clases identificadas, donde
especifiquemos sus atributos y procedimientos. A su vez, para cada procedimiento deberamos haber
diseado los algoritmos necesarios y representado el cdigo, por ejemplo, a nivel de pseudocdigo.
Una regla sencilla para elegir las clases y los objetos es la asociacin del software con entidades
fsicas o componentes hardware que las clases controlen. Los objetos que representan entidades fsi-
cas son ms fciles de entender, puesto que se puede establecer una analoga con el software. Estas
clases corresponderan al dominio del problema. Sin embargo, a medida que el software es ms com-
plicado, hay mayor necesidad de clases lgicas que representen ideas intangibles, tal como una clase
que controle el acceso y uso de varios objetos. Una indicacin general es la de fijarse en los sustanti-
vos que aparecen en la descripcin del problema. Sin embargo, esta tcnica no es rigurosa. Dada la
ambigedad del lenguaje puede no hacerse referencia a una entidad que en nuestro problema es una
clase. Esta tcnica no puede pretender usarse como una panacea sino como una simple orientacin. El
proceso de determinacin de clases implica proponer clases posibles y eliminar las que no sean real-
mente clases en el dominio de nuestro problema. Ante una posible clase candidata una pregunta a
hacer es qu datos y qu procedimientos necesita. Una clase debe trabajar con algunos datos (atribu-
tos) y tener unos procedimientos asociados para ello. En caso contrario, no es una clase. Para una dis-
cusin ms detallada vase (Meyer, 1999).
Cuando se estn identificando los objetos y las clases, algunos de sus detalles (atributos, comporta-
miento) son obvios. A menudo, al mismo tiempo que se identifica la clase, se obtiene una idea general de
los mtodos que cada clase debe soportar. Por ejemplo, una clase que est relacionada con el control de
un robot debera tener un mtodo asociado con el movimiento del robot. Estas suposiciones sobre la cla-
se deberan documentarse. Obsrvese que no todos los detalles sobre la clase se conocern en este punto
del ciclo de desarrollo. Otra consideracin es la reutilizacin de clases existentes. Un programador debe
tener en cuenta que existen clases ya escritas que pueden, y deben, usarse en los nuevos desarrollos.
Errores comunes en la seleccin de clases: De entrada, recordemos siempre que un objeto encap-
sula una serie de datos que se manipulan con sus mtodos. Un objeto no contiene slo datos ni slo
mtodos. Un error habitual es el de la rutina glorificada. En este caso tenemos una clase que contiene
slo un mtodo. Eso no es una clase, simplemente estamos considerando una rutina como muy impor-
tante y errneamente la clasificamos como una clase. Este error se puede magnificar cuando se crean
clases que slo tienen mtodos. Es un error en el que estamos confundiendo un enfoque funcional con
un objeto (clase). La clase que hemos creado es en realidad un mdulo funcional, por lo que no tiene
sentido dentro de la filosofa de objetos.
Con lo visto, est claro ahora que en orientacin a objetos usamos el mismo tipo de herramientas
para anlisis y diseo, por lo que las dos etapas no estn tan claramente diferenciadas como en la pro-
gramacin funcional tradicional. Estos conceptos son muy generales y se pueden aplicar con las herra-
mientas de modelado proporcionadas por UML, adaptndose a diferentes estrategias de desarrollo.
El lector interesado en el estudio detallado de la ingeniera del software orientada a objetos puede
consultar textos especializados como Pressman, 2002; Larman, 1999; Bruegge y Dutoit, 2002.
a) Definicin de clases
Una clase es, en cierto sentido, equivalente al tipo de una variable objeto. Por ejemplo, conside-
remos las dos siguientes sentencias en Java,
int total=10;
Cuenta cuenta_corriente = new Cuenta();
En la primera se declara una variable, total, de tipo entero y se le asigna el valor 10. La segun-
da sentencia crea una variable objeto, cuenta_corriente, de tipo (clase) Cuenta. Es evidente
que, como requisito para poder crear objetos de una clase, debemos definir antes la clase. En Java la
sintaxis general para la definicin de una clase es:
class Nombre_clase {
declaraciones
constructores
mtodos
}
El Nombre_clase es arbitrario y por convenio la primera letra del nombre de la clase se escribe
en maysculas. En declaraciones declaramos las variables que son accesibles a todos los mtodos
de esa clase. Los constructores son uno o ms mtodos (si hay ms de uno es un mtodo sobre-
cargado) que tienen el mismo nombre de la clase y se usan para crear los objetos de la misma. Apar-
te de los mtodos constructores se pueden escribir todos los mtodos que se necesiten.
Una clase puede tener cualquier nmero de variables y mtodos. Como ya indicamos, las variables
y mtodos especificados para la clase se denominan miembros de la clase. Las variables se denomi-
nan variables de ejemplar o de instancia porque no existen hasta que se crea un ejemplar, es decir,
un objeto de la clase. Cuando un objeto se define tiene su propio espacio de almacenamiento para sus
variables y, por tanto, sus valores pueden ser diferentes a las de otro objeto de la misma clase. A su
vez, los mtodos actan sobre los datos del objeto que se est usando en cada momento y no sobre los
de otro. Cuando se invoca un mtodo se hace a travs de un ejemplar particular de la clase. Recorde-
mos que las variables del objeto pueden ser a su vez objetos. Un objeto que contiene otros objetos
se denomina objeto agregado. Estas ideas genricas quedarn ms claras con un ejemplo. Considere-
mos una clase que representa una cuenta corriente.
Anlisis
Identifiquemos los requisitos. Supongamos que la cuenta contiene una identificacin de la misma (un
entero) y el saldo de la cuenta. Por otro lado, las operaciones sobre la cuenta se limitan al ingreso
(depsito de cantidades), retirada de fondos y consulta del saldo. Supongamos que a la hora de crear
una cuenta nueva se le puede indicar un saldo inicial.
Diseo
Con estos requisitos la estructura de la clase podra ser la recogida en la Figura 7.7.
174 Introduccin a la programacin con orientacin a objetos
Cuenta
numero_cuenta:int
saldo:double
class Cuenta {
int numero_cuenta;
double saldo;
5
Pronombre demostrativo que significa ste, sta, esto.
Clases y Objetos 175
class Alumno{
//Atributos de ejemplar
int dni, edad;
//Constructor
Alumno(int dni, int edad){
this.dni = dni;
this.edad = edad;
}
}
En el mtodo constructor Alumno, los nombres de los atributos coinciden con los nombres de los par-
metros del constructor declarados en su cabecera (parmetros formales). Para indicar dentro del mtodo si
nos estamos refiriendo al parmetro o la variable de ejemplar se utiliza this. Cuando hacemos referencia
al dni o a la edad precedido de this. estamos haciendo referencia a los atributos de ejemplar.
b) Creacin de objetos
Una vez definida la clase podemos crear diferentes objetos de esa clase, tantos como necesitemos, va-
se la Figura 7.8.
176 Introduccin a la programacin con orientacin a objetos
cuenta 1
numero_cuenta:
123456
saldo:
250532
Clase Cuenta
numero_cuenta:int
saldo:double
cuenta 2
numero_cuenta:
456784
saldo:
1523879
En la Figura 7.8. se observa que de la clase Cuenta se crean dos objetos diferentes llamados
cuenta_1 y cuenta_2. Cada uno de estos objetos tiene su propio valor de numero_cuenta y de
saldo. Los objetos se crearan de la siguiente forma:
Creamos, declaramos, un objeto cuenta_1 de clase Cuenta, inicializndolo con el operador new
y un constructor de la clase Cuenta. En esta sentencia en realidad tenemos dos operaciones: declara-
cin e inicializacin. Estas dos operaciones se pueden separar,
En la primera lnea se declara la variable cuenta_1 como una referencia a un objeto de la clase
Cuenta. Todava no se ha creado el objeto y la referencia no refiere a nada. Este valor de nada se
identifica en Java con la palabra reservada null. Es posible usar null en un if para saber si una refe-
rencia ya refiere a un objeto o an no. Veremos un ejemplo de esta tcnica en el apartado sobre estruc-
turas dinmicas. No hay todava un ejemplar o instancia de la clase (objeto) sino una referencia que
puede referir, apuntar, a un objeto. Las variables en Java pueden ser de tipo primitivo o referencias a
objetos. Una referencia a un objeto se puede entender como una variable que almacena la direccin en
memoria donde se encuentra el objeto. Hasta que se le asigna un objeto a la referencia, sta no refie-
re a nada. En el ejemplo, en la segunda lnea se crea un objeto de la clase Cuenta con el operador new
y se asigna a la referencia de la variable cuenta_1. En otras palabras, hacemos que la referencia refie-
ra, apunte, a algo.
Cuando un objeto ya existe, se pueden invocar sus mtodos con el operador punto . como en el
Clases y Objetos 177
Referencia Objeto
cuenta_1 cuenta_1
Atributos (datos)
Procedimientos
(mtodos)
class Banco {
public static void main(String [] args) {
double total_cuenta;
// Se consulta el saldo
total_cuenta=cuenta_1.saldo();
System.out.println(Total actual en la cuenta:
+total_cuenta + Euros);
7.7. REFERENCIAS
El concepto de referencia (relacionado con el de puntero en otros lenguajes) es muy importante. Es la
178 Introduccin a la programacin con orientacin a objetos
base de la creacin de estructuras dinmicas. Vamos a ilustrarlo partiendo del concepto de alias, sin-
nimo, de un objeto.
int numero_1=5;
int numero_2=12;
la asignacin
numero_1=numero_2;
hace que numero_1 almacene lo mismo que numero_2, en este caso 12. Se trata de una asignacin
del valor. Al final, la variables numero_1 y numero_2 contienen lo mismo. Es posible cambiar cual-
quiera de las variables sin que se modifique el contenido de la otra, ya que corresponden a zonas de
memoria distintas, independentemente de tener el mismo contenido, vase la Figura 7.10.
numero_1 numero_2
Antes de
la asignacin
5 12
numero_1 numero_2
Despus de
la asignacin
12 12
En la Figura 7.10 los rectngulos simbolizan posiciones de memoria. Como podemos ver, hay una
relacin directa entre el identificador y el contenido de la memoria. A efectos prcticos, el identifica-
dor es el contenido de la memoria.
Con objetos, una asignacin no se refiere al contenido sino a la direccin de memoria donde se
almacena el contenido. Aqu tendramos el identificador que corresponde a una posicin de memo-
ria, la cual indica dnde est la zona de la memoria en la que se almacena el objeto, vase la Figu-
ra 7.11.
Clases y Objetos 179
Identificador: cuenta_1
Direccin de memoria
Objeto cuenta_1
La Figura 7.11 muestra que ahora existe un paso ms. Comparando con el caso de una variable de
tipo primitivo mostrado en la Figura 7.10, observamos que ahora la referencia es la direccin de
memoria donde se encuentra el objeto. Para llegar al objeto primero examinaramos el contenido de la
referencia para identificar la direccin de memoria, y despus iramos all para manipular el objeto. En
el resto del captulo simplificaremos el diagrama y simplemente representaremos el identificador refi-
riendo al objeto. Podemos decir que cuando creamos la referencia lo que hacemos es establecer la pri-
mera flecha del diagrama y cuando creamos el objeto enlazamos la referencia con el objeto real en
la memoria, vase la Figura 7.12.
Objeto cuenta_1
Declaracin Inicializacin
inicialmente cuenta_1 y cuenta_2 referirn a dos objetos diferentes de tipo Cuenta, cada uno en
posiciones de memoria diferentes. Si hacemos la siguiente asignacin,
cuenta_1=cuenta_2;
como trabajamos sobre referencias y no sobre datos primitivos, el resultado es diferente de la asigna-
cin de valores enteros. Aqu, lo que hacemos es que cuenta_1 adquiera el valor (direccin de memo-
ria) almacenado en cuenta_2. No hemos sustituido el contenido del objeto cuenta_1 con el objeto
cuenta_2, manteniendo dos zonas independientes de memoria. Lo que hemos hecho ha sido que la
referencia cuenta_1 no refiera o apunte al objeto cuenta_1, sino al cuenta_2, como muestra la
Figura 7.13.
cuenta_1 cuenta_2
cuenta_1 cuenta_2
Las dos referencias originalmente referan a distintos objetos con espacio independiente para las
variables, cada objeto ocupa distinta posicin de memoria. Despus de la asignacin, cuenta_1 y
cuenta_2 se refieren al mismo objeto, que es al que refera originalmente cuenta_2. Las dos refe-
rencias apuntan a la misma zona de memoria, la de cuenta_2, y el objeto cuenta_1 sigue ocupan-
do una zona de memoria, aunque no tiene ninguna referencia apuntando a l. Los nombres de los dos
objetos son ahora alias, sinnimos que refieren a la misma entidad. Los dos identificadores refieren a
lo mismo, as que los cambios realizados a travs de uno o a travs de otro lo son sobre el mismo obje-
to. En otras palabras, slo hay un objeto con sus propios datos, pero se puede acceder a l de dos for-
mas. Cualquier cambio realizado a travs de un alias se refleja en lo que ven todos los dems alias.
Clases y Objetos 181
Respecto a la referencia a objetos, qu pasa cuando un objeto no tiene ya referencia que apunte a
l, como el objeto cuenta_1 en el ejemplo de la Figura 7.13? En este caso no hay forma de referir-
nos a dicho objeto. En principio el objeto se mantendra en memoria, ocupando espacio intilmente.
Por esta razn, Java dispone de un mecanismo denominado recogida automtica de basura que elimi-
na de la memoria los objetos que no se utilizan ms. En los lenguajes orientados a objetos donde no
hay recogida automtica de basura, es el usuario el que debe eliminar el objeto usando algn tipo de
mtodo destructor.
Ahora podemos entender por qu los objetos se pasan por referencia. En realidad lo que se pasa es
la referencia al objeto y, estrictamente hablando, la referencia se pasa por valor. Lo que ocurre es que
ese valor de la referencia refiere a la posicin de memoria donde se almacena el objeto, es decir, que
el nombre local (en el mtodo) de la referencia es un alias del nombre externo. Por esa razn ambos
se refieren a la misma zona de memoria y los cambios que hagamos al objeto en el mtodo se reflejan
al salir de l. Hay que tener en cuenta que cuando un mtodo devuelve un objeto, en realidad devuel-
ve una referencia a ese objeto. La situacin se ilustra en el ejemplo mostrado en el Programa 7.2 don-
de hay dos clases y un mtodo de una de ellas que acepta un objeto de la otra.
class Clase1 {
private int valor=10;
class Clase2 {
private int indicador=0;
class Referencias {
public static void main(String [] args) {
int i;
Clase1 obj1 = new Clase1();
6
En el campo de las estructuras de datos existe un tipo abstracto de datos (TAD) denominado lista. Este TAD es muy
flexible y puede considerarse como una estructura que puede crecer y decrecer segn se necesite. Sus elementos pueden ser
accedidos, insertados o eliminados en cualquier posicin de la lista. La lista de nuestro ejemplo no se corresponde al tipo abs-
tracto de datos lista, sino que es una simplificacin didctica del mismo. El lector interesado puede consultar algn texto espe-
cializado en estructuras de datos (Aho et al., 1987; Smith, 1987; Weiss, 1998).
182 Introduccin a la programacin con orientacin a objetos
obj1.modificar(obj2);
i=obj2.indicador(); // Modificacin a travs de la referencia
System.out.println(i tras aplicar el metodo modificar: +i);
}
}
El resultado es:
En el mtodo main del Programa 7.2 se construye un objeto de Clase1 y otro de Clase2. En pri-
mer lugar se usa el mtodo indicador sobre obj2 para obtener el valor de la variable de ejemplar
indicador de obj2. En este caso el valor es 0. A continuacin, al mtodo modificar de Clase1
se le pasa la referencia al objeto obj2 de Clase2. Dentro del mtodo modificar se declara un par-
metro actual objt de Clase2, aplicndosele el mtodo cambiar de la Clase2. Esto implica que la
variable indicador de objt se actualiza con el contenido de la variable valor de Clase1 que es
10. Al acabar el mtodo modificar se vuelve al principal y se usa el mtodo indicador sobre obj2
para obtener el valor de la variable indicador de obj2. Ahora el resultado es 10. Esto se debe a que
en el paso de parmetros al mtodo modificar, el parmetro actual (obj2) y el formal (objt) devie-
nen en sinnimos del mismo objeto. Por eso las modificaciones que se realizan en objt se realizan en
el mismo objeto que el referido por obj2.
class Nodo {
String texto;
Nodo siguiente;
}
Aqu declaramos una referencia, siguiente, que puede referir a un objeto de clase Nodo. Fij-
monos en que estamos slo declarando la referencia y no creando un objeto. Ahora podramos ir cre-
ando objetos de clase Nodo y encadenarlos, enlazarlos, haciendo que la referencia siguiente de uno
de ellos refiera al siguiente objeto creado, vase la Figura 7.14.
Hemos creado una lista enlazada 6 de objetos. El primer nodo en la lista se puede referenciar usan-
do una variable separada, el segundo, a partir del primero usando la referencia siguiente del primer
objeto de clase Nodo, etc. El ltimo nodo de la lista tendr una referencia siguiente que al no refe-
rir a nada contendr el valor null, indicando el final de la lista.
Vamos a ver un ejemplo que ilustre de modo bsico cmo se puede construir una lista enlazada.
Consideremos el siguiente ejemplo. Se trata de un programa que gestiona una serie de libros identifi-
cados por su ttulo. El programa debe ser capaz de ir aadiendo libros a la serie mantenida y de poder
imprimir los ttulos de toda la serie cuando se le indique. Con esta definicin abordemos las etapas de
anlisis y diseo.
Anlisis
Identificamos una clase Lista que contendr toda la informacin a manejar (en este caso solo el ttu-
lo) y que realiza dos operaciones; aadir un libro a la lista e imprimir la lista.
Diseo
Implementaremos la lista como una estructura dinmica, como una lista enlazada. As, en el dominio
de la solucin identificamos una clase Nodo que representar a cada libro dentro de la lista y que
incluir una referencia de la propia clase Nodo para el enlazamiento dinmico. Como operaciones, esta
clase debe poder conectar un objeto de la misma a otro, debe poder devolver el ttulo para imprimirlo
y, para poder recorrer la lista, debe devolver el nodo siguiente a aquel en el que estamos. Por lo tanto,
necesitaramos dos clases, una clase Lista y una clase Nodo que representara cada elemento de la
lista. La relacin entre ellas sera estructural, la lista est formada por un conjunto de nodos. El dia-
grama de clases correspondiente se muestra en la Figura 7.15. El programa principal crear un objeto
de clase Lista, aadir algunos elementos y luego imprimir la lista.
Lista Nodo
1 *
primero: Nodo titulo:String
siguiente:Nodo
Lista ( )
incluir (titulo:String):void Nodo ( )
imprimir ( ):void void poner (siguiente_nodo:Nodo)
coger ( ):Nodo
titulo ( ):String
184 Introduccin a la programacin con orientacin a objetos
Codificacin
En el Programa 7.3 implementamos el diagrama de clases de la Figura 7.15 y un ejemplo de progra-
ma principal que usa dicha estructura de clases.
class Ejemplo_lista {
public static void main(String [] args) {
class Nodo {
private String titulo;
private Nodo siguiente; /*Se refiere al siguiente elemento
de la lista */
class Lista {
private Nodo primero;
public Lista() {
primero=null; // Almacena el primer elemento de la lista
Clases y Objetos 185
} // Fin constructor
Nodo aux;
aux.poner(elemento);
}
} // Fin metodo incluir
Es interesante destacar en el Programa 7.3 cmo la clase Lista usa una referencia de clase Nodo
llamada primero para almacenar el primer nodo de la lista. Tngase en cuenta que a diferencia de una
matriz, en nuestra lista no hay un ndice que nos permita alcanzar directamente uno de los nodos. Lo
nico que podemos hacer es salvar el primer nodo de la lista y empezar a movernos a partir de l. Tam-
bin merecen especial atencin los mtodos imprimir e incluir de la clase lista. Considermoslos
por separado.
a) Mtodo imprimir
La condicin de finalizacin del bucle for es que aux sea nulo. De esta forma vamos recorrien-
do la lista hasta llegar al ltimo objeto referido que es el objeto nulo (null). As, se imprimen todos
los ttulos. Si en lugar de aux!=null pusiramos aux.coger()!= null, como se muestra a conti-
nuacin:
el ltimo ttulo no se imprimira porque al llegar al penltimo nodo aux.coger() ya devuelve null.
b) Mtodo incluir
public void incluir(String cadena) {
Nodo aux=primero;
for (aux=primero;aux.coger()!= null;aux = aux.coger());
aux.poner(elemento);
}
} // Fin metodo incluir
7
En el Captulo 8 trataremos el concepto de paquetes con detalle. De momento baste considerar un paquete como un
conjunto o biblioteca de clases identificadas con un nombre.
8
Las interfaces representan un mecanismo adicional de abstraccin basado en herencia y como tal se tratarn en deta-
lle en el Captulo 8. De momento baste con considerarlas como un tipo especial de clases.
Clases y Objetos 187
Obsrvese que el alcance del bucle es una sola sentencia. Dicho de otra forma, el bucle se usa para
recorrer la lista entera hasta que la referencia aux refiera al ltimo nodo. Es en la siguiente sentencia,
ya fuera del bucle, donde asignamos el nodo nuevo.
7.8. MODIFICADORES
Comencemos definiendo qu es un modificador. Un modificador es una palabra reservada que espe-
cifica una caracterstica particular de un elemento de un lenguaje de programacin (Winder y Roberts,
2000). Por ejemplo, la palabra final, que es modificador usado para declarar constantes. En Java las
clases, los objetos y los miembros de las clases pueden estar afectados por modificadores. Veamos los
distintos tipos de modificadores.
Estos tres modificadores se pueden aplicar a las variables y a los mtodos de una clase. Vamos
a considerar en este captulo los dos primeros. El tercero lo detallaremos cuando hayamos visto
herencia y paquetes.
Cuando una variable o mtodo va precedido del modificador public se puede invocar desde fue-
ra de la clase a la que pertenece. Cuando el modificador es private (privado) la variable o mtodo
slo se puede invocar desde dentro de la clase, no se puede invocar externamente pero puede ser usa-
do en cualquier lugar dentro de la definicin de la clase. Lgicamente, los mtodos constructores son
forzosamente de tipo public, pues hay que invocarlos desde fuera de la clase. Si no se indica una visi-
bilidad especfica, se asigna el valor por defecto que implica que el miembro (dato, mtodo) es acce-
sible slo a las clases del mismo paquete 7.
Un cliente de una clase (un objeto) debe ser capaz de referirse a los mtodos de servicio (la inter-
faz pblica), pero no debe ser capaz de invocar directamente a los mtodos internos (de soporte).
Por tanto, debe usarse el modificador de visibilidad public para los mtodos de servicio, pero cual-
quier mtodo que no defina un servicio debe declararse private (privado). Por ejemplo, el mto-
do main siempre es pblico porque es ejecutado directamente por el intrprete de Java desde el
exterior. Como ya hemos indicado, un objeto encapsula informacin y las variables slo se deben
cambiar a travs de un mtodo del propio objeto. Por tanto, todas las variables deberan declararse
private. En resumen,
Como se indicar ms adelante si queremos que los mtodos o variables sean accesibles a travs
de una relacin de herencia, pero no a clases fuera de dicha relacin, habra que declararlos como
protected (protegido) y no private. De momento usaremos el modificador private.
Los modificadores de visibilidad tambin se aplican a las clases. En este caso slo se puede utili-
zar el modificador public. Con public la clase ser accesible desde cualquier sitio, no estando res-
tringido dicho acceso a relaciones de herencia o de pertenencia al mismo paquete.
El efecto de los modificadores resulta ms claro en forma tabular, vase la Tabla 7.1 8:
En notacin UML los tres modificadores se indican en los miembros (datos, mtodos) de una cla-
se precediendo su nombre con un + para los pblicos, un # para los protegidos, y un - para los priva-
dos (Booch et al., 2000; Rumbaugh et al, 2000), vase la Figura 7.16.
Clase Ejemplo
# dato_protegido
- dato_privado
+ mtodo_pblido ( )
# mtodo_protegido ( )
- mtodo_privado ( )
Figura 7.16. Notacin UML para denotar miembros pblicos, protegidos y privados de una cla-
se
a) Variables static
De momento hemos visto dos tipos de variables, las variables locales, que son las declaradas dentro
de un mtodo y las variables de ejemplar, que estn declaradas en la clase pero no dentro de un mto-
do. Estas variables se denominan de ejemplar porque se accede a ellas a travs de un ejemplar parti-
cular (un objeto) de una clase. Cada objeto tiene espacio de memoria distinto para cada variable, es
decir, cada objeto puede almacenar un valor distinto en esa variable.
Hay otra clase de variables, las denominadas variables static (estticas) o variables de clase. Se
construyen usando el modificador static. ste hace que una variable sea global a todos los obje-
tos de la clase. Dicho de otra manera, slo hay una copia en memoria de esa variable y se comparte
por todos los objetos de la clase. Cambiar el valor de una de estas variables en un objeto lo cambia en
todos los dems de la misma clase. Las variables static no se pueden declarar dentro de los mto-
dos, porque seran locales al mtodo en cuestin. Las variables con atributo de static son accesibles
a travs del nombre de la clase o del nombre de un objeto de dicha clase.
Las constantes, que llevan el modificador final, a menudo se declaran como estticas. As, el
valor de la constante ser compartido por todos los objetos de la clase. Al igual que las variables est-
Clases y Objetos 189
Programa 7.5. Ejemplo del uso de una variable y un mtodo estticos (continuacin)
class Alumno{
private String nombre;
private int matricula;
static long numero;
public Alumno(){
numero = numero+1; // Incremento de la variable esttica
}
}//fin clase Alumno
class Principal{
public static void main(String args[]){
Alumno alumno1 = new Alumno();
System.out.print(Alumno.numero);
System.out.print(alumno1.numero);
Alumno alumno2 = new Alumno();
System.out.print(Alumno.numero);
}// fin main
}//fin clase Principal
Como el Programa 7.4 muestra, se puede acceder a la variable esttica usando el nombre de la cla-
se (Alumno.numero) o el del objeto (alumno1.numero). La segunda forma es mejor evitarla para
no dar la falsa impresin de que numero es una variable de ejemplar.
b) Mtodos static
Los mtodos tambin pueden declararse de tipo static. Se habla entonces de mtodos estticos o de
clase. Los mtodos estticos estn asociados a la clase y no a ejemplares de dicha clase. Un mtodo
esttico no se invoca slo a travs de un objeto (instancia) de una clase, sino tambin a travs de la
clase misma. Por eso no hace falta tener un objeto de esa clase para poder invocarlo.
Los mtodos estticos no operan en el contexto de un objeto particular, por esta razn no pueden
usar variables de ejemplar, las cuales slo existen en un ejemplar de una clase. El compilador dara un
error. Un mtodo esttico slo puede usar variables que sean estticas o locales al mtodo, porque si
no, la variable no existira hasta que no haya un objeto de la misma. Con las variables estticas no hay
problema, porque stas existen independientemente de objetos especficos.
El mtodo main de una clase en Java debe declararse de tipo static para poder invocarlo sin que
exista un objeto de dicha clase. Otros ejemplos son los mtodos de la clase Math en el paquete
java.lang como:
Como estos mtodos son de tipo esttico se pueden invocar sin crear un objeto de clase Math. En
el Programa 7.5 se ilustra el uso de un mtodo y variables estticas desde el mtodo main.
class SomosEstaticos{
static int estatica1=13;
static int estatica2=80;
static void estatico( ){
System.out.println(variable1= + estatica1);
}
}
class Principal2{
public static void main(String args[]){
SomosEstaticos.estatico();
System.out.println(variable2= + SomosEstaticos.estatica2);
System.out.print(variable1= + SomosEstaticos.estatica1);
}
}
variable1=13
variable2=80
variable1=13
Obsrvese cmo el mtodo main del Programa 7.5 invoca al mtodo esttico a travs del nombre
de la clase y cmo se accede a las variables estticas a travs del nombre de la clase.
EJERCICIOS PROPUESTOS
Ejercicio 1.* El siguiente diagrama de clases, incompleto, representa la estructu-
ra bsica de un programa para recopilar los encargos de productos
de una compaa.
Clases y Objetos 191
Encargo Cliente
encargoID
fecha 1 * nombre
valor direccin
pago ( )
*
Producto Cliente corporativo Cliente particular
pago ( ) pago ( )
Ejercicio 2.* Una empresa de desarrollo de software tiene una serie de emplea-
dos que forman parte de un equipo de desarrollo. Estos empleados
pueden ser contratados, cobrando por horas, o bien programadores
en plantilla con un sueldo fijo. En el equipo, uno/a de los programa-
dores/as en plantilla acta como director/a cobrando un comple-
mento adicional. Cree un diagrama de clases que represente las
relaciones existentes en el sistema descrito.
Ejercicio 3.* Una universidad est formada por una serie de departamentos a los
que estn asignados los distintos profesores. Cada curso impartido
est vinculado a un departamento y cada profesor puede impartir
uno o ms cursos. Los profesores pueden ser titulares o asociados.
Los alumnos pertenecen a la universidad y asisten a uno o ms cur-
sos. A su vez, los alumnos pueden ser de dos tipos: de curso com-
pleto o de curso de verano. Cree un diagrama de clases que muestre
las distintas relaciones existentes en este modelo de universidad.
Ejercicio 4.* Considere una recta en el plano cartesiano. Implemente una estruc-
tura de clases que permita obtener su pendiente y su ordenada en
el origen. Para ello se debe poder caracterizar la recta por medio de
dos puntos o por medio de un punto y de la pendiente. Tambin
debe ser posible determinar si los dos puntos que se pasan son igua-
les o no, y cul es el valor de y (ordenada) que corresponde a un
valor de x (abscisa) determinado.
Ejercicio 5.* Implemente una estructura de clases que represente una serie de
personas caracterizadas por el nombre (compuesto de nombre de
pila y dos apellidos) y el nmero del DNI. Debe ser posible imprimir
192 Introduccin a la programacin con orientacin a objetos
Ejercicio 6.* Modifique el ejemplo anterior para poder construir un rbol gene-
algico donde se establezca dinmicamente un vnculo que indique
qu persona es el padre y cul la madre de una persona dada.
class Ejercicio {
public static void main(String [ ] args){
Clase1 obj1=new Clase1();
obj1.imprimir(3.2);
}
}
class Clase1 {
private double valor=9.8;
public void imprimir(double valor) {
System.out.println(valor);
}
}
Ejercicio 8.* Desarrolle un programa que sirva para evaluar el valor de un polino-
mio, cuyo grado y coeficientes se introducen por teclado, en un
valor de abscisa determinado.
REFERENCIAS
AHO, A. V., HOPCROFT, J. E. y ULLMAN, J. D.: Data Structures and Algorithms, Addison-Wesley, 1987.
ARNOW, D. y WEISS, G.: Introduccin a la programacin con Java, Addison-Wesley, 2001.
BlueJ: http://www.bluej.org, ltima visita realizada en junio 2002.
BOOCH, G., RUMBAUGH, J. y JACOBSON, I.: El Lenguaje Unificado de Modelado, Addison Wesley, Primera reim-
presin, 2000.
BRUEGGE, B. y DUTOIT, A. H.: Ingeniera del software orientado a objetos, Primera Edicin, Prentice-Hall, 2002.
LARMAN, C.: UML y Patrones, Prentice Hall, Primera Edicin, 1999.
LEWIS, J. y LOFTUS, W.: Java Software Solutions, Addison-Wesley, 1998.
MEYER, B.: Construccin de Software Orientado a Objetos, Segunda Edicin, Prentice-Hall, 1999.
MILLER, G. A.: The Magical Number Seven, Plus or Minus Two: Some Limits on Our Capacity for Processing
Information, Psychol. Rev., 63(2), 81-97, 1956.
PRESSMAN, R. S.: Ingeniera del Software, Quinta Edicin, Mc Graw Hill, 2002.
RUMBAUGH, J., JACOBSON, I., y BOOCH G.: El Lenguaje Unificado de Modelado. Manual de Referencia, Addison-
Wesley, 2000.
SMITH, H. E.: Data Structures. Form and Function, Harcourt Brace Jovanovich, Publishers, 1987.
WEISS, M. A.: Data Structures & Problem Solving using Java, Addison-Wesley, 1998.
WINDER, R. y ROBERTS, G.: Developing Java Software, John Wiley & Sons, Ltd, 2000.
8
Herencia
Sumario
8.1. INTRODUCCIN
Las tres caractersticas fundamentales que definen la programacin orientada a objetos son: encapsu-
lacin, herencia y polimorfismo. En el captulo anterior hemos visto la utilidad de la abstraccin,
expresada a travs de la encapsulacin de informacin y procedimientos, e implementada por medio
de los conceptos de clase y objeto. Encapsulando aumentbamos la cohesin y disminuamos el aco-
plamiento en los programas, lo que se traduca en una mayor reutilizabilidad y fiabilidad del softwa-
re, mayor facilidad de desarrollo, de pruebas y de mantenimiento (aumento de la calidad del software).
Otra caracterstica bsica de la orientacin a objetos es la herencia. La herencia es un mecanismo
de abstraccin consistente en la capacidad de derivar nuevas clases a partir de otras ya existentes. La
herencia se suele usar cuando la clase hija y la padre comparten cdigo comn. De esta manera no es
necesario repetir ese cdigo comn, sino que ste se transmite (se hereda) de la clase padre a la clase
hija. As, podemos centrarnos slo en las caractersticas propias de la clase hija que se est desarro-
llando. La herencia permite reutilizar cdigo y tambin mantener la complejidad de las nuevas clases
dentro de lmites manejables. Adems, por medio de la herencia podemos construir jerarquas de clases
que podemos manejar cmodamente a travs de los paquetes de clases, como veremos ms adelante.
Otra caracterstica clave de la orientacin a objetos es el polimorfismo. El polimorfismo permite
que diferentes objetos puedan responder al mismo mensaje en diferentes formas (Arnow y Weiss,
1998). Usando polimorfismo podemos tratar de forma unificada diferentes clases relacionadas por
herencia. Como veremos, el polimorfismo involucra la denominada sobrescritura de mtodos y la uti-
lizacin de referencias.
La caracterstica de herencia dota de una gran potencia a la programacin orientada a objetos. De
hecho, para ser estrictos, la programacin usando objetos pero sin herencia se denomina programacin
basada en objetos. La programacin orientada a objetos implica el uso de herencia (Joyanes, 1998).
8.2. HERENCIA
La herencia es una tcnica de desarrollo de software muy potente y, como hemos visto, una carac-
terstica que define la programacin orientada a objetos. La herencia relaciona los datos y mtodos de
clases nuevas con los de clases ya existentes, de forma que la nueva clase se puede entender como una
extensin de la antigua. En cualquier caso, la nueva clase es un tipo particular de la clase original.
Abordemos el tratamiento de esta caracterstica de la orientacin a objetos.
mamferos, pero adems tienen caractersticas que los hacen diferentes de los otros mamferos. En tr-
minos de software, tendramos una clase, Mamferos, que tendra ciertas variables y mtodos que des-
cribiran el estado y comportamiento de los mamferos. La clase Caballo se podra derivar de la clase
Mamfero, heredando automticamente las variables y mtodos que contiene la clase Mamfero.
Adems, se pueden aadir nuevas variables y mtodos a la clase Caballo, la clase derivada, que defi-
nan al Caballo. A su vez, la clase Mamfero sera un tipo particular de vertebrado que a su vez es un
tipo particular de animal. La Figura 8.1 muestra una jerarqua de clases que va desde lo ms general
(animal) a lo ms concreto (caballo).
El proceso de derivacin de una clase nueva por herencia corresponde a la existencia de una rela-
cin particular entre las dos clases: la relacin es-un que vimos en el captulo anterior. En esta rela-
cin, la clase derivada es una versin ms especfica de la original. Esto debe estar bien claro, la
herencia implica que la clase nueva es un tipo particular de la clase original. As, todos los ejemplares
de la clase nueva pertenecen al tipo de la clase antigua, pero no al revs. Por ejemplo, dada la relacin
de herencia entre caballo y mamfero se puede decir que Babieca (ejemplar de la clase caballo) es un
mamfero. Esto no quiere decir que todos los mamferos sean caballos, pero s que todos los caballos
son mamferos. Como notacin, indiquemos que la clase original que se usa para derivar una clase
nueva se denomina clase padre, superclase, clase base o ascendiente. La clase derivada se llama clase
hija, subclase o descendiente.
Los lenguajes orientados a objetos proporcionan mecanismos para implementar la herencia. En
Java se usa la palabra reservada extends para indicar que una clase nueva est siendo derivada de (es
decir que extiende a) otra. La sintaxis bsica es:
Animal
Invertebrado Vertebrado
Caballo
Figura 8.1. Organizacin jerrquica (de parte) del reino animal. El diagrama utiliza la notacin
UML para la relacin de herencia
196 Introduccin a la programacin con orientacin a objetos
Por ejemplo, si existe una clase Vehculo y queremos derivar una nueva clase Coche haramos,
Figura 8.2. Relacin entre clases en los casos de herencia mltiple y simple usando notacin
UML
1
El concepto de paquete de clases se expone posteriormente en este captulo en relacin con la organizacin de rela-
ciones jerrquicas.
Herencia 197
bin estos atributos generales (ao, autor, ttulo). Sin embargo, algo que distingue una tesis de otro tipo
de publicacin (como un libro o una revista) es que se presenta en un departamento universitario. Si
representamos la clase publicacin y la clase tesis con los datos indicados y con mtodos de retorno
para estos datos tendramos el diagrama de clases de la Figura 8.3.
La relacin de herencia entre la clase Publicacion y la clase Tesis mostrada en la Figura 8.3
se implementara de la forma siguiente:
Publicacin
titulo:String
autores:String
fecha_publicacion:int [ ]
titulo ( ):String
autores ( ):String
fecha ( ):int [ ]
Tesis
departamento:String
departamento ( ):String
Figura 8.3. Relacin de herencia entre las clases Publicacin y Tesis usando notacin UML
class Publicacion {
protected String titulo;
protected String autores;
protected int[]fecha_edicion=new int [3];
fecha_edicion [0]=dia;
fecha_edicion [1]=mes;
fecha_edicion [2]=agno;
}
Las variables fecha_edicion, titulo y autores se han declarado protected, por lo que se
puede heredar en la clase hija pero no se puede acceder directamente a ellas desde clases externas a la
relacin de herencia. Un ejemplo que usa las dos clases implementadas anteriormente se presenta en
el Programa 8.1.
Programa 8.1. Programa que usa la relacin de herencia entre las clases Publicacion y
Tesis
class Herencia {
public static void main(String [] args) {
Tesis una_tesis= new Tesis
(Simulacion de sistemas biologicos,
Francisco Perez,Informatica,1,3,2002);
System.out.println(Titulo: +una_tesis.titulo());
System.out.println(Autor/es: +una_tesis.autores());
System.out.println(Departamento: +una_tesis.departamento());
Fijmonos en que desde el objeto una_tesis estamos llamando a mtodos que se han heredado y
que se manejan variables que tambin se han heredado. Sin embargo, la variable departamento y el
mtodo departamento() que se declaran en la clase hija no se podran invocar desde objetos de la cla-
se padre.
Cuando hablamos de heredar mtodos surge la cuestin de qu pasa con los constructores. No tie-
ne sentido que un mtodo constructor se herede, puesto que su misin es crear ejemplares de una cla-
se dada. Si estamos en una clase hija querremos crear ejemplares de ella y no de la superclase. Los
constructores no se heredan ni aunque tengan visibilidad pblica. Sin embargo, los constructores se
suelen usar para inicializar variables y puede interesarnos que las variables de la clase padre que se
heredan se inicialicen cuando creemos un objeto de la clase hija. De esta forma, si existen varias cla-
Herencia 199
ses hijas no tendramos que estar repitiendo el cdigo de inicializacin de las variables heredades en
todas y cada una de ellas. Para ello, habra alguna forma de inicializar estas variables como hace el
constructor de la clase padre pero sin crear un objeto de la clase padre? En Java es posible conseguir-
lo con la referencia super (de superclase) que obedece a la siguiente sintaxis:
super(lista_de_ parmetros;
donde, lista_de_ parmetros especifica los parmetros del constructor de la superclase. Si se uti-
liza super(), tiene que ser la primera sentencia ejecutada dentro del constructor de la subclase. Como
el constructor puede estar sobrecargado en la superclase, super() puede ser llamado utilizando cual-
quier forma definida en la superclase. El constructor ejecutado ser aquel que tenga la misma firma.
En una jerarqua de herencia de varios niveles, super() siempre se refiere a la superclase inmediata-
mente superior a la clase que lo utiliza. Usando esta superreferencia lo que conseguimos es utilizar el
cdigo del constructor de la clase padre. El comportamiento es el que obtendramos si hubiramos
copiado el cdigo del constructor de la clase padre y lo hubiramos pegado en el constructor de la cla-
se hija. Veamos una variacin de la implementacin anterior de la estructura jerrquica de clases
Publicacin y Tesis donde se usa la referencia super().
class Publicacion {
String titulo;
String autores;
protected int[]fecha_edicion=new int [3];
// Mtodo constructor
public Publicacion(String titulo, String autores,
int dia, int mes, int agno) {
this.titulo=titulo;
this.autores=autores;
fecha_edicion [0]=dia;
fecha_edicion [1]=mes;
fecha_edicion [2]=agno;
}
// Mtodos de servicio
public String titulo() {
return titulo;
}
public String autores() {
return autores;
}
public int [] fecha() {
return fecha_edicion;
}
} // Fin clase publicacin
}
}
Supongamos que en una clase hija declaramos una variable o definimos un mtodo con el mismo nom-
bre o firma que una variable o mtodo heredadas, qu ocurre en este caso? Vemoslo, considerando
en primer lugar las variables y luego los mtodos.
a) Variables
Cuando una variable se hereda y en la clase hija declaramos una nueva variable con el mismo identi-
ficador, la nueva variable es la que se usa. Se dice que hemos enmascarado la variable original. No es
que hayamos reasignado su contenido, sino que hemos creado una variable nueva. Lgicamente, cuan-
do desde la clase hija usemos el identificador de la variable se usa la variable definida en la clase hija,
con el valor que se le haya asignado. La variable de la clase padre sigue existiendo, y se podra acce-
der a ella pero indicando explcitamente que nos referimos a la clase padre con el prefijo super. En
el Programa 8.2 se ilustra un ejemplo.
class Padre{
protected int dato=10;
Como podemos ver en el Programa 8.2, dato en Padre almacena el valor 10. En Hija, dato
se enmascara recibiendo ahora el valor 5. Esto no implica que la antigua variable haya desapareci-
do, sino que en la clase Hija cuando usemos el identificador dato nos vamos a referir a la defini-
cin local. La otra variable dato no ha desaparecido y podemos acceder a ella por medio del prefijo
super, como hacemos en el mtodo imprime_ padre(). Como se puede comprobar en el Progra-
ma 8.2, el enmascaramiento de variables suele producir cdigo confuso, por lo que se recomienda
evitar su uso.
b) Mtodos
Un comportamiento similar ocurre cuando definimos un mtodo con la misma firma (nombre y par-
metros) que otro que se hereda. En este caso, el mtodo al que se accede a travs de los objetos de la
clase hija es al definido en la clase hija. Se dice que el nuevo mtodo sobrescribe el mtodo heredado.
Es importante distinguir la sobrescritura de la sobrecarga:
comportamiento de la clase. El sistema sabe qu versin del mtodo debe usar, porque conoce la cla-
se del objeto en el que se invoca el mtodo. Este proceso es transparente para el usuario. Veamos un
ejemplo de sobrescritura de mtodos en el Programa 8.3.
class ClaseX {
protected int n=25;
class Herencia {
public static void main(String [] args) {
ClaseX x = new ClaseX();
ClaseY y = new ClaseY();
x.imprimir();
y.imprimir();
}
}
ClaseY se hereda de ClaseX con lo que el mtodo imprimir en principio contendra el mismo
cdigo que el de ClaseX y al heredar tambin la variable n, el resultado del Programa 8.3 parece que
debera ser:
En ClaseX, n= 25
En ClaseX, n= 25
Sin embargo, en ClaseY sobrescribimos el mtodo imprimir, con lo que el cdigo para los obje-
tos de esa clase ser el escrito para dicha clase y el resultado real es:
En ClaseX, n= 25
En ClaseY, m= 10
2
Una vez ms usamos mtodo por semejanza con la notacin de Java.
Herencia 203
Mtodo_B
(Clase padre)
Mtodo_A ?
Mtodo_B
(Clase hija)
}
else {
Clase_Hija objeto = new Clase_Hija();
}
objeto.mtodo_B();
Slo se sabr si el objeto debe ser de una clase u otra despus de haber ledo la variable i. Sin
embargo, a la hora de compilar el compilador no puede saber a qu clase pertenece el mtodo_B que
se invoca en el cdigo, pues esto depende de la historia del programa. Para obtener enlazamiento din-
mico se mantiene para cada objeto una tabla con sus mtodos y se decide en tiempo de ejecucin la
versin a ejecutar de cualquier mtodo sobrescrito, vase la Figura 8.5.
Por defecto, los mtodos en Java usan enlazamiento dinmico lo que implica una disminucin
(pequea) de rendimiento, ya que se consume algn tiempo en determinar dinmicamente el mtodo
a usar. Si queremos optimizar en velocidad un mtodo que sabemos que no va a ser sobrescrito, lo
debemos declarar con modificador final para forzar enlazamiento esttico.
Clase padre
Mtodo_B
(Clase padre)
Mtodo_A
Mtodo_B
(Clase hija)
Clase hija
Figura 8.5. El enlazamiento del Mtodo_A con el Mtodo_B lo realiza dinmicamente el siste-
ma en tiempo de ejecucin
Herencia 205
Figura_cerrada
Polgono
Tringulo
Tringulo_equiltero
La herencia del invariante se manifiesta en que un tringulo equiltero tiene tres lados iguales y
tres lados (Tringulo) y lados rectos (Polgono) y es cerrado (Figura_cerrada).
Tras todas las consideraciones anteriores abordemos un ejemplo donde usemos una jerarqua de
clases. Consideremos un programa para gestionar cuentas de un banco. En todas las cuentas pueden
retirarse fondos y hacerse depsitos. Tenemos unas cuentas (corrientes) que se usan para realizar pagos
y que no proporcionan ningn inters. Estas cuentas tienen una libreta de ahorro (cuenta) asociada para
cubrir descubiertos de la cuenta corriente. Por otro lado las libretas de ahorro proporcionan un inters
(el 4%). Adems, existe otro tipo de libretas de ahorro (libreta 2000) que rinde un mayor inters (8%),
Cuenta_bancaria
1 1
Cuenta_bancaria Libreta_de_ahorro
Libreta_2000
Figura 8.8. Estructura jerrquica de clases para el ejemplo de las cuentas bancarias
Figura 8.9. Diagramas de clase para las clases del ejemplo de las cuentas bancarias
Herencia 207
pero que penalizan la retirada de fondos con un 2% de la cantidad retirada. Con esta descripcin, el
correspondiente diagrama de clases resultado del diseo sera el recogido en la Figura 8.8.
Obsrvese que la relacin entre Cuenta_corriente y Libreta_de_ahorro es estructural, lo que
implica que uno de los atributos de Cuenta_corriente va a ser un objeto de clase Libreta_de_aho-
rro. Continuando con el diseo detallado, los miembros de cada clase podran ser los recogidos en la
Figura 8.9.
Los mtodos y atributos que incorporemos en las clases dependen de los requisitos que nos exi-
jan, aunque es mejor planificar para el cambio. Por ejemplo, si nos indicaran que las cuentas se abren
con saldo inicial de cero y que despus se hacen los ingresos, no tendramos entonces que usar una
variable saldo_inicial en los constructores. Sin embargo, ponindola cubrimos un futuro cambio
consistente en abrir una cuenta con un saldo inicial. Para el caso actual bastara con introducir valor
cero para la variable saldo_inicial. Por la misma razn, se introducen los intereses y la penali-
zacin a travs del constructor.
El cdigo correspondiente a las clases del diagrama de la Figura 8.9 sera,
class Cuenta_bancaria {
protected int numero;
protected double saldo;
super(numero, saldo_inicial);
cuenta_asociada=libreta;
}
Fijmonos en que en los constructores estamos usando la referencia super para llamar al cons-
tructor de la clase padre y no tener que repetir ese mismo cdigo otra vez en las clases hijas. Obsr-
vese tambin cmo el mtodo retirar se sobrescribe en varias clases.
Un posible ejemplo de programa que usara estas clases podra ser el mostrado en el Programa 8.4.
Programa 8.4. Ejemplo de uso de las clases del sistema de cuentas bancarias
class Herencia {
public static void main(String [] args) {
El resultado sera,
Respecto a las relaciones de herencia es interesante conocer que especficamente en Java, todas las
clases heredan de la clase Object. Esta clase constituye el nodo raz de toda la jerarqua de clases de
Java. Esto es importante, todas las clases, predefinidas o creadas por el usuario, heredan implcita-
mente de la clase Object. Esto implica que las siguientes definiciones son equivalentes:
class nombre_clase {
}
sea cual sea la clase considerada. El caso en el que una clase hereda de otra no es bice para que tam-
bin implcitamente se herede de Object. Esta situacin no se considera herencia mltiple.
La clase Object posee mtodos interesantes que heredan todos sus descendientes, como el mto-
do toString(). Este mtodo devuelve una cadena conteniendo una descripcin del objeto que lo lla-
ma. Este mtodo es particularmente til, porque permite que un objeto lleve asociada una cadena.
Cualquier clase puede sobrescribir este mtodo para proporcionar una representacin String de una
clase particular. Si no se sobrescribe y se llama al mtodo, se usar el mtodo de la clase Object que
imprime el nombre de la clase seguido de un cdigo hexadecimal. En el Programa 8.5 se muestra un
ejemplo de cmo en una clase Alumno se sobrescribe el mtodo toString().
class Alumno{
public String nombre;
public int matricula;
// Constructor
Alumno(String nombreAlumno, int matriculaAlumno){
nombre = nombreAlumno;
matricula = matriculaAlumno;
}
}
Herencia 211
class Facultad{
public static void main(String args[]){
Alumno alumno1 = new Alumno(Juan,123);
System.out.println(alumno1);
Alumno alumno2 = new Alumno(Maria,124);
System.out.print(alumno2);
}
}
Alumno@111f71
Alumno@273d3c
Un uso tpico del mtodo toString() es utilizarlo para devolver simplemente el nombre de la
clase o algn cdigo que la identifique. De esa manera invocando a toString() se puede saber a qu
clase pertenece un objeto determinado.
a) Crearlos, indicando qu clases forman parte del mismo y dnde se va a encontrar dicho paquete.
b) Usarlo, importando el paquete tal y como ya hemos visto en programas anteriores.
Para crear un paquete, el programador debe hacer dos cosas: primero identificar los ficheros y cla-
ses que pertenecern al paquete. Segundo, colocar todas las versiones compiladas de las clases (los
ficheros .class) en un subdirectorio con el mismo nombre del paquete. As, si tenemos un fichero
con varias clases, la sintaxis sera:
3
En realidad el mecanismo no controla si las clases estn relacionadas o no. Podemos entender el mecanismo de paque-
tes como una tcnica para agrupar clases. Ser responsabilidad del diseador/a el incluir en el paquete unas clases u otras.
212 Introduccin a la programacin con orientacin a objetos
class nombre_clase {
...
}
class nombre_clase {
...
}
La clusula package debe estar colocada al principio del fichero. Todas las clases que haya en
el fichero donde aparezca la clusula package se aaden al paquete. Las clases que son pblicas
(modificador public) sern accesibles desde otras clases aunque no estn en el mismo paquete.
Las clases sin modificador (valor por defecto) slo son accesibles a otras clases dentro del mismo
paquete.
Si queremos que todas las clases existentes en distintos ficheros formen parte del mismo paquete,
debemos colocar la sentencia package nombre_ paquete; como primera lnea en todos los fiche-
ros. En Java todas las clases estn contenidas en un paquete. Si no aparece la declaracin de un paque-
te en un fichero dado, las clases de ese fichero pertenecern al paquete por defecto que es el paquete
sin nombre. Slo debe aparecer una clusula package por fichero.
Veamos un ejemplo. Creemos un paquete con una clase para lectura cmoda. Se va a tratar de una
clase pblica (para poder usarla desde cualquier parte) donde vamos a incluir una serie de mtodos
estticos (para no tener que crear objetos de la clase) que lean un entero (int) y una lnea como cade-
na. En un mismo fichero pondramos lo siguiente,
package LecturaComoda;
import java.io.*;
4
El uso de la barra normal, /, o la barra invertida, \, para identificar los distintos subdirectorios depende del sistema ope-
rativo empleado. Aqu se usa la barra normal por ser la notacin del sistema ms extendido (al menos en nmero de usuarios).
Herencia 213
o
import nombre_ paquete.*;
en el fichero donde aparezca la clase que va a usar alguna de las clases contenidas en el paquete. Pode-
mos usar tantos paquetes como queramos, pero las sentencias de importacin deben colocarse inme-
diatamente despus de la sentencia de declaracin de paquete o si no existe, al principio del fichero.
De las dos formas de import mencionadas anteriormente, la primera importar slo la clase indi-
cada. La segunda forma, la del .*, permitir que todas las clases de ese paquete sean accesibles a todas
las clases del fichero actual. Como ya dijimos, por defecto siempre se importa automticamente el
paquete predefinido java.lang.
Es posible usar las clases de un paquete sin importarlo, pero hay que indicar el nombre del paque-
te precediendo a la clase de la forma siguiente,
nombre_ paquete.nombre_clase
Veamos cmo usar la clase Lectora del paquete LecturaComoda sin importarlo en el Progra-
ma 8.6.
Programa 8.6. Ilustracin del uso de las clases de un paquete sin importarlo
import java.io.*;
class Herencia {
public static void main(String[] args) throws IOException {
int valor = LecturaComoda.Lectora.read_int();
String linea = LecturaComoda.Lectora.read_line();
System.out.println(valor);
System.out.println(linea);
El uso normal, sin embargo, sera con la clusula import, vase el Programa 8.7.
Programa 8.7. El programa del Ejemplo 8.6 importando el paquete con la clusula import
import java.io.*;
214 Introduccin a la programacin con orientacin a objetos
import LecturaComoda.*;
class Herencia {
public static void main(String[] args) throws IOException {
int valor = Lectora.read_int();
String linea = Lectora.read_line();
System.out.println(valor);
System.out.println(linea);
Hay un caso en el que se necesita la primera forma. Es cuando se usan dos paquetes en los que
existe una clase con el mismo nombre. En ese caso debemos identificar la clase de forma absoluta,
incluyendo el nombre del paquete al crear objetos de la misma, como en el Programa 8.6.
8.2.5. POLIMORFISMO
Polimorfismo indica en orientacin a objetos que una misma identificacin puede referir a distintas
entidades. Se trata de una de las caractersticas definitorias de la orientacin a objetos. El polimorfis-
mo es la capacidad de una entidad determinada (entindase referencia) de conectarse a objetos de varias
clases relacionadas por herencia (Meyer, 1999). Esta capacidad tiene sentido y sirve para algo si esos
distintos objetos de diferentes clases tienen mtodos sobrescritos, que son los que se van a invocar a
travs de la referencia polimrfica. El polimorfismo implica la posibilidad de usar una sola referencia
para referirse a varios objetos relacionados jerrquicamente. Generalmente, en una clase padre se decla-
ra un mtodo que es el que se va usar polimrficamente. Entonces, esa misma funcin (mtodo) se rede-
fine en clases que son derivadas de la clase padre, es decir, el mtodo se sobrescribe. As, existirn
mtodos en las clases descendientes con la misma firma que el de la clase padre. Si un objeto de la cla-
se padre se declara en un programa, la definicin del mtodo original que se encuentra en la clase padre
ser la que se invoque cuando se llame al mtodo. Sin embargo, si un objeto de una clase hija se asig-
na posteriormente a la referencia de la clase padre, entonces se invoca la definicin de mtodo para la
clase hija. Como podemos ver, hay una clara diferencia entre una simple sobrecarga y el polimorfismo.
Cuando tenemos un mtodo polimrfico necesariamente se usa el enlazamiento dinmico y el sistema
comprueba la clase real del objeto antes de invocar la definicin de mtodo apropiada.
En trminos ms prcticos, el polimorfismo implica que una referencia puede referir a cualquier obje-
to de su clase o a un objeto de una clase descendiente de la primera. Por ejemplo, si tenemos una clase
Vacaciones que es heredada por una clase Navidades, y otra Verano, vase la Figura 8.10, la refe-
Vacaciones
Navidades Verano
Figura 8.10. Relacin jerrquica entre las clases Vacaciones, Navidades y Verano
Herencia 215
rencia de un objeto Vacaciones se puede usar para referir a un objeto de clase Navidades o Verano
de la forma siguiente,
Vacaciones mis_vacaciones;
mis_vacaciones = new Navidades();
Suponiendo que exista un mtodo sobrescrito llamado duracion() que nos d el nmero de das
festivos, la referencia mis_vacaciones (que es de tipo clase Vacaciones) se podra usar para invo-
car el mtodo duracion() en objetos de clase Navidades o Verano. En estos casos, sera la clase
real del objeto al que refiere mis_vacaciones la que determinara qu versin del mtodo se usa.
Esto es importante, es la clase del objeto referido la que indica qu mtodo usar, no la clase con la que
se declara inicialmente la referencia.
Veamos un ejemplo que muestra la mecnica de uso del polimorfismo, vase el Programa 8.8.
class Padre{
void dondeEstoy() {
System.out.println(Estoy en el metodo del Padre);
}
}
class HijaPrimera extends Padre{
void dondeEstoy() {
System.out.println(Estoy en el metodo de la HijaPrimera);
}
}
class HijaSegunda extends Padre{
void dondeEstoy() {
System.out.println(Estoy en el metodo de la HijaSegunda);
}
}
class Herencia {
public static void main(String [] args) {
Padre padre =new Padre();
HijaPrimera hijaPrimera =new HijaPrimera();
HijaSegunda hijaSegunda =new HijaSegunda();
Padre polimorfico;
pero no todos estn implementados. Estas clases son tiles como ayuda en los procesos de anlisis y diseo
de la estructura de clases. De hecho, en este ltimo caso puede ser aconsejable mantener estas clases como
indicacin de diseo. Estas clases no totalmente implementadas se denominan clases diferidas o abstrac-
tas. Su uso, lgicamente, es a travs de la herencia ya que no tiene sentido pretender crear objetos de una
clase no totalmente implementada. La utilidad de las clases abstractas es la de definir la especificacin de
la abstraccin representada por la clase. Los mtodos no implementados, aunque especificados (es decir,
indicando su firma y tipo de retorno), se denominan mtodos abstractos y lgicamente (aunque no nece-
sariamente) una clase abstracta debe contener al menos un mtodo abstracto. Un mtodo abstracto es un
mtodo que no contiene ninguna implementacin, slo tipo de retorno y firma.
En la prctica, las clases abstractas se usan dentro de una jerarqua de clases en la cual la clase abs-
tracta define slo parte de su implementacin, difiriendo el resto a las clases hijas por medio de la
sobrescritura de los mtodos abstractos.
Cualquier clase que contenga mtodos abstractos debe ser declarada como abstracta, aunque se
puede declarar una clase abstracta sin tener ningn mtodo abstracto. Sin embargo, una clase abstrac-
ta no tiene por qu contener mtodos abstractos propios, podra derivar de una clase padre abstracta,
a partir de la cual hereda un mtodo abstracto que no se implementa.
Para declarar una clase como abstracta en Java se usa el modificador abstract. El mismo modi-
ficador se aplica para identificar un mtodo abstracto. La sintaxis para declarar una clase abstracta, por
lo tanto, es:
abstract class nombre_clase {
// cdigo de la clase
}
Anlogamente, para definir un mtodo abstracto dentro de una clase abstracta la sintaxis sera:
abstract visibilidad tipo_retorno nombre_mtodo(lista_ parmetros);
Obsrvese que en lugar de un grupo de sentencias siguiendo a la cabecera del mtodo (implemen-
tacin del mtodo) hay un punto y coma (;). La falta de implementacin indica que el mtodo es abs-
tracto. En un mtodo abstracto no se indica su implementacin, pero s qu parmetros acepta y qu
devuelve. Lgicamente un mtodo abstracto no puede ser declarado como final (pues se debe poder
sobrescribir) ni static (pues no puede ser invocado ya que no tiene implementacin). Tampoco se
pueden declarar constructores abstractos. Sera una operacin sin sentido ya que un constructor siem-
pre se usa para crear un objeto (lo que no se puede hacer con una clase abstracta). En notacin UML
indicamos una clase abstracta especificando su nombre en cursiva. Si esto es complicado de represen-
tar (en una pizarra, por ejemplo) se puede incluir un valor etiquetado (entre llaves, {}) al lado de la
clase para indicar que es abstracta (Booch et al., 1999), vase la Figura 8.11.
No hay restricciones sobre dnde se puede definir una clase abstracta en la jerarqua de clases. Por
ejemplo, una clase abstracta podra derivarse de una clase padre no abstracta. Sin embargo, puesto que
la utilidad de las clases abstractas es especificar una abstraccin, lo lgico es que aparezcan en nive-
les altos (si no en el primero) de las jerarquas de clases.
Clase {abstracta}
Atributos
Procedimientos
Las clases descendientes de una clase padre abstracta no tienen por qu sobrescribir todos los
mtodos abstractos de sus padres. Sin embargo, si una clase hija no sobrescribe todos los mtodos abs-
tractos de la clase padre debe ser declarada tambin como abstracta, pues a travs de la herencia la cla-
se hija contiene los mtodos abstractos. La utilidad prctica de las clases abstractas radica en el hecho
de no poderse crear objetos de dicha clase. As, la clase abstracta especifica una abstraccin. Las cla-
ses hijas heredan la estructura de mtodos de las clases padre y para poder crear objetos de las clases
hijas stas deben sobrescribir todos los mtodos abstractos que han heredado. Al usar un mtodo abs-
tracto en la clase padre, forzamos a que explcitamente todas las clases hijas que lo hereden tengan que
elegir como implementarlo. Si no se usan mtodos abstractos, las clases hijas no estn forzadas a hacer
la eleccin. Pueden usar el mtodo heredado de su clase padre.
La eleccin de qu mtodos deben ser abstractos es una decisin de diseo que afecta a la aplica-
cin completa. Tal eleccin debe ser hecha despus de mucha reflexin y experimentacin. Usando
cuidadosamente las clases abstractas, nos aseguramos que el personal de mantenimiento futuro de la
aplicacin entienda la estructura de clases y su propsito.
Una clase abstracta puede contener datos y mezclar mtodos abstractos con no abstractos. Todo
ser heredado por sus subclases.
Las clases abstractas se pueden usar para declarar referencias a objetos, al igual que hemos usado
las clases no abstractas. De hecho una clase abstracta se puede usar de la misma forma que cualquier
otra clase, excepto que no se pueden crear objetos de ella. Esta forma de trabajar es habitual, se decla-
ra una referencia de la clase abstracta (la clase padre de una jerarqua) para referir a objetos de clases
hijas (no abstractas) por medio de referencias polimrficas. Veamos un ejemplo de esta tcnica. Con-
sideremos un programa que trabaja con distintos tipos de automviles: deportivos, turismos y familia-
res. La relacin jerrquica sera la recogida en la Figura 8.12.
Automvil
{abstracta}
Es normal tener que construir versiones especficas de los mtodos que se van heredando en una
estructura jerrquica. Una buena forma de evitar los posibles problemas, como el tpico de no sobres-
cribir el mtodo heredado con la versin apropiada para la clase hija, y usar por error el heredado del
padre, es utilizar una clase abstracta. En el ejemplo que estamos viendo, supongamos que queremos
incluir un mtodo eslogan() en todos los automviles (podemos imaginar que es un mtodo nuevo
que se aade a los ya existentes) que imprime una frase publicitaria. Si no usramos un mtodo abs-
tracto eslogan() en la clase raz, estaramos obligados a escribir un mtodo eslogan() intil en la
clase Automovil (intil porque no vamos a crear objetos de esa clase sino de sus hijas), para que se
pudiera heredar y sobrescribir en sus clase hijas. En el ejemplo vamos a usar una matriz de referen-
cias de clase Automovil que refieran polimrficamente a los objetos de clase Deportivo, Turis-
mo y Familiar y a usar una sola llamada a un mtodo imprimir_eslogan() al que se le pase la
matriz. El cdigo apropiado sera el mostrado en el Programa 8.9.
Herencia 219
class Herencia {
public static void main(String [] args) {
Automovil [] auto=new Automovil [3];
auto [0]=new Deportivo();
auto [1]=new Turismo();
auto [2]=new Familiar();
imprime_eslogan(auto);
}
}
}
Obsrvese en el Programa 8.9 cmo se crea la matriz de objetos de clase Automovil y cmo des-
pus se hace que las referencias refieran polimrficamente a objetos de las clases hijas. Fijmonos
tambin que el mtodo imprime_eslogan() recibe la matriz y que el mtodo no cambiara aunque
nosotros alterramos la estructura de la jerarqua. Es decir, el programa principal sera el mismo inde-
220 Introduccin a la programacin con orientacin a objetos
8.3.2 INTERFACES
Si en una clase abstracta algn mtodo puede (y podramos decir debe) constar slo de su especifica-
cin, las interfaces slo contienen especificaciones de mtodos. Una interfaz es una coleccin de cons-
tantes y mtodos abstractos. No son clases, pero pueden usarse en la definicin de una clase. La
sintaxis general en Java es:
interface nombre_interfaz {
declaracin de constantes
declaracin de mtodos abstractos
}
Si una clase incluye una o varias interfaces pero no implementa todos los mtodos definidos por
la/s interface/s la clase debe declararse como abstract.
Con respecto a las constantes definidas en la interfaz, stas se comportan como si estuvieran defi-
nidas en la clase que implementa dicha interfaz. Esto nos proporciona una forma de poder distribuir
constantes entre varias clases. Para ello basta con definir las constantes en una interfaz y luego hacer
que las diferentes clases implementen dicha interfaz.
Por lo que respecta al paso de parmetros y las interfaces, hay que decir que un parmetro formal
declarado de tipo interfaz puede aceptar como parmetro actual cualquier clase o subclase que
implemente dicha interfaz.
Tras lo visto, cules son las diferencias entre interfaces y clases abstractas? Dos fundamental-
mente. Una, la simplicidad en la notacin. En una interfaz todos los mtodos son abstractos por lo que
se puede omitir la palabra clave abstract. A su vez, todas las constantes son public, static y
final, por lo que se pueden omitir las palabras clave. La otra razn, ms importante, es que una cla-
se puede implementar ms de una interfaz, mientras que una subclase slo se puede derivar de una cla-
se. Por lo tanto, en cierto sentido, las interfaces en Java nos permiten cierta capacidad de herencia
mltiple. La sintaxis para la implementacin de varias interfaces sera:
Herencia 221
Una vez vistas las diferencias, cules son las similitudes entre interfaces y clases abstractas? Por
un lado, que tanto interfaces como clases abstractas definen mtodos abstractos que sern sobrescritos
posteriormente en clases particulares. Por otro lado, que ambas se pueden usar como nombres de tipos
genricos para referencias. Ambas se pueden usar como parmetros formales.
Para finalizar, comentemos que el uso de clases abstractas e interfaces es importante en el desa-
rrollo de una aplicacin software desde el punto de vista de la encapsulacin y ocultamiento de infor-
macin. Cuanto mayor es un proyecto, ms importante resulta la capacidad de encapsulacin y de
ocultamiento de informacin. Esta capacidad reduce el nmero de detalles que cada miembro espec-
fico del equipo de desarrollo necesita conocer y entender acerca del sistema software. Usando clases
abstractas e interfaces un programador puede identificar los enlaces entre las diferentes partes del sis-
tema sin necesidad de tener que proporcionar detalles de su implementacin. Como las clases abs-
tractas e interfaces no especifican cmo se hace algo, sino qu se debe hacer, son un buen mecanismo
para identificar partes relativamente independientes del sistema software. Despus de haber identifi-
cado un buen conjunto de clases abstractas e interfaces, varios programadores pueden trabajar en para-
lelo, lo que supone una optimizacin de los recursos de desarrollo.
Tras la exposicin anterior veamos un ejemplo de uso de interfaces para especificar constantes.
Abordemos como ilustracin el clculo del volumen molar de una sustancia a una presin y tempera-
tura dadas usando la ecuacin de estado de un gas ideal:
pV= n Na k T
interface Constantes_fisicas {
double Na=6.023e23; // Constante de Avogadro
// en (partculas/mol)
La salida del Programa 8.10 con datos de entrada de 1 atm. (101325 Pa) y 298.15 K (25 C) sera,
Presion: 101325.0 Pa
T: 298.15 K
Volumen molar: 24.46908937495189 litros
En la interfaz hemos definido unas constantes que se usan luego en el programa. Lgicamente, si
hemos creado la interfaz es porque las constantes recogidas tienen inters para varias clases (cada una de
ellas implementar la interfaz). Otro caso tpico de constantes en una interfaz es el de usar unas constan-
tes cuyo valor codifique algo. Por ejemplo, imaginemos un cdigo numrico para los permisos tpicos
sobre un fichero: lectura, escritura y ejecucin. Podramos definir tres constantes en una interfaz denomi-
nadas LECTURA, ESCRITURA Y EJECUCIN, cada una con el valor numrico que se use para indi-
car el permiso correspondiente. Un programa que lea el permiso de un fichero (el cdigo numrico) y
necesite saber lo que significa ese cdigo, slo tendra que implementar la interfaz y comparar con las
constantes. Si se cambia el cdigo numrico de los permisos habra que modificar la asignacin de valo-
res a las constantes en la interfaz, pero no en todos los programas que la usen, lo que es una gran ventaja.
Para informacin ms detallada sobre interfaces, el lector interesado puede consultar el texto de
Eckel (Eckel, 2002).
EJERCICIOS PROPUESTOS
Ejercicio 1.* Cul es el resultado del siguiente programa?
class Programa {
public static void main(String [ ] args){
Clase1 obj1=new Clase1();
obj1.imprimir(4);
Clase2 obj2=new Clase2();
obj2.imprimir(5);
}
}
class Clase1 {
int prop1=0,prop2=0;
public void imprimir(int i){
prop1=prop1+i;
prop2=prop2+i;
System.out.print(prop1+ +prop2+ );
}
}
class Uno {
int i=2;
public void frase() {
System.out.println(Estoy en un objeto de clase
Uno);
}
}
class Driver {
public static void main(String[] args) {
Uno [] lista =new Uno [2];
lista [0]= new Dos();
lista [1]= new Tres();
for (int i=0; i<2; i++){
lista[i].frase();
}
}
}
class Uno {
public void imprime(double x,int j) {
System.out.println(Valor de las variables pasadas:
+x +,+j);
}
}
class Dos extends Uno {
public void imprime(int j,double x) {
System.out.println(Valor de la variable:
+j+,+x);
}
}
class Ejercicio {
public static void main(String [] args) {
Dos objeto1 =new Dos();
double a=5.0;
int i=4;
objeto1.imprime(a,i);
224 Introduccin a la programacin con orientacin a objetos
objeto1.imprime(i,a);
}
}
class Uno {
public void imprime(double x) {
System.out.println(Valor de la variable pasada:
+x);
}
}
class Ejercicio {
public static void main(String [] args) {
Dos objeto1 =new Dos();
double a=5.0;
int i=4;
objeto1.imprime(a);
objeto1.imprime(i);
}
}
Ejercicio 5.* En una empresa hay dos tipos de empleados: los encargados, que
reciben un salario mensual, y los empleados a comisin que reciben
un salario mensual base ms el 10% de las ventas que hayan reali-
zado. Escriba un programa que indique el nombre y apellido de cada
trabajador y su salario. (Use polimorfismo y una clase abstracta.)
Ejercicio 6.* Implemente una interfaz que contenga las constantes SALARIO y
COMISION que se utilizan en la clase TrabajadorComision del ejerci-
cio anterior. Cmo quedara el cdigo de esta clase?
Ejercicio 8.* Implemente el cdigo de una interfaz llamada Primera que conten-
ga dos mtodos A y B. Defina otra interfaz llamada Segunda que
herede de la anterior y adems contenga un mtodo llamado C.
Escriba el cdigo de otra clase llamada Objetos que use la segunda
interfaz. Cuntos mtodos debe implementar esta clase? Imple-
Herencia 225
mente dichos mtodos de forma que cada mtodo imprima una lnea
indicando el nombre del mtodo. Cree un programa que utilice los
mtodos definidos.
REFERENCIAS
ARNOW, D. y WEISS, G.: Introduction to Programming Using Java. An Object-Oriented Approach, Addison-Wes-
ley, 1998.
BISHOP, J. y BISHOP, N.: Java Gently for Engineers & Scientists, Addison-Wesley, 2000.
BISHOP, J.: Java. Fundamentos de Programacin, Addison-Wesley, 1999.
BOOCH, G., RUMBAUGH, J. y JACOBSON, I.: El Lenguaje Unificado de Modelado, Addison-Wesley, 1999.
ECKEL, B.: Piensa en Java, Segunda Edicin, Prentice-Hall, 2002.
JOYANES AGUILAR, L.: Programacin Orientada a Objetos, Segunda Edicin, Osborne McGaw-Hill, 1998, y las
referencias all incluidas.
MEYER, B.: Construccin de Software Orientado a Objetos, Segunda Edicin, Prentice-Hall, 1999.
SAVITCH, W.: Java. An Introduction to Computer Science & Programming, Prentice-Hall, 1999.
9
Ficheros
Sumario
9.1. Introduccin to
9.2. Definicin y uso 9.4. Operaciones sobre los ficheros
9.3. Clasificacin de ficheros 9.5. Ficheros en Java
9.3.1. Clasificacin de acuerdo al tipo de 9.5.1. Manejo de excepciones
acceso 9.5.2. Ficheros de acceso secuencial
9.3.2. Clasificacin de acuerdo al tipo de 9.5.3. Ficheros de acceso directo
organizacin 9.5.4. La clase File
9.3.3. Clasificacin de acuerdo al forma- 9.5.5. Ficheros y objetos
226 Introduccin a la programacin con orientacin a objetos
9.1. INTRODUCCIN
Hasta ahora hemos estado manejando cantidades pequeas de informacin. Esta informacin se gestio-
naba en la memoria central. En los ejercicios que se han visto, la informacin de entrada era tan poca
que se poda introducir por teclado con comodidad. A su vez, el procesamiento era tan sencillo que no
se tardaba mucho en obtener los resultados, y la informacin de salida se poda representar en unas pocas
lneas en la pantalla. Sin embargo, muchas veces se necesita trabajar con grandes cantidades de infor-
macin que, adems, debe guardarse sobre un soporte duradero. En ocasiones, la informacin de entra-
da tiene una estructura compleja y no se puede teclear fcilmente cada vez que se necesita. Otras veces
el procesamiento (sobre todo en los problemas de computacin cientfica) puede ser muy complejo invo-
lucrando muchas horas de clculo, por lo que no se puede estar ejecutando el programa cada vez que
necesitemos algn dato de la informacin de salida. Por su parte, la informacin de salida puede ser tan
compleja que no se puede visualizar en una (o en muchas) pantallas o debe poderse consultar una y otra
vez. Es tambin frecuente que sea necesario usar ficheros auxiliares en el procesamiento, al manejarse
cantidades de informacin que no caben en la memoria central. En todas estas condiciones, la memoria
central no nos sirve (es voltil y limitada) y debemos trabajar con dispositivos de almacenamiento secun-
dario. La herramienta para realizar estas tareas es el fichero 1, que se usa sobre memoria secundaria, aun-
que en ocasiones, en algunos sistemas, se pueda implementar en memoria central.
Fichero
Registros
Campos
1
El trmino archivo tambin est muy extendido y es equivalente. En este texto usaremos preferentemente la palabra
fichero.
Ficheros 227
Un registro suele tener varios campos que almacenan diferente informacin. Por ejemplo, los dife-
rentes datos de un empleado en una empresa formaran los campos de un hipottico registro. Dichos
campos podran ser:
a) Soportes secuenciales.
b) Soportes direccionables.
Los soportes secuenciales son aquellos en los que el acceso se realiza de forma secuencial, de for-
ma que para acceder a una posicin determinada hay que pasar por todas las anteriores (o las poste-
riores si estamos yendo de atrs hacia adelante). Dicho de otra forma, la informacin (los registros)
est almacenada consecutivamente. As, para acceder a un determinado registro, n, se necesita pasar
por los n1 registros anteriores. La secuencia puede corresponder al orden fsico de los registros en el
fichero (organizacin secuencial) o bien al orden de claves (ascendente o descendente) de los registros
(organizacin indexada). Un ejemplo tpico de soporte secuencial es una cinta magntica.
Los soportes direccionables permiten al acceso directo a una posicin dada. Estos soportes se
estructuran de modo que cada elemento de informacin registrado se pueda localizar directamente por
una direccin, no requirindose pasar por los registros precedentes. En estos soportes, los registros
deben poseer un campo clave que los diferencie del resto de los registros del fichero. A travs del valor
del campo clave, el sistema determina la direccin en la que se almacena el registro sobre el soporte.
228 Introduccin a la programacin con orientacin a objetos
Una direccin en un soporte direccionable podra ser el nmero de pista junto con el nmero de sec-
tor de un disco magntico, por ejemplo. Los soportes direccionables tpicos son los distintos tipos de
discos, pticos o magnticos, usados en los ordenadores. Lgicamente un soporte direccionable se
puede usar, si se desea, secuencialmente.
a) Acceso secuencial.
b) Acceso directo.
Esta clasificacin es paralela a la del tipo de soporte, pero no hay una correspondencia uno a uno
entre ellas.
El acceso secuencial es aquel en el que se van recorriendo los registros de forma consecutiva. Los
registros se recorren en orden, y no se puede saltar de uno, a otro que est ms de una posicin por
encima o por debajo. Se accede a los registros segn el orden de almacenamiento, uno detrs de otro.
Siempre es posible avanzar desde el principio hacia el final del fichero y a veces, depende del siste-
ma, puede ser posible tambin avanzar desde una posicin hacia atrs. Un fichero de acceso secuen-
cial puede implementarse sobre soporte secuencial o direccionable.
El acceso directo es aquel en el que se puede acceder a cualquier registro desde cualquier otro, es
decir, saltando. No es forzoso recorrer el fichero secuencialmente, no hay que consultar los registros
precedentes. Este tipo de acceso slo es posible con soportes direccionales.
a) Organizacin secuencial.
b) Organizacin directa o aleatoria.
c) Organizacin secuencial indexada.
En la organizacin directa el orden fsico de los registros no se corresponde con el orden lgico.
El acceso a los registros se hace directamente a travs de su posicin (lugar relativo que ocupan), no
de su orden. Por lo tanto, en cada momento se puede acceder al registro que necesitemos conocien-
do su posicin dentro del fichero e indicando que queremos ir a esa posicin concreta. Este tipo de
organizacin se relaciona con el acceso de tipo directo. En esta forma de organizacin podemos leer
y escribir en cualquier orden. Para saber a qu registro queremos llegar necesitamos usar la informa-
cin de uno de los campos (campo clave 2 o ndice). Esto quiere decir que el programa que use el
fichero debe determinar la posicin del registro que nos interesa a partir del valor del campo clave.
La idea es que conociendo el valor de la clave (un apellido o un nmero de identificacin, por ejem-
plo) se obtenga el nmero del registro usando alguna tcnica. La ms sencilla es que la clave sea el
nmero de orden que corresponda al registro (el registro 1, el 2, etc.) y saltar a ese registro. Otras
veces no es tan sencillo y hay que usar algn algoritmo que permita convertir la clave en un nmero
de registro. Se dice en este contexto que usamos algn tipo o tcnica de direccionamiento. Para una
exposicin de diferentes modos de direccionamiento en los ficheros de organizacin directa vase
Prieto et al., 1995. Para que un fichero sea de organizacin directa debe estar almacenado en un
soporte direccionable.
En la organizacin secuencial indexada la informacin est organizada con ayuda de dos ficheros 3.
Uno de ellos, fichero de datos o registros, contiene la informacin total ordenada de acuerdo a un cam-
po clave. Este fichero debe permitir el acceso directo. Para acceder a los registros de este fichero tra-
bajamos con la ayuda de otro fichero, el de ndices, que es de organizacin secuencial. Para poder usar
el fichero de ndices debemos considerar el fichero de registros organizado en zonas. En el fichero de
ndices tenemos tantas entradas (registros en este fichero) como zonas en el fichero de registros. En
cada entrada del fichero ndice se almacena el ltimo valor del campo clave de cada zona del fichero
de registros y la direccin del primer registro de esa zona. En la Figura 9.2 se presenta un ejemplo don-
de tenemos una serie de registros de personas, actuando el nombre de pila como campo clave.
Bartolo |1 1| Amparo
Juan |4 2| Ana Zona 1
Pedro |7 3| Bartolo
Fichero de ndices 4| Ester
5| Eva Zona 2
Acceso secuencial 6| Juan
7| Lola
8| Lourdes
Zona 3
9| Luis
10 | Pedro
Fichero de ndices
Acceso directo
2
La clave de un registro puede estar formada por uno o varios campos dependiendo, de si un solo campo identifica de
forma nica el registro. Por simplicidad, en este texto hablaremos de campo clave refirindonos a un solo campo del registro.
3
Estrictamente hablando no necesitaramos dos ficheros, bastara con uno dividido en dos zonas. Lo normal es, sin
embargo, dos ficheros.
230 Introduccin a la programacin con orientacin a objetos
Por ejemplo, para buscar un nombre se busca primero secuencialmente en el fichero de ndices
hasta encontrar un nombre que sea mayor (alfabticamente) que el que buscamos. Miramos en ese
registro el valor de la direccin del fichero de registros y saltamos all. Una vez en esa posicin,
que es la primera de un bloque, recorremos secuencialmente el fichero hasta encontrar el nombre bus-
cado o un nombre que sea mayor que l, en cuyo caso el nombre no est en la lista. Veamos un
ejemplo intentando localizar a Luis en el caso de la Figura 9.2. El primer paso sera buscar a Luis u
otro nombre mayor, alfabticamente, en el fichero de ndices. Despus de leer Bartolo y Juan lle-
garamos a Pedro que es mayor (alfabticamente), y entonces se empezar a realizar una bsqueda
secuencial en el fichero de registros, comenzando en la posicin que indicaba la entrada donde esta-
ba Pedro, en nuestro caso la posicin 7. Despus de leer Lola y Lourdes se llegara al nombre bus-
cado.
Los soportes que se utilizan para esta organizacin son los que permiten el acceso directo, nor-
malmente los discos magnticos. Los soportes de acceso secuencial no pueden utilizarse ya que no dis-
ponen de la posibilidad de direccionamiento directo necesaria para el fichero de datos.
Desde el punto de vista genrico podemos distinguir nicamente entre la organizacin secuencial
y la de acceso directo. Normalmente, por tanto, se habla de ficheros secuenciales o ficheros de acce-
so directo.
Tambin conviene comentar que podemos clasificar los ficheros en funcin del formato usado para
almacenar la informacin en ellos. Las dos posibilidades son,
a) Ficheros binarios.
b) Ficheros de texto.
Los ficheros binarios son aquellos cuyo contenido son secuencias de dgitos binarios. Los ficheros
binarios se disean para que se lean desde un programa, pero no pueden leerse directamente con un
editor de texto.
Los ficheros de texto son aquellos cuyos contenidos son una secuencia de caracteres y pueden, por
lo tanto, ser ledos con un editor de texto.
Estos dos tipos de ficheros presentan sus ventajas y desventajas respectivas, considermoslas.
Los ficheros de texto pueden ser transportados de un ordenador a otro. Sin embargo, los bina-
rios dependen del ordenador y slo pueden ser ledos en el mismo tipo de ordenador que los
cre. En el caso de Java, los ficheros binarios son independientes de la plataforma usada, pero
esto es una excepcin.
La gran ventaja de los binarios es que se procesan de manera ms eficiente, ya que el sistema
se ahorra la conversin del texto al formato binario, que es el que al final maneja la mquina.
Una gran ventaja de los ficheros de texto es que pueden leerse con cualquier editor de texto,
siendo inteligibles para las personas. Los ficheros binarios slo los puede leer y escribir un pro-
grama. Si intentamos editar un fichero binario, se obtiene un galimatas sin sentido.
En un fichero binario los datos se almacenan igual que en memoria principal. Cada elemento
de datos se almacena como una secuencia de bytes.
Cada tipo de fichero presenta sus particularidades y es en funcin de ellas que debemos escoger
un tipo u otro. Por ejemplo, como fichero auxiliar en un programa de clculo cientfico nos intere-
sar un fichero binario, pues el procesamiento es ms eficiente y su contenido no se necesita editar,
Ficheros 231
slo se necesita que lo lea el propio programa. Sin embargo, para imprimir resultados necesitamos
un fichero de texto.
Desde el punto de vista semntico las operaciones ms comunes que se pueden realizar sobre un fiche-
ro pueden clasificarse y considerarse independientemente de cualquier lenguaje. Una posible clasifi-
cacin de dichas operaciones es la siguiente:
a) Creacin.
b) Apertura.
c) Consulta.
d) Modificacin.
e) Insercin.
f) Borrado.
g) Eliminacin.
h) Clausura (cerrar).
i) Movimiento (sobre el fichero).
Pensando en orientacin a objetos, si tuvisemos que crear una clase que representara los ficheros
deberamos aadir mtodos que realizarn las operaciones anteriores. Consideremos cada una de ellas.
La creacin es la primera operacin sobre un fichero, para ello es necesario saber el tipo del fiche-
ro y qu organizacin necesitamos para el mismo. Todo fichero debe estar creado antes de empezar a
operar en l por primera vez.
La apertura es la primera operacin sobre un fichero que ya existe. Esta operacin consiste en la
conexin desde el programa al fichero para permitir su uso.
La consulta de un fichero es el acceso a sus registros para recuperar y utilizar su informacin.
La modificacin consiste en la alteracin (actualizacin) de la informacin de algn o algunos
registros del fichero.
La insercin es la inclusin de un nuevo registro en el fichero.
El borrado es la eliminacin del fichero.
La eliminacin es la supresin de uno o ms registros del fichero considerado.
La clausura (cerrar ficheros) es la operacin que corta el acceso (desconecta) el fichero. Aunque
el sistema lo haga automticamente cuando el programa termina normalmente, conviene cerrar los
ficheros, principalmente por dos razones. Primera, si el programa acaba anormalmente, el sistema no
cerrar el fichero automticamente. El fichero quedar abierto sin tener ningn programa conectado a
l y esto puede daarlo o bloquearlo. Segundo, en algunos casos cuando se quiere leer de un fichero
despus de haber escrito en l, es necesario cerrar el fichero y reabrirlo para poder empezar la lectura
desde el principio. En cualquier caso, es ms que recomendable cerrar los ficheros tras haber acabado
de trabajar con ellos.
El movimiento se refiere a la posibilidad de cambiar de un registro a otro dentro del fichero. En
un fichero secuencial no hay ninguna instruccin de salto hasta un registro y habr que moverse
sobre dicho fichero un registro tras otro hasta llegar al que nos interesa. Si nuestro sistema lo permi-
te puede ser posible ir hacia atrs (retroceder) para localizar registros anteriores a aquel en el que esta-
mos. Si nuestro sistema no permite este retroceso es necesario rebobinar totalmente el fichero y
empezar a avanzar otra vez buscando el registro. En este caso, si existe una instruccin para rebobinar
deberemos usarla y si no habr que cerrar el fichero y volverlo a abrir. En los ficheros de acceso direc-
to el movimiento se realiza por medio de desplazamientos a los registros de inters usando el valor
deseado del campo ndice.
232 Introduccin a la programacin con orientacin a objetos
En algunos lenguajes (como Java) existe un mecanismo de gestin de excepciones que permite
capturar el problema y evitar la finalizacin irrecuperable del programa. En Java toda excepcin que
pueda producirse debe manejarse de alguna manera. La gestin de excepciones funciona, genrica-
mente, de la forma siguiente. En primer lugar debe definirse el bloque de cdigo en el que se puede
producir la o las excepciones. A continuacin, hay que indicar qu se debe hacer cuando se presente
cada una de dichas excepciones. Para ello en Java disponemos de la sentencia try-catch. En la par-
te del try (intenta) se debe indicar el bloque de cdigo que puede producir las excepciones. Como
habitualmente, el bloque se delimita entre llaves. Las llaves indican dnde comienza y acaba el blo-
que try. A continuacin, se escriben bloques que indican qu hacer si una determinada excepcin es
capturada. Estos bloques van precedidos por la palabra catch (coge, captura) seguida entre parnte-
sis del nombre de la excepcin que se captura. Se pueden escribir tantos bloques catch como excep-
ciones posibles puedan darse. La sintaxis del manejo de excepciones es:
try {
Ficheros 233
finally { //opcional
-- bloque de sentencias que se ejecuta siempre --
}
La clusula finally es opcional y se ejecuta siempre, tanto si se produce una excepcin como si
no, incluso aunque no exista ninguna clusula catch. El finally se ejecuta en cuanto el flujo de con-
trol abandona la sentencia try.
En Java las excepciones son clases. Por eso, como podemos observar en los catch en el ejemplo
genrico anterior, al lado del nombre de cada excepcin se incluye un identificador. Este identificador
es el de una referencia de la clase correspondiente a la excepcin. Este identificador es arbitrario y muy
habitualmente se usa una simple e (de excepcin).
El manejo de excepciones puede modificar el flujo de control del programa. Cuando se lanza (se
produce) una excepcin, el control del programa sale del bloque try y examina los bloques catch
buscando un gestor o manejador (bloque catch) adecuado para ese tipo de excepcin. Si se encuen-
tra, se ejecuta el bloque catch correspondiente. Seguidamente el flujo de control salta hasta el bloque
finally, si existe, o hasta el cdigo que haya a partir del ltimo catch.
Adems de las excepciones generadas por el intrprete de Java y predefinidas en el sistema se pue-
den crear nuevas excepciones. La sintaxis para crear una excepcin es similar a la sintaxis para crear
una clase hija que hereda de una clase padre. En concreto, toda excepcin nueva debe heredar de la
clase Exception que es la clase padre de todo el sistema jerrquico de excepciones de Java. La sin-
taxis para la creacin de una excepcin nueva sera:
Es normal que en el cuerpo del constructor no se incluya cdigo ya que el inters es nicamente
la existencia de la clase. La ventaja es que el sistema de captura y gestin de estas nuevas excepcio-
nes es el mismo que para las predefinidas. Este tipo de excepciones siempre se lanzan explcitamente,
es decir, debe ser el programador quien las lance usando la palabra reservada throw. La sintaxis para
lanzar una excepcin es:
Por ejemplo:
if (denominador == 0)
throw new DivideporCeroExcepcion();
a permitir que se produzca el correspondiente error. Para ello se usa la palabra reservada throws en
la cabecera del mtodo tal y como hemos hecho en algunos ejemplos previos. Por ejemplo:
public double division(int numerador, int denominador) throws
DivideporCeroExcepcion {
if (denominador == 0)
throw new DivideporCeroExcepcion();
return (double)numerador/denominador;
}
IndexOutOfBoundsException
StringIndexOutOfboundsException ArrayIndexOutOfBoundsException
class Excepcion {
public static void main(String[] args) {
try {
System.out.println(args[0]);
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(B);
}
catch(IndexOutOfBoundsException e) {
System.out.println(A);
}
}
}
error, porque el segundo catch ya se habra tenido en cuenta al probar la excepcin de la clase padre. Este
comportamiento permite un pequeo truco. Si no se recuerda el nombre de la excepcin que se desea cap-
turar o se pretende capturar todas las excepciones de forma inespecfica se puede usar Exception. Como
sta es la clase padre de toda la jerarqua de excepciones, capturndola se capturan todas ellas. Para ello
tendramos que incluir en la estructura try-catch correspondiente el siguiente cdigo:
catch(Exception e) {
//bloque de cdigo
}
class EjemploExcepciones{
public static void main(String [] args){
int valor=Integer.parseInt(args[0]);
try {
if (valor==3) throw new Aexception();
if (valor==5) throw new Bexception();
System.out.println(llega hasta aqui);
} //Fin del bloque try
catch(Aexception a){
System.out.println(Error tipo A);
}
catch(Bexception b){
System.out.println(Error tipo B);
}
finally{
System.out.println(Ahora me toca a mi);
}
System.out.println(Imprimeme);
}
}
Error tipo A
Ahora me toca a mi
Imprimeme
236 Introduccin a la programacin con orientacin a objetos
Error tipo B
Ahora me toca a mi
Imprimeme
Obsrvese que las dos excepciones definidas no tienen cdigo. Esto no es ningn problema pues-
to que lo importante es que exista la clase para poder luego arrojar la correspondiente excepcin con
throw.
Tras haber presentado el mecanismo de captura y gestin de excepciones, abordemos cmo traba-
jar en Java con los distintos tipos de ficheros.
En Java podemos trabajar de dos formas con los ficheros secuenciales binarios:
En este caso, los ficheros para escritura o lectura se abren creando objetos de las clases (streams)
FileOutputStream y FileInputStream.
Ficheros 237
a.1) Escritura
Cuando se abre un fichero para escribir en l, es decir, para salida, se debe crear un objeto de clase
FileOutputStream. Adems, se debe pasar un argumento al constructor del objeto indicando el
nombre del fichero. Los ficheros existentes que se abren para salida se borran, es decir, se desechan
todos los datos del fichero. Si el fichero especificado no existe, se crea un fichero con ese nombre.
Veamos la sintaxis:
FileOutputStream salida;
salida= new FileOutputStream(nombre);
donde nombre, de clase String, contendr el nombre del fichero a abrir, incluyendo opcionalmente
el directorio en el que se encuentra. Se puede escribir directamente entre los parntesis y entre comi-
llas el nombre del fichero como una cadena literal, sin necesidad de utilizar una variable que habra
que declarar e inicializar previamente. El constructor admite otra forma:
FileOutputStream(File Objeto_File);
Esta segunda forma indica que se puede usar en el constructor un objeto de clase File. Como
expondremos ms adelante, esta clase permite trabajar con las propiedades de un fichero y cuando se
crea un objeto de dicha clase se puede vincular a un fichero determinado.
Una vez abierto el fichero podemos escribir con el mtodo write(int i) que escribe un byte en
el fichero. Tras concluir el trabajo con el fichero debemos cerrarlo por medio del mtodo close().
A partir de la versin 1.1 de Java se aadi un nuevo constructor para la clase FileOutputStre-
am que permite abrir un fichero en modo de adicin, sin borrar la informacin existente. La sintaxis es:
si aadir es true el fichero se abre en modo de adicin. El puntero interno del fichero se coloca al
final del mismo, lo que implica que la escritura se realiza a partir de lo ltimo escrito. Si el fichero no
existe se crea y si existe se aaden los nuevos datos al final del mismo.
Ilustremos el uso de la clase FileOutputStream con un ejemplo donde se abra un fichero
secuencial binario para escribir en l una serie de valores enteros. El nombre del fichero se introducir
por lnea de rdenes, vase el Programa 9.3.
import java.io.*;
class Ficheros {
public static void main(String [] args) {
FileOutputStream salida; //Se crea la referencia
try {
// Creando el fichero
salida=new FileOutputStream(args[0]);
} // Fin try
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(Introduzca como argumento el
+nombre del fichero destino );
}
catch(IOException e) {
System.out.println(Excepcion de entrada/salida:
+e.toString());
}
En el Programa 9.3 se abre un fichero representado por el objeto salida y se escriben en l los
valores de 0 a 98. En el ejemplo se capturan dos excepciones. La primera es ArrayIndexOutOf-
BoundsException que se puede producir si no hemos introducido el nombre del fichero por lnea de
rdenes. La segunda es la excepcin general de entrada/salida (IOException). Al crear un objeto
FileOutputStream e intentar abrir un fichero, el programa prueba si la operacin de apertura tuvo
o no xito. Si la operacin falla, se genera una excepcin de E/S (IOException) que debe ser captu-
rada por el programa. Algunas de la posibles razones de que se lance una IOException al abrir fiche-
ros son intentar abrir para lectura un fichero que no existe, intentar abrir para lectura un fichero sin
tener permiso o abrir un fichero para escritura cuando no hay espacio en disco. Si el fichero se abre
con xito, el programa est listo para procesar datos. El mtodo close() lanza tambin la IOExcep-
cion que hay que capturar. El mtodo toString del objeto e invocado en la IOException impri-
me informacin sobre la naturaleza de la excepcin producida. Por ejemplo, si el fichero donde
queremos escribir est ya abierto por otro programa se producira la excepcin y el mtodo toS-
tring() indicara que el fichero ya est abierto.
a.2) Lectura
Para abrir un fichero binario para lectura a nivel de byte se usa la clase FileInputStream. El nom-
bre del fichero a leer se puede pasar como argumento al constructor de FileInputStream. Al igual
que en la escritura hay dos constructores de lectura habituales, y la creacin del objeto con ellos sera:
FileInputStream entrada;
entrada=new FileOutputStream(nombre);
o
FileInputStream entrada;
entrada=FileInputStream(objeto File);
donde nombre, de clase String, contendr el nombre del fichero a abrir, incluyendo el directorio si se
desea. En la segunda forma pasamos al constructor un objeto de la clase File, que veremos ms adelan-
te. Todos estos constructores pueden lanzar una FileNotFoundException. Esta excepcin puede cap-
Ficheros 239
turarse para indicar al usuario que ese fichero no se ha encontrado. La clase FileNotFoundException
se deriva de la IOException por lo que si se captura sta y no la FileNotFoundException tambin
la gestionaremos. De todas formas conviene capturar la FileNotFoundException, porque as sabre-
mos exactamente la razn de la excepcin. Adems, al crear un objeto de clase FileInputStream e
intentar abrir un fichero, el programa determina si la operacin de apertura tuvo xito. Si la operacin
falla, se genera una excepcin de entrada/salida (IOException) que debe ser capturada por el progra-
ma.
Para leer del fichero, disponemos del mtodo read() que lee un nico byte y lo devuelve como
un entero (int). Es interesante saber que la marca de fin de fichero en estos ficheros se identifica como
valor 1. Localizando cundo hemos ledo 1 sabremos cundo hemos llegado al fin del fichero. Una
vez finalizado el trabajo con el fichero debemos cerrarlo con el mtodo close().
Ilustremos el uso de la clase FileInputStream con un ejemplo donde se abra el fichero secuen-
cial binario escrito en el Programa 9.3. Leeremos el contenido del fichero byte a byte copindolo en
otro fichero y mostrando los datos ledos por pantalla. Los nombres de los ficheros origen y destino se
introducirn por lnea de rdenes, vase Programa 9.4.
import java.io.*;
class Ficheros {
public static void main(String [] args) {
int i;
boolean sigue=true;
FileInputStream entrada; //Se crea la referencia
FileOutputStream salida; //Se crea la referencia
try {
// Creando los ficheros
entrada=new FileInputStream(args[0]);
salida=new FileOutputStream(args[1]);
salida.close();
} // Fin try
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(Introduzca como argumento los
+nombres de los ficheros
+origen y destino);
}
catch(FileNotFoundException e) {
System.out.println(Fichero origen inexistente);
}
240 Introduccin a la programacin con orientacin a objetos
Cuando no introducimos el nombre del fichero por la lnea de rdenes se lanza la excepcin
ArrayIndexOutOfBoundsException que capturamos para indicar cmo hay que ejecutar el progra-
ma. Tambin capturamos la excepcin FileNotFoundException, que se produce cuando no se
encuentra el fichero especificado. Por ltimo capturamos la excepcin general de entrada y salida
(IOException).
Cuando se encuentra el fin de fichero, el mtodo read() devuelve 1, por lo que lo aprovecha-
mos este hecho para leer hasta fin de fichero. El bucle do-while est controlado por la condicin de
leer un valor distinto de 1. Observemos que la lectura y la copia de bytes es independiente de lo que
codifiquen dichos bytes. Es decir, eliminando la sentencia System.out.println(i) tendramos un
programa de copia literal de ficheros.
b.1) Escritura
En este caso se puede usar la clase DataOutputStream conectada a un objeto de clase FileOutputS-
tream mediante encadenamiento de objetos de flujo (como ya hicimos con el BufferedReader y el
InputStreamReader). La creacin del objeto sera:
DataOutputStream salida;
salida=new DataOutputStream(new FileOutputStream(nombre));
donde nombre, de clase String, contiene el nombre del fichero. Tambin es vlida la segunda forma
del constructor de la clase FileOutputStream vista anteriormente y que aceptaba un objeto de cla-
se File. En el ejemplo anterior creamos un objeto de clase DataOutputStream llamado salida
asociado al fichero indicado por nombre. El argumento nombre se pasa al constructor FileOut-
putStream que abre el fichero. Esto establece una lnea de comunicacin con el fichero. Es impor-
tante recordar que si abrimos un fichero existente directamente para salida, el contenido del fichero se
desechar sin advertencia alguna. Si deseamos aadir informacin podemos abrir el fichero indicando
en el constructor de la clase FileOutputStream que la opcin de aadir es true.
Para aumentar la eficiencia de la salida al fichero podemos realizar almacenamiento temporal de
la salida a travs de un buffer. Para ello, podemos usar el FileOutputStream como parmetro del
constructor de la clase BufferedOutputStream:
(new BufferedOutputStream
(new FileOutputStream(nombre)));
Para escribir datos en un fichero podemos usar los mtodos de la clase DataOutputStream.
Algunos de estos mtodos son los siguientes:
De forma anloga tenemos mtodos para el resto de tipos primitivos como: writeBoolean, wri-
teByte, writeLong, writeShort, etc.
int i=10;
salida.writeInt(i);
Una vez que hemos terminado de escribir es conveniente cerrar el fichero con el mtodo close().
El trabajo con DataOutputStream se ilustra en el Programa 9.5 donde se van solicitando por
teclado una serie de valores reales y se van salvando en un fichero cuyo nombre se introduce tambin
por teclado.
import java.io.*;
class Ficheros {
public static void main(String [] args) {
double valor;
boolean seguir=true;
DataOutputStream salida;
BufferedReader teclado;
try {
teclado=new BufferedReader(new InputStreamReader(
System.in));
System.out.println(Introduzca nombre del fichero:);
while (seguir) {
System.out.println();
System.out.println(Introduzca un valor real:);
valor=Double.parseDouble(teclado.readLine());
242 Introduccin a la programacin con orientacin a objetos
salida.writeDouble(valor);
System.out.println();
System.out.println(Desea continuar. Si:0 No:1);
valor=Double.parseDouble(teclado.readLine());
if (valor==1) {
seguir=false;
}
} // Fin while
salida.close();
}
catch(IOException e) {
System.out.println(Excepcion de entrada/salida: +
e.toString());
}
b.2) Lectura
Como ya hemos indicado para la escritura, leer datos como bytes directamente es rpido pero burdo,
por lo que se usa la clase DataInputStream. Esta clase nos permite leer datos de tipo primitivo gra-
bados en un fichero binario. Lgicamente, los datos deben leerse del fichero en el mismo formato en
el que se escribieron en l. Por tanto, para leer datos escritos con DataOutputStream debemos usar
un DataInputStream encadenado a un FileInputStream. La declaracin del objeto sera:
DataInputStream entrada;
entrada=new DataInputStream(new FileInputStream(nombre));
donde nombre, de clase String, identifica el fichero. De la forma anterior creamos un objeto de cla-
se DataInputStream llamado entrada asociado al fichero identificado por nombre. Este parme-
tro, nombre, se pasa al constructor de la clase FileInputStream. Esto establece una lnea de
comunicacin con el fichero. Tambin es vlido el constructor de la clase FileInputStream que
acepta un objeto de clase File previamente asociado al fichero.
Como en el caso de la escritura con la DataOutputStream, para aumentar la eficiencia de la sali-
da se puede usar un buffer. Para ello podemos usar el FileInputStream como argumento del cons-
tructor de la clase BufferedInputStream de la forma siguiente,
DataInputStream entrada;
entrada=new DataInputStream(new BufferedInputStream
(new FileInputStream(nombre)));
Si el programa se abre con xito estamos en condiciones de leer con los mtodos correspondien-
tes, algunos de los cuales son,
readUTF(): lee una cadena en formato UTF y devuelve la misma cadena como String
Anlogamente, existen mtodos para lectura del resto de tipos primitivos como readBoolean,
readByte, readShort, readLong, etc. Por ejemplo, para leer un dato de tipo int de un fichero
identificado por el objeto salida haramos,
int i=salida.readInt();
Todos los mtodos anteriores arrojan la IOException. Si sta no se captura en un catch debe-
mos indicar en la cabecera del mtodo donde se realice la lectura throws IOException. Por otro
lado, si se llega al fin del fichero durante la lectura se lanza una excepcin de fin de fichero identifi-
cada como EndOfFileException. Esta excepcin puede capturarse en una clusula catch de la for-
ma habitual,
catch(EOFException e) {
//cdigo si se llega al final del fichero
}
Capturando esta excepcin podemos leer del fichero con un bucle controlado por una condicin
que devenga false cuando se produzca la EOFException. Esto se puede conseguir colocando una
variable lgica que controle el bucle y que se ponga a false en el catch que captura la EOFExcep-
tion.
Como habitualmente, cuando acabemos de leer es conveniente cerrar el fichero con el mtodo
close().
Como ilustracin construyamos un programa que lea hasta el final la serie de valores reales intro-
ducidos anteriormente en un fichero en el Programa 9.5 y los muestra por la pantalla. El nombre del
fichero se introducir por teclado, vase el Programa 9.6.
import java.io.*;
class Ficheros {
public static void main(String [] args) {
double valor;
boolean seguir=true;
DataInputStream entrada=null;
BufferedReader teclado;
try {
try {
teclado=new BufferedReader(new InputStreamReader(
System.in));
System.out.println(Introduzca nombre del fichero origen:);
entrada=new DataInputStream(new BufferedInputStream
(new FileInputStream(teclado.readLine())));
}
catch(FileNotFoundException e) {
Ficheros 245
} // Fin del if
catch(IOException e) {
System.out.println(Excepcion de entrada/salida+
e.toString());
El Programa 9.6 muestra que la sentencia try-catch se puede concatenar y anidar como, por
ejemplo, los bucles. Hay un total de tres sentencias try-catch. La primera abarca todo el programa
y sirve para capturar la IOExcepcion que se puede producir en los distintos mtodos que se usan. El
segundo try-catch est anidado al primero y captura la FileNotFoundException que se produ-
ce si el nombre dado al fichero es errneo. En caso de producirse esta excepcin se salta el if siguien-
te, dentro del cual est la lectura del fichero. En caso de entrar en el if hay una tercera sentencia
try-catch colocada dentro del bucle while que lee el fichero. Obsrvese cmo al producirse la
EOFExcepcion por haber llegado al fin del fichero la variable seguir que controla el bucle se pone
a false. Finalmente, cerramos el fichero con el mtodo close(). A continuacin, encontramos la
clusula catch de la IOExcepcion del try ms externo.
a) Escritura
Para escribir en un fichero de texto se puede usar la clase PrintWriter, que contiene los cono-
cidos mtodos println y print. Estos mtodos se comportan igual que los mtodos
246 Introduccin a la programacin con orientacin a objetos
PrintWriter Nombre_flujo_salida;
salida =new PrintWriter(new FileWriter(nombre));
donde nombre, de clase String, identifica el fichero a usar. Tambin es vlida la forma del constructor
de la clase FileWriter donde se acepta un objeto de clase File. Al igual que con FileOutputStre-
am, si queremos aadir informacin podemos usar el constructor con la estructura FileWriter (nom-
bre, aadir) con la opcin aadir a valor true. Conviene recordar que el fichero debe cerrarse
usando el mtodo close() cuando se ha terminado de escribir en l.
Como ejemplo, construyamos un programa que lea tres lneas por pantalla y las escriba en un
fichero cuyo nombre se da como argumento por la lnea de rdenes, vase Programa 9.7.
import java.io.*;
class Ficheros {
public static void main(String [] args){
PrintWriter salida=null; //Inicializando referencias
BufferedReader entrada=null;
String fichero=;
String linea=null;
try {
fichero=args[0];
entrada= new BufferedReader
(new InputStreamReader(System.in));
salida= new PrintWriter
(new FileWriter(fichero));
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(Debe introducir el nombre del
+fichero como argumento);
}
catch(IOException e){
System.out.println(No se abrio bien el fichero\n
+e.toString());
}
} //Fin mtodo main
Ficheros 247
b) Lectura
Podemos usar la clase BufferedReader para leer un fichero de texto con el habitual mtodo
readLine(). La clase BufferedReader se usa como ya sabemos, pero para leer de un fichero se le
pasa a su constructor un objeto de la clase FileReader. La sintaxis es la siguiente,
BufferedReader entrada;
entrada= new BufferedReader(new FileReader(nombre));
donde nombre, de clase String, contiene el nombre fsico del fichero a leer. El constructor de
FileReader tambin acepta un objeto de tipo File que contenga el fichero a abrir.
Una vez abierto el fichero podemos usar el mtodo readLine(). Este mtodo funciona de la
misma forma que cuando hemos ledo de teclado, es decir, devuelve una cadena. La clase
BufferedReader tambin contiene el mtodo read() para leer un solo carcter. El mtodo devuel-
ve el carcter ledo como un tipo entero. Si lo queremos en forma de carcter debemos usar un mol-
de, como se indica a continuacin:
Conviene recordar que cuando se ha acabado la lectura se debe cerrar el fichero con el mtodo
close().
Es interesante saber que el mtodo readLine() devuelve null cuando llega al final del fichero,
y que el mtodo read() devuelve 1. El programa puede entonces comprobar el fin de fichero com-
probando si readLine() devuelve null o si read() lee un 1. Estos mtodos no lanzan una
EOFException.
Como ejemplo vamos a construir un programa que lea lneas de caracteres de un fichero y las
imprime en otro fichero incluyendo el nmero de lnea, vase el Programa 9.8.
import java.io.*;
class Ficheros {
public static void main(String [] args) {
String fichero1=,fichero2=;
BufferedReader entrada=null;
PrintWriter salida=null;
try {
try {
fichero1=args[0];
fichero2=args[1];
entrada= new BufferedReader
248 Introduccin a la programacin con orientacin a objetos
(new FileReader(fichero1));
salida= new PrintWriter
(new FileOutputStream(fichero2));
int cont=0;
String linea=entrada.readLine();
while(linea!=null) { // Leyendo hasta final del fichero
cont++;
salida.println(cont+ +linea);
linea=entrada.readLine();
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(Debe introducir como argumentos
+los ficheros de entrada y salida);
}
catch(FileNotFoundException e) {
System.out.println(Fichero +fichero1
+ no encontrado);
}
finally {
entrada.close();
salida.close();
}
} // Fin del try externo
catch(IOException e){
System.out.println(No se abrio bien el fichero\n+
e.toString());
}
Una vez ms, usamos dos try-catch anidados. El ms externo controla la aparicin de la
IOException, que lanza, por ejemplo, el mtodo close(). El ms interno controla las excep-
ciones ArrayIndexOutOfBoundsException que se produce si falta el nombre de algn fichero,
y la FileNotFoundException que se produce si el nombre del fichero es incorrecto. En este caso
hemos colocado los close() de los dos ficheros en la clusula finally. El mtodo toString()
del objeto e, invocado en la IOException, informa sobre la naturaleza de la excepcin produci-
da.
bancarios, sistemas de reservas, etc. Estos sistemas se incluyen dentro de los sistemas llamados de pro-
cesamiento de transacciones, que requieren acceso rpido a datos especficos. Este tipo de acceso de
forma rpida y directa a los registros de un fichero se puede realizar con los ficheros de acceso direc-
to o aleatorio. En un fichero de acceso aleatorio se pueden insertar datos sin necesidad de destruir los
dems datos del fichero. Los datos previamente almacenados tambin pueden actualizarse o eliminarse
sin tener que reescribir todo el fichero.
Como ya hemos indicado, Java no impone estructura alguna a los ficheros, as que la aplicacin
que desee usar ficheros de acceso aleatorio deber crear y mantener la estructura necesaria. Podemos
usar diversas tcnicas para crear ficheros de acceso aleatorio. La ms sencilla es la de exigir que todos
los registros tengan la misma longitud (longitud fija). El empleo de ficheros de longitud fija permite a
un programa calcular con facilidad, en funcin del tamao del registro y de la clave del registro, la
posicin exacta de cualquier registro relativa al principio del fichero.
Comencemos exponiendo las generalidades en Java de los ficheros de acceso directo o aleatorio.
y
RandomAccessFile(String nombre, String modo)
donde nombre identifica el nombre del fichero y modo indica el modo de apertura del fichero. Este
modo puede ser slo lectura (slo se podr leer del fichero) o lectura-escritura (se puede leer y escri-
bir en el fichero). El modo de slo lectura se indica con r (read) mientras que el de lectura-escritu-
ra se indica con rw (read-write). Obsrvense las dobles comillas usadas para denotar el modo, ya
que se trata de una cadena, incluso en el caso de indicar modo de slo lectura, r.
Especialmente para los ficheros de acceso directo, debemos entender claramente que en Java el
fichero es una secuencia lineal de bytes que concluye en algn tipo de marca de fin de fichero (EOF,
End Of File). Para conocer dnde estamos en el fichero existe un puntero interno de posicin. La situa-
cin se ilustra en la Figura 9.4.
EOF
Puntero
250 Introduccin a la programacin con orientacin a objetos
Con este esquema in mente podemos comentar algunos de los mtodos genricos de la clase
RandomAccessFile:
void seek(long posicin): Se utiliza para establecer la posicin actual del puntero dentro del
fichero. La variable posicin indica el nmero de bytes desde el principio del fichero. Despus de lla-
mar a seek, la siguiente operacin de lectura o escritura se realizar en la nueva posicin dentro del
fichero.
long getFilePointer(): Devuelve la posicin actual del puntero del fichero (en bytes) a partir del
principio del fichero.
int skipBytes(int desplazamiento): Mueve el puntero, desde la posicin actual, el nmero
de bytes indicado por desplazamiento, hacia delante si el valor es positivo o hacia atrs si el valor
es negativo. El mtodo devuelve el nmero de bytes que se ha saltado (podemos haber alcanzado el
fin de fichero y no saltar todos los indicados en desplazamiento).
long length(): devuelve la longitud del fichero en bytes.
Una vez usado el fichero, debemos cerrarlo como siempre con el mtodo close().
RandomAccessFile salida;
salida=new RandomAccessFile(nombre,rw);
import java.io.*;
class Ficheros {
public static void main(String [] args) {
double valor;
int contador=0;
boolean seguir=true;
RandomAccessFile salida;
BufferedReader teclado;
try {
teclado=new BufferedReader(new InputStreamReader(
System.in));
System.out.println(Introduzca el nombre del fichero:);
while (seguir) {
contador++;
System.out.println();
System.out.println(Introduzca un valor real:);
valor=Double.parseDouble(teclado.readLine());
salida.writeInt(contador);
salida.writeDouble(valor);
System.out.println();
System.out.println(Desea continuar. Si:0 No:1);
valor=Double.parseDouble(teclado.readLine());
if (valor==1) {
seguir=false;
}
} // Fin while
salida.close();
}
catch(IOException e) {
System.out.println(Excepcion de entrada/salida: +
e.toString());
}
Obsrvese cmo nos colocamos al final del fichero. Se usa el mtodo seek al que se le pasa como
parmetro la longitud del fichero obtenida con el mtodo length. El Programa 9.9 es similar al Pro-
grama 9.5 con la DataOutputStream. En el programa si el fichero no existe se crea.
En esta seccin vamos a abrir un fichero de acceso aleatorio para lectura, con el modo r. El uso del
constructor es similar al caso de escritura:
RandomAccessFile entrada;
entrada=new RandomAccessFile(nombre, r);
readUTF(): Lee una cadena en formato UTF. Para los caracteres normales se usa un byte por
carcter, usndose dos caracteres adicionales para saber el nmero de bytes de la cadena. En otras pala-
bras, se lee directamente la misma cadena que se escribi con UTF, con su misma longitud.
Disponemos de mtodos adicionales para leer todos los tipos primitivos, como readFloat(),
readShort(), etc.
Es importante tener en cuenta que si slo queremos leer de un fichero se recomienda abrir en modo
lectura r. Esto evitar una modificacin no intencional del contenido del fichero.
Especialmente para lectura es importante saber cmo moverse en el fichero. Para trabajar a nivel de
registro lo ms til es poder saltar registros y no bytes. En Java esto se puede hacer conociendo el nme-
ro de bytes de cada registro. Supongamos que en un fichero de acceso aleatorio vamos a almacenar los
datos de los clientes de una organizacin, que constan de un nmero de orden (int) el nombre escrito
en formato UTF con 30 caracteres (30+2 bytes) y un saldo (double). El tamao del registro, en bytes,
es:
l_registro=44;
Conociendo la longitud del registro (nmero de bytes por registro) puedo cambiar de unidades: de
registros a bytes o de bytes a registros.
Para realizar la conversin es importante decidir si los registros van a comenzar a numerarse en
cero o en uno. La Figura 9.5 muestra un fichero como una serie de nueve bytes, numerados de 0 a 8,
que acaba con la marca de fin de fichero (EOF), y donde se usan registros de un tamao de tres bytes.
En ella se muestra tambin la numeracin de registros en 0-origen y en 1-origen. Claramente, si el pri-
mer registro lo numero como uno, para ir al registro n (al principio de l) debera colocarme en el byte
dado por la siguiente expresin:
Ficheros 253
0 1 2
0-origen
0 1 2 3 4 5 6 7 8 EOF
1-origen
1 2 3
posicion=(n-1)*l_registro;
Por otro lado, si el primer registro lo numero como cero, para llegar al principio del registro n
debera saltar la siguiente cantidad de bytes:
posicion=n*l_registro;
Como ilustracin, desarrollemos un programa que devuelva del fichero del Programa 9.9 un regis-
tro que le solicitemos. Para solicitar el registro usaremos su nmero de orden (1-origen). Las peticio-
nes se harn por teclado, vase el Programa 9.10.
import java.io.*;
class Ficheros {
public static void main(String [] args) {
double valor;
int n, l_registro, entero;
boolean seguir=true;
RandomAccessFile entrada;
BufferedReader teclado;
try {
teclado=new BufferedReader(new InputStreamReader(
System.in));
System.out.println(Introduzca el nombre del fichero:);
while (seguir) {
System.out.println();
System.out.println(Introduzca un numero de registro:);
n=Integer.parseInt(teclado.readLine());
entrada.seek((n-1)*l_registro);
entero=entrada.readInt();
valor=entrada.readDouble();
254 Introduccin a la programacin con orientacin a objetos
System.out.println();
System.out.println(Registro: +entero+ +valor);
System.out.println();
System.out.println(Desea continuar. Si:0 No:1);
valor=Double.parseDouble(teclado.readLine());
if (valor==1) {
seguir=false;
}
} // Fin while
entrada.close();
}
catch(IOException e) {
System.out.println(Excepcion de entrada/salida
+e.toString);
}
En el Programa 9.9 se calcula la longitud del registro y se abre el fichero para lectura. Dentro del
bucle se pregunta por los registros que se desea visualizar hasta que el usuario decide terminar. Como
se usa 1-origen lo que se debe indicar al programa son nmeros de orden para identificar los registros.
File(String nombre)
donde nombre contendr un nombre de fichero o directorio, incluido el camino hasta llegar a l. Por
defecto estaremos en el directorio de trabajo.
donde directorio incluye la trayectoria hasta llegar al directorio o fichero indicado por nombre.
igual al anterior pero en directorio se utiliza un objeto de la clase File, previamente creado, para
localizar el directorio o fichero indicado por nombre.
Puedo entonces crear un fichero con la clase File y luego pasarlo como parmetro a los distintos
constructores de las clases vistas hasta ahora para manipulacin de ficheros.
A partir de este momento los objetos de la clase Ejemplo podran guardarse y leerse de un fiche-
ro. Para guardar o leer un objeto en un fichero se debe crear una corriente de objetos con la clase
ObjectOutputStream para salida y ObjectInputStream para lectura, conectadas con las clases
FileOutputStream y FileInputStream, respectivamente. Los constructores se usan de la forma
256 Introduccin a la programacin con orientacin a objetos
siguiente,
ObjectOutputStream salida;
salida=new ObjectOutputStream(FileOutputStream(nombre));
ObjectInputStream entrada;
entrada=new ObjectInputStream(FileInputStream(nombre));
En particular, readObject() devuelve un objeto de clase Object, as que luego se debe usar un
molde correspondiente a la clase del objeto en cuestin. Veamos un ejemplo. Supongamos que tene-
mos una clase Ejemplo indicada como Serializable. Para escribir un objeto, obj1, que hubira-
mos creado de la clase Ejemplo, en un fichero llamado datos.dat haramos,
ObjectOutputStream salida;
salida=new ObjectOutputStream(FileOutputStream(datos.dat));
salida.writeObject(obj1);
Para leer el objeto del fichero y enlazarlo con una referencia llamada obj2 de clase Ejemplo hara-
mos,
ObjectInputStream entrada;
entrada=new ObjectInputStream(FileInputStream(datos.dat));
obj2=(Ejemplo) entrada.readObject();
EJERCICIOS PROPUESTOS
Ejercicio 1.* En un fichero de acceso aleatorio se han escrito una serie de regis-
tros compuestos por un campo entero (int, 4 bytes) y uno real en
doble precisin (double, 8 bytes). Sobre el fichero se aplica el mto-
do seek de la forma siguiente: fichero.seek(24). Si el contenido
del fichero es:
8 20.5
1 15.8
3 40.9
Ficheros 257
6 2.5
Ejercicio 2.* En un fichero de acceso aleatorio se han escrito una serie de regis-
tros compuestos por un campo entero (int, 4 bytes) y uno real en
doble precisin (double, 8 bytes). Si queremos leer a partir del cuar-
to registro escrito (inclusive), cmo deberamos aplicar el mtodo
seek sobre el fichero?
Ejercicio 3.* Construya un programa que lea por teclado un cierto nmero de
clientes. Para cada cliente se debe introducir tambin por teclado un
nmero de cuenta, el nombre y el saldo. Estos datos se deben alma-
cenar en un fichero de acceso secuencial binario cuyo nombre se
dar como argumento por la lnea de rdenes.
Ejercicio 4.* Suponga que tenemos dos excepciones del sistema, Exception y
NumberFormatException que hereda de Exception. Queremos que
un mtodo capture la excepcin NumberFormatException cuando se
produzca la situacin especfica a la que corresponde, y que en los
dems casos se capture la excepcin Exception. En qu orden
habra que colocar las excepciones en la sentencia try-catch?
Ejercicio 5.* Suponga que tenemos un fichero llamado datos donde se han escri-
to una serie de enteros (int) por medio de la clase DataOutputS-
tream. Escriba un programa que utilice el mtodo readInt() de la
clase DataInputStream para leer todos los datos del fichero anterior
e imprimirlos por pantalla. Utilice la EOFException para controlar el
fin del fichero. Asegrese de que en cualquier situacin el fichero se
cierra adecuadamente.
REFERENCIAS
ARNOLD, K., GOSLING, J. y HOLMES, D.: El Lenguaje de Programacin Java, Addison Wesley, 2000.
HAROLD, E. R.: Java I/O, First Edition, OReilly, 1999.
Java: http://java.sun.com ltima visita realizada en junio de 2002.
JOYANES, L.: Fundamentos de Programacin, Segunda Edicin, McGraw-Hill, 1997.
258 Introduccin a la programacin con orientacin a objetos
LAMBERT, K. A. y OSBORNE, M.: Java A Framework for Programming and Problem Solving, PWS Publishing.
Brooks/Cole Publishing Company, 1999.
PRIETO, A., LLORIS, A. y TORRES, J. C.: Introduccin a la Informtica, McGraw-Hill, Segunda Edicin, 1995.
RUIZ, I. L., ROMERO DEL CASTILLO, J. A. y GMEZ-NIETO, M. A.: Ficheros. Organizaciones Clsicas para el Alma-
cenamiento de la Informacin, Universidad de Crdoba, 1998.
10
Ordenacin y Bsqueda
Sumario
10.1. INTRODUCCIN
En este captulo nos vamos a centrar en dos actividades comunes y totalmente extendidas en el
mbito de la programacin como son las de ordenacin y bsqueda. La ordenacin implica la dis-
tribucin de una serie de elementos de acuerdo a una cierta regla o norma. Por ejemplo, ordenar
una serie de nombres aplicando un criterio alfabtico. El inters de la ordenacin radica en su fre-
cuente uso y su utilizacin por parte de otras tcnicas como la bsqueda. Segn estadsticas acep-
tadas, los ordenadores gastan ms de un cuarto de su tiempo en labores de ordenacin (Smith,
1987). Como hemos indicado, la ordenacin suele ser un paso previo para acelerar bsquedas pos-
teriores. Justamente, la bsqueda es otra actividad, en principio sencilla, pero relacionada con la
ordenacin. La bsqueda implica la determinacin de la existencia de un cierto elemento en una
serie. Como podemos ver, en ambos casos se trabaja sobre un conjunto de elementos que se
encuentran recogidos en alguna estructura de datos que puede residir en memoria principal o
secundaria, normalmente dependiendo de su tamao. En este captulo y dado el carcter introduc-
torio del texto usaremos como estructuras de datos, estructuras lineales de elementos implementa-
das con matrices.
10.2. ORDENACIN
b) Mtodos de distribucin
En este texto nos vamos a centrar en los tres primeros casos de los mtodos basados en compa-
racin. Se remite al lector interesado en una visin ms completa de los diferentes mtodos a textos
ms especializados en el campo (Knuth, 1998; Smith, 1987; Wirth, 1986; Gonnet y Baeza-Yates,
1991).
La caracterstica comn de todos los mtodos que vamos a estudiar es que se basan en la realiza-
cin de comparaciones sobre el conjunto de elementos a ordenar. La manera en que se realizan estas
comparaciones vara de algoritmo en algoritmo, siendo la aproximacin ms directa la del algoritmo
de ordenacin por intercambio.
Ordenacin y Bsqueda 259
50 15 56 14 35 1
1. Comparar los dos primeros elementos, 50 y 15. Si estn en orden se mantienen como estn,
en caso contrario, se intercambian entre s. En este caso se intercambiaran:
15 50 56 14 35 1
En el ejemplo, la secuencia de pasos (indicando en negrita las parejas comparadas) para la prime-
ra pasada sobre el conjunto de elementos seran:
50 15 56 14 35 1
15 50 56 14 35 1
15 50 56 14 35 1
15 50 14 56 35 1
15 50 14 35 56 1
15 50 14 35 1 56
Como podemos observar, el ltimo nmero ya est en su sitio. Ahora repetiramos con la sublista
sin ordenar (todos los elementos menos el ltimo) y as hasta un total de cinco pasadas.
El nombre de mtodo de la burbuja proviene del hecho de que el elemento que se ordena en cada
iteracin va recorriendo la lista hasta su posicin final, igual que una burbuja en un vaso va desde el
fondo hasta arriba. Dada una lista de n elementos, el pseudocdigo para el algoritmo, ordenando en
orden creciente y considerando 0-origen para la lista, sera el siguiente,
Inicio
Leer matriz lista con n valores
Para ii0 mientras i < (n-1) incremento iii+1
Para ji0 mientras j < (n-i-1) incremento jij+1
Si lista (j) >lista (j+1) entonces
intercambiar lista (j) con lista (j+1)
Fin_Si
Fin_Para
Fin
260 Introduccin a la programacin con orientacin a objetos
Un mtodo en Java que aplicase este algoritmo a una lista de enteros podra ser el siguiente,
Obsrvese que al pasar la lista por referencia (puesto que es un objeto de clase matriz) no es nece-
sario devolverla con un return.
La ordenacin por seleccin toma su nombre del hecho de aplicar una serie de operaciones de selec-
cin para ordenar una lista. La idea bsica es la de seleccionar elementos uno a uno y colocarlos en su
posicin definitiva en la lista.
Supongamos que queremos ordenar una lista de valores numricos en orden creciente, por ejem-
plo la misma lista usada en el mtodo de la burbuja,
50 15 56 14 35 1
1. 1 15 56 14 35 50
Si la lista contiene n elementos, tras un total de (n 2 1) pasadas la lista estara ordenada. Como ilus-
tracin consideremos los resultados en cada pasada para la lista del ejemplo,
50 15 56 14 35 1
1 15 56 14 35 50
1 14 56 15 35 50
1 14 15 56 35 50
1 14 15 35 56 50
1 14 15 35 50 56 i Resultado final
Ordenacin y Bsqueda 261
Para una lista de n elementos, el pseudocdigo del algoritmo de seleccin, ordenando en orden cre-
ciente y con 0-origen para la lista, sera,
Inicio
Leer matriz lista con n valores
Para ji0 mientras j < n incremento jij+1
indice_minij
Para iij+1 mientras i < n incremento iii+1
seleccionar el ndice del elemento menor
Fin_Para
intercambiar lista(j) con lista(indice_min)
Fin_Para
Fin
La implementacin de la ordenacin por seleccin usa dos bucles para ordenar la lista. El bucle
externo controla la posicin de la matriz donde hay que colocar el valor menor. El bucle interno
encuentra el valor menor del resto de la lista, mirando en todas las posiciones mayores o iguales que
el ndice especificado en el ciclo externo. Cuando se encuentra el valor menor, se intercambia con el
valor almacenado en indice_min. Este algoritmo encuentra el valor menor en la lista durante cada
iteracin, por lo que ordena en orden ascendente. Se puede cambiar a orden descendente slo con bus-
car el valor mayor en cada iteracin.
Veamos un mtodo en Java que aplica el algoritmo:
n=lista.length;
Una vez ms, el paso por referencia de la lista hace innecesaria la devolucin de la matriz orde-
nada con un return.
En este mtodo se selecciona un elemento y se coloca directamente en el sitio que le corresponde entre
todos los que ya se han ordenado. La tcnica es la misma que cuando se ordena un palo de una bara-
ja de cartas.
262 Introduccin a la programacin con orientacin a objetos
Vamos a ilustrar el mtodo considerando una vez ms una ordenacin en orden creciente en la lis-
ta de valores,
50 15 56 14 35 1
1. Comenzamos con el segundo valor (el primero acta como referencia) y lo colocamos en la
posicin que le corresponda con respecto al primero. En nuestro ejemplo habra que poner el
valor 15 delante del 50.
1. 15 50 56 14 35 1
Lgicamente, para n elementos necesitaramos (n 2 1) inserciones para ordenar la lista. Para nues-
tra lista de ejemplo, el resultado de cada insercin (marcando el elemento insertado) sera,
15 50 56 14 35 1
15 50 56 14 35 1
14 15 50 56 35 1
14 15 35 50 56 1
1 14 15 35 50 56 i Resultado final
Inicio
Leer matriz lista con n valores
Para ii1 mientras i < n incremento iii+1
valori lista(i)
posicionii
Mientras (posicion>0) y (lista(posicion-1) > valor)
lista(posicion)ilista(posicion-1)
posicion iposicion-1
Fin_Mientras
lista(posicion)ivalor
Fin_Para
Fin
De manera similar al mtodo de seleccin esta ordenacin usa dos bucles para ordenar una lista.
En la ordenacin por insercin, sin embargo, el ciclo externo controla el ndice en la matriz del ele-
mento a ser ordenado. El ciclo interno compara el valor actual a ser colocado (insertado) con los valo-
res anteriores (los cuales son una sublista ordenada de la lista entera). Si el valor actual es menor que
el valor en posicin, entonces se corre el valor del ndice posicin a la derecha (una unidad
menos). Los desplazamientos continan hasta que se localiza un valor menor que el que estamos colo-
cando o hasta que llegamos al principio de la lista. Cada iteracin del ciclo externo aade un valor ms
a la sublista ordenada de la lista, hasta que la lista entera queda ordenada. El algoritmo realiza la inser-
Ordenacin y Bsqueda 263
cin de un valor copiando hacia la derecha los valores de la sublista ordenada. Por ejemplo, en nues-
tra lista cuando nos toca colocar el elemento con ndice 3 (el valor 14) el bucle Mientras realizara
la insercin de la forma siguiente,
15 50 56 14 35 1 i Punto de partida
15 50 56 56 35 1
15 50 50 56 35 1
15 15 50 56 35 1
14 15 50 56 35 1 i Resultado final
El efecto es que los valores de la sublista ordenada se mueven para hacer sitio al valor insertado.
Veamos un mtodo en Java que aplica el algoritmo:
n=lista.length;
for (int i=1; i<n; i++) {
valor=lista[i];
posicion=i;
while (posicion>0 && lista[posicion-1] > valor) {
lista[posicion]=lista[posicion-1];
posicion--;
}
lista[posicion]=valor;
}
} // Fin mtodo insercin
Una vez ms, el paso por referencia de la matriz simplifica la devolucin de los resultados.
A la hora de seleccionar un mtodo de ordenacin u otro hay diferentes factores a tener en cuenta. As,
la facilidad de inteligibilidad del algoritmo, su eficiencia o el consumo de recursos requeridos pueden
ser factores importantes. El criterio ms usado es el de la eficiencia medida a travs de la complejidad
de cada algoritmo. Desde este punto de vista debemos considerar el nmero de comparaciones reali-
zadas y el nmero de intercambios o recolocaciones que se hacen con los elementos de la lista. Ana-
licemos los dos factores,
a) Nmero de comparaciones
Para n elementos, el mtodo de la burbuja realiza como mximo n 2 i comparaciones en la pasada i,
existiendo un total de n 2 1 pasadas. El nmero total de comparaciones es,
y el algoritmo es de orden n 2.
264 Introduccin a la programacin con orientacin a objetos
10.3. BSQUEDA
La bsqueda es el proceso de determinar si un elemento particular, llamado a veces valor clave o valor
objetivo, est incluido (o no) en una lista de elementos, y si es as dnde. Al igual que para la ordena-
cin, existen distintos algoritmos de bsqueda, cada uno con su propia complejidad. Algunos algorit-
mos de bsqueda necesitan que la lista est ya ordenada. Vamos a presentar en este texto dos tcnicas
de bsqueda apropiadas para estructuras de datos lineales: la bsqueda lineal o secuencial (que no
necesita ordenacin previa) y la bsqueda binaria que s precisa ordenacin.
1. Comparar el valor clave buscado con el elemento actual de la lista. Si coinciden el proceso
concluye.
2. Si no hemos encontrado el valor clave pasamos al siguiente elemento de la lista y repetimos
el paso 1.
El pseudocdigo para el algoritmo de bsqueda secuencial para una lista de n valores implemen-
tada en una matriz con 0-origen, y usando un valor centinela de -1 para indicar que la clave no est en
la lista, sera,
Inicio
Leer lista y clave
posicioni 0
Ordenacin y Bsqueda 265
Si (posicion=n) entonces
posicini -1
Fin_Si
Devuelve posicion
Fin
Como vemos, se recorre la matriz hasta que se encuentra el valor clave o hasta que se acaba la
matriz. Si hemos encontrado el elemento, se coloca su ndice en la variable posicion y si no, se le
asigna 21.
Un mtodo en Java que implementara el algoritmo sobre una lista representada por una matriz de
enteros sera el siguiente:
Obsrvese que al mtodo hay que pasarle la lista y la clave. Como la lista no tiene por qu estar
ordenada ni organizada de ninguna forma, el valor buscado (la clave) puede estar en cualquier posi-
cin. Por lo tanto, debemos examinar todos los elementos de la lista para determinar si la clave no est
en la lista. El mtodo slo encuentra el primer valor de la lista coincidente con el valor buscado. Si
queremos buscar todos los elementos de la lista coincidentes con el buscado, deberamos recorrer la
lista entera y almacenar, por ejemplo, en una matriz auxiliar las posiciones donde se encuentran los
elementos encontrados.
Si la lista a usar est previamente ordenada podemos hacer uso de este hecho para acelerar el proceso
de bsqueda. sta es la idea bsica de la bsqueda binaria. En este caso se aplica una aproximacin de
tipo divide y vencers. Lo que se hace es localizar el elemento central de la lista y ver si corresponde
a la clave. Si es as hemos solucionado el problema pero, aunque no lo sea, el hecho de tener la lista
ordenada me permite, comparando si el valor de la clave es mayor o menor que el elemento central,
descartar toda una mitad de la lista. Es decir, he reducido el problema a buscar en la mitad de ele-
mentos. Los pasos del algoritmo seran,
266 Introduccin a la programacin con orientacin a objetos
En el algoritmo debemos considerar que puede que no haya exactamente un valor central que divi-
da en dos la lista. Si no lo hay (porque haya un nmero par de elementos) tomamos el primer elemento
de los dos centrales.
Veamos el mtodo en la prctica. Por ejemplo, dada la siguiente lista
1231 1473 1545 1838 1892 1898 1983 2005 2446 2685 3200
1. Examinamos el elemento central, en este caso como hay once elementos el central sera el sex-
to, 1898. Como 1983 es mayor que 1898, se desprecia la primera sublista y nos centramos en
la segunda:
2. Exploramos el elemento central, 2446. Como es mayor que 1983, eliminamos la segunda par-
te de la sublista y nos queda:
1. 1983 2005.
3. Al no haber trmino central, elegimos el primero de los dos que quedan, que en este caso
es el nmero buscado. Se han hecho 3 comparaciones. En la bsqueda lineal se hubieran
hecho 7.
Suponiendo que tenemos una lista de n elementos con 0-origen, ordenada en orden creciente, y que
se devuelve el valor 21 como valor centinela para indicar que la clave no est en la lista, el pseu-
docdigo para el algoritmo sera:
Inicio
posicion i 0
izquierda i 0
derecha i n-1
clave i Valor buscado
posicion= -1
Fin_Si
Devuelve posicion
Fin
// Bsqueda binaria
if (clave != lista[posicion]) {
posicion=-1;
}
return posicion;
La implementacin de la bsqueda binaria usa los enteros derecha e izquierda para indicar los
lmites de la regin de la matriz que se est considerando. Inicialmente, son la primera y ltima posi-
ciones de la matriz. En cada iteracin, al eliminarse la mitad de los datos a considerar, se actualizan los
valores de derecha e izquierda. El elemento del medio se consigue haciendo el cociente entero del
promedio de derecha e izquierda. Cuando el nmero de elementos a considerar es par, hay dos
posibilidades para la eleccin del elemento del medio. Esta implementacin elige el de la izquierda, es
decir, el anterior, puesto que al hacer la divisin el operador trunca el resto. Esta decisin es arbitraria.
La bsqueda termina cuando se encuentra la clave buscada o cuando se termina de explorar la lista.
Al igual que para los algoritmos de ordenacin, resulta interesante el estudio comparativo de los mto-
dos de bsqueda presentados.
Respecto al comportamiento en el caso ms desfavorable el anlisis es sencillo. Para la bsqueda
lineal tenemos una comparacin que se realiza siempre dentro de un bucle que se repite, en el peor
caso (cuando el elemento buscado no est en la lista o es el ltimo), n veces para n elementos. El nme-
ro de comparaciones es por lo tanto n y la complejidad del algoritmo es de orden n, O(n).
En el caso de la bsqueda binaria con n elementos en la lista, en el peor de los casos, el elemento
no estar en la lista y habr que dividir la misma hasta que slo quede un elemento. En cada particin
reducimos el nmero de elementos a la mitad (aproximadamente). As, partiendo de n elementos ira-
268 Introduccin a la programacin con orientacin a objetos
mos obteniendo n/2, n/4, n/8, etc., elementos en cada particin. Si el nmero de particiones por la
mitad necesarias para obtener un solo elemento es de m, se cumple que:
n
15} m}
2
o bien,
m5log2n
Como podemos observar cuanto mayor es n mucho ms til es el uso de la bsqueda binaria.
EJERCICIOS PROPUESTOS
Ejercicio 1.* Implemente en Java una versin recursiva del algoritmo de ordena-
cin por seleccin.
Ejercicio 2.* Implemente en Java una versin recursiva del algoritmo de bs-
queda lineal.
Ejercicio 3.* Implemente en Java una versin recursiva del algoritmo de bs-
queda binaria.
1
ste es un tratamiento simplificado del clculo de la complejidad del algoritmo de bsqueda binaria. El lector intere-
sado en un tratamiento ms detallado y riguroso, aunque conducente al mismo resultado, puede consultar Rawlins, 1992.
Ordenacin y Bsqueda 269
REFERENCIAS
GONNET, G. H. y Baeza-Yates, R.: Handbook of Algorithms and Data Structures, Second Edition, Addison-Wes-
ley, 1991.
KNUTH, D. E.: The art of computer programming. Vol. 3: Sorting and Searching, Second edition, Addison-Wes-
ley, 1998.
RAWLINS, G. J. E.: Compared to what? An introduction to the analysis of algorithms, Computer Science Press, 1992.
SMITH, H. F.: Data Structures. Form and Function, Harcourt Brace Jovanovich, Publishers, 1987.
WIRTH, N.: Algoritmos+Estructuras de datos=Programas, Ediciones del Castillo, Madrid, 5. reimpresin, 1986.
A
Soluciones a los
ejercicios propuestos
272 Introduccin a la programacin con orientacin a objetos
Este apndice contiene las soluciones a los ejercicios propuestos en los captulos del libro divididas en
diez secciones, una por captulo. Siempre que el ejercicio consiste en el desarrollo de un programa se
ha dividido la solucin en anlisis, diseo e implementacin. Tngase en cuenta que la solucin a la
implementacin no es nica, nosotros hemos indicado una de las posibles.
Ejercicio 1
La informacin es un conjunto de datos con una determinada organizacin, son datos significativos.
Ejercicio 2
Digital.
Ejercicio 3
El cdigo UNICODE permite representar muchos ms caracteres (por ejemplo, los acentuados) ya que
usa 16 bits.
Ejercicio 4
212 = 4096
Ejercicio 5
Por la Unidad de Control y la Unidad Aritmtico-Lgica.
Ejercicio 6
230
Ejercicio 7
Es de lectura-escritura.
Es voltil.
Ejercicio 8
Una memoria adicional pequea pero muy rpida, donde se guarda la informacin ms usada.
Ejercicio 9
Conexin de lnea compartida.
Ejercicio 10
TCP/IP.
Soluciones a los ejercicios propuestos 273
Ejercicio 1
Lenguajes de cuarta generacin.
Lenguajes de alto nivel.
Lenguaje ensamblador.
Lenguaje mquina.
Ejercicio 2
El lenguaje compilado es ms rpido, ya que el interpretado va traducindose y ejecutndose senten-
cia a sentencia (o seccin a seccin).
Ejercicio 3
Errores en tiempo de compilacin.
Errores en tiempo de ejecucin.
Errores lgicos.
Ejercicio 4
Error lgico.
Ejercicio 5
Lo ms aconsejable es comparar los resultados del programa con los de algn ejemplo conocido, o ir
siguiendo manualmente el flujo de control del programa para detectar qu es lo que no se hace correc-
tamente. Mostrar resultados parciales y consultar los valores de las variables ayuda a delimitar el rea
donde se encuentra el error.
Ejercicio 6
Anlisis, Diseo, Codificacin, Pruebas y Mantenimiento.
Ejercicio 7
El mantenimiento (70-80% del esfuerzo total del ciclo de vida).
Ejercicio 8
En la etapa de anlisis.
Ejercicio 9
El A sera ms complejo.
Ejercicio 10
En un programa monoltico slo hay un programa principal.
Ejercicio 11
En diseo.
274 Introduccin a la programacin con orientacin a objetos
Ejercicio 1
Se trata de un ejemplo de precedencia de operadores. Sustituyendo los valores de las variables y eva-
luando por orden de precedencia tendramos,
1 1 4/2 1 1
1 1 2 (cociente entero) 1 1
311
Ejercicio 2
El caso a) es una comparacin, el resultado es true o false.
El caso b) es una asignacin, el resultado es que la variable b (que debe ser lgica) adquiere el
valor true.
Ejercicio 3
En el caso a) primero se aplica el operador y luego la asignacin, por tanto: total=4, num=4.
En el caso b) primero se realiza la asignacin y luego el incremento, por tanto: num=2, total=3.
En el caso c) el operador usado como prefijo incrementa el valor de la variable antes de que sta
se use en la expresin. Con el operador como sufijo se usa el valor primero y se incrementa despus.
Entonces, con ++num se incrementa num usndose ya incrementado, mientras que en num++ se usa el
valor de num y despus se incrementa. Con ++num incrementamos primero num a 4 y luego se usa
en la expresin. Luego se suma el valor de num (4) obteniendo 8, que es lo que se almacena en total
y num se incrementa posteriormente con el ++. El resultado final es total=8 y num=5.
Ejercicio 4
Cuando la comparacin es con nmeros en punto flotante la igualdad de dos nmeros no se debe
comparar directamente como numero1==numero2. Esto es porque slo van a ser iguales si todos los
bits que los representan son iguales, y en clculos con nmeros reales hay siempre error de redondeo.
Lo que hay que comprobar es si son muy parecidos, usando un valor lmite adecuado a cada proble-
ma. Si en nuestro caso, el lmite viene dado por la precisin de la representacin numrica, p, tendra-
mos:
Hemos usado el mtodo valor absoluto (abs) de la clase Math que contiene mtodos matemti-
cos. La clase Math esten el paquete java.lang y, por lo tanto, no hay que importarla explcita-
mente. Tomamos el valor absoluto para que no afecte el signo de la diferencia entre r1 y r2.
Soluciones a los ejercicios propuestos 275
Ejercicio 5
ANLISIS
En estos ejemplos sencillos el anlisis estimplcito en el enunciado. En este caso vamos a leer los
datos por el teclado, evaluando los parmetros requeridos y produciendo una salida por el monitor.
DISEO
La entrada y salida de informacin se realizar usando la clase BufferedReader y
System.out.println(), respectivamente. El permetro, 1, y la superficie, s, se obtendrn por
medio de las relaciones:
l 5 2r
s 5 r2
donde r representa el radio. Para evaluar el cuadrado de r podemos hace r*r o usar el mtodo poten-
cia de la clase Math que permite evaluar ab como Math.pow(a,b).
IMPLEMENTACIN
import java.io.*;
class Circunferencia {
11public static void main(String[] args) throws IOException {
// Declaraciones
1111double radio, perimetro, superficie;
1111BufferedReader leer =new BufferedReader
111111111111111111(new InputStreamReader(System.in));
// Lectura
1111System.out.println(Introduzca el radio: );
1111radio=Double.parseDouble(leer.readLine());
// Procesamiento
1111perimetro=2.*Math.PI*radio;
1111superficie=Math.PI*radio*radio; // Como alternativa se
111111111111111111111111111111111111// puede usar el mtodo
111111111111111111111111111111111111// Math.pow(a, b)
// Impresin de resultados
1111System.out.println(perimetro: +perimetro+ unidades);
1111System.out.println(superficie: +superficie
1111111111111111111111+ unidades^2);
11}//Fin del main
}//Fin de la clase
Ejercicio 6
ANLISIS
Una vez ms, el enunciado define el anlisis. Las labores de lectura y escritura se realizarn por tecla-
do y por monitor, respectivamente. La tarea es nica, evaluar un sumatorio de cuadrados.
DISEO
La entrada y salida de informacin se realizar con la clase BufferedReader y
System.out.println(), respectivamente. El sumatorio de los cuadrados se evaluarcon un bucle de
tipo while y los cuadrados se evaluarn como productos ( n 2 5 n n).
276 Introduccin a la programacin con orientacin a objetos
IMPLEMENTACIN
import java.io.*;
class Cuadrados {
11public static void main(String [] args) throws IOException {
1111int n, n_2, i;
1111BufferedReader leer =new BufferedReader
11111111111111111(new InputStreamReader(System.in));
Ejercicio 7
El programa da un error de compilacin porque la variable j estdeclarada dentro de la seccin inter-
na que va delimitada por las llaves ({}). Fuera de ese bloque la variable no estdeclarada, es decir, su
alcance es el bloque en cuestin. Por eso se produce un error de variable no declarada al intentar usar
j en el println fuera del bloque.
Ejercicio 8
ANLISIS
La entrada y salida se realiza por teclado y monitor y la funcionalidad del programa queda definida en
el enunciado.
DISEO
La entrada y salida de informacin se gestionarn usando las clases BufferedReader y
System.out.println(), respectivamente. El pH se calcularde acuerdo a la expresin dada en el
enunciado. La peticin continuada de concentraciones se implementarcon un bucle while, que se
repetirhasta que el usuario indique que no desea ms clculos. En este caso, pondremos a false la
condicin que controla el bucle.
IMPLEMENTACIN
import java.io.*;
class Pehache {
11public static void main(String [] args) throws IOException {
1111double Ka, pKa, c, pH;
Soluciones a los ejercicios propuestos 277
1111while (sigue) {
111111System.out.println();
111111System.out.println(Concentracion (M):);
111111c=Double.parseDouble(leer.readLine());
111111pH= (pKa-0.43429448* Math.log(c))/2.0;
111111System.out.println();
111111System.out.println(pH:+pH);
111111System.out.println();
111111System.out.println(Desea usar otra concentracion?);
111111System.out.println(Teclee 1 para si, otra opcion para no);
111111opcion=Integer.parseInt(leer.readLine());
111111if (opcion!=1) {
11111111sigue=false;
111111}
1111}
11}111// Fin mtodo main
} // Fin clase Pehache
Ejercicio 9
probador:c
probador:d
probador:deed
La primera lnea imprime el valor de probador que es c. La segunda lnea vuelve a imprimir
el valor de probador que en este caso es d, porque se ha incrementado en uno su valor, con lo cual
probador contiene la siguiente letra del cdigo Unicode. En la tercera lnea, la variable probador
se incrementa, pero al utilizar el operador sufijo primero se imprime el valor que tena la variable (d)
y luego se incrementa. A continuacin, se vuelve a imprimir probador cuyo valor en ese momento
es e (debido al incremento). Posteriormente probador es decrementado usando el operador sufijo.
Por esta razn se imprime el valor de probador antes de realizar el decremento (se imprime e) y
despus se realiza el decremento. Por eso, cuando finalmente se vuelve a imprimir probador en pan-
talla aparece una d. Resultando la salida deed.
Ejercicio 10
ANLISIS
Una vez ms, el problema es sencillo y el enunciado nos sirve como documento de anlisis indicando
claramente la funcionalidad necesaria. Baste indicar que como habitualmente la lectura y la escritura
se realizarn por teclado y pantalla, respectivamente.
278 Introduccin a la programacin con orientacin a objetos
DISEO
La distincin del caso de masa mayor de 1 kg se realizarcon un if. El clculo de la frecuencia se
realizarcon la expresin genrica, slo si la masa es menor de 1 kg. La entrada y salida de informa-
cin se realizarusando las clases BufferedReader y System.out.println(), respectivamente.
IMPLEMENTACIN
import java.io.*;
class Pendulo {
11public static void main(String [] args) throws IOException {
1111double l, m, frecuencia;
1111final double g=9.8 ; // (m/s^2) Sistema internacional
111111frecuencia = Math.sqrt(g/l);
111111frecuencia = frecuencia /(2.0*Math.PI);
Ejercicio 11
21 21 22
En el primer caso se usa el operador de incremento como prefijo, por lo cual primero se incrementa
la variable indice y luego se imprime. En el segundo caso el operador es sufijo por lo cual primero
se imprime y luego se incrementa. Finalmente, la ltima ocurrencia de indice en el println impri-
me el valor actual que es una unidad ms que el impreso anteriormente.
Ejercicio 1
Este ejemplo aunque simple presenta una situacin ms realista que los vistos hasta ahora. El progra-
ma es ms complejo que los desarrollados anteriormente y presenta una estructura representativa de
un programa real. Ya en este simple ejemplo podemos ahondar en las ventajas de una aproximacin
Soluciones a los ejercicios propuestos 279
sistemtica al desarrollo de software. Aunque de manera un tanto informal abordemos una etapa de
anlisis y diseo antes de la codificacin.
ANLISIS
En esta etapa debemos responder a la pregunta de qu debe hacer el programa. En la aproximacin tra-
dicional aplicamos un punto de vista funcional centrndonos en las tareas (funciones) que debe desa-
rrollar el software. A partir del enunciado podemos evaluar una lista de requisitos:
Calcular factorial.
Distinguir entero negativo.
Distinguir caso de N 5 0 (relacionado con el primer requisito).
Preguntar si continuar o no (repitiendo si no se hace una seleccin vlida).
Indicar si se contina calculando factoriales o no.
DISEO
Una vez realizada la recoleccin de requisitos abordemos el diseo. Vamos a determinar cmo lleva-
mos a la prctica cada uno de los requisitos. Como herramienta de diseo y, en particular, como herra-
mienta de especificacin de algoritmos, utilizaremos pseudocdigo. Abordando cada uno de los
requisitos tendramos:
Calcular factorial
Inicio
11factorial i1
11Para i i 1 mientras i n incremento i i i+1
1111factorial ifactorial*i
11Fin_Para
Fin
Obsrvese que la variable repite se pone a falso dentro del bucle. Si se pusiera a falso justo antes de
entrar en el bucle, cuando hubiera una lectura de un n menor que 0 habramos generado un bucle infinito.
Distincin caso n 5 0
Teniendo en cuenta que 0! 5 1 el algoritmo podra ser
Inicio
11factoriali1
280 Introduccin a la programacin con orientacin a objetos
Inicio
11factoriali1
11Si (n!=0) entonces
1111Para i i1 mientras i n incremento iiI+1
111111factorial ifactorial*i
1111Fin_Para
11Fin_Si
Fin
Obsrvese que otra_opcion se inicializa a verdadero fuera del bucle. En este caso no hay posi-
bilidad de generar un bucle infinito pues la asignacin de valor dentro del Si es a falso y en ese caso
terminara el bucle.
Indicar si se contina calculando factoriales o no
Inicio
11numero iverdadero
11Mientras (numero)
1111Distinguir entero negativo
1111Distincin caso n=0 y clculo del factorial
1111Preguntar si continuar o no
1111Si (opcion = no seguir) entonces
111111numero ifalso
1111Fin_Si
11Fin ientras
Fin
IMPLEMENTACIN
import java.io.*;
class Factorion {
Soluciones a los ejercicios propuestos 281
1111pide_numero=true;
1111while (pide_numero) {
111111factorial=1.0;
111111do {
11111111repite=false;
11111111System.out.println(Introduzca N para calcular N!:);
11111111n=Integer.parseInt(leer.readLine());
11111111if (n <0 ) {
1111111111repite=true;
1111111111System.out.println( N no puede ser negativo);
11111111}
111111} while (repite);
111111otra_opcion=true;
111111do {
11111111System.out.println(Desea calcular otro factorial?);
11111111System.out.println(Si: 0);
11111111System.out.println(No: 1);
11111111opcion=Integer.parseInt(leer.readLine());
11111111if (opcion!=0 && opcion !=1) {
1111111111System.out.println(Las opciones validas son s o n);
11111111}
11111111else {
1111111111otra_opcion=false;
11111111}
111111} while (otra_opcion);
111111if (opcion==1) {
11111111pide_numero=false;
111111}
1111}111// Fin del while (pide_numero)
11}111// Fin mtodo main
} // Fin clase Factorion
Ejercicio 2
if (opcion==1){
11System.out.println(Uno);
282 Introduccin a la programacin con orientacin a objetos
}
else {
11if (opcion==2) {
1111System.out.println(Dos);
11}
11else {
1111if (opcion==3) {
111111System.out.println(Tres);
1111}
1111else {
111111System.out.println(Otros);
1111}
11}
}
Ejercicio 3
class Asterisco{
11public static void main(String[] args){
1111for (int i=0;i<3;i++){
111111for (int j=0;j<i;j++) {
11111111System.out.print( );
111111}
111111for (int k=0;k<5-2*i;k++){
11111111System.out.print(*);
111111}
111111System.out.println();
1111}
11}
}
Ejercicio 4
No es correcto, se suma hasta n+1. Para arreglar el problema basta con intercambiar las dos ltimas
sentencias.
Ejercicio 5
class Asterisco {
11public static void main(String [] args) {
1111for (int i=5; i>0; i) {
111111for (int j=0; j<i;j++) {
11111111System.out.print(*);
111111}
111111System.out.println();
1111}
11}
}
Ejercicio 6
j=0;
while (i<n && j != -1) {
Soluciones a los ejercicios propuestos 283
11j=objetoX.valor(i);
11i++;
}
Ejercicio 7
class Asterisco {
111public static void main(String[] args) {
11111for (int i=7; i>0; i) {
1111111for (int k=0; k<7-i;k++) {
111111111System.out.print( );
1111111}
1111111for (int j=0; j<i; j++) {
111111111System.out.print(*);
1111111}
1111111System.out.println();
11111}
111}
}
Ejercicio 8
Es un ejemplo de uso de un switch con algunos casos sin break. Hay un bucle que recorre un ndi-
ce de cero a 7 y, en funcin de dnde hay break y dnde no, el resultado es:
0 es menor que 3
1 es menor que 3
2 es menor que 3
3 es menor que 6
4 es menor que 6
5 es menor que 6
6 es 6 o mayor
7 es 6 o mayor
Ejercicio 9
En el programa tenemos un switch controlado por el valor de x (que es cero). El flujo de control ira
al case 0 pero al no haber break entrara luego en el default. All, asignara el valor 1 a s y al
acabar el switch imprimira este valor. El resultado sera: 1.
Ejercicio 10
while (m<=11) {
11m++;
11d=1;
11while (d<=30){
1111if (m==2 && d==29){
111111d=31;
1111}
1111else{
111111System.out.println(m+ +d);
111111d++;
1111}//del else
11}//del while
}//del while
284 Introduccin a la programacin con orientacin a objetos
Ejercicio 11
ANLISIS
Es til organizar en forma de tabla las acciones en funcin de las condiciones (Tabla de decisin):
3 cntimos/km 10% 15 %
distancia >200 km S No No
3 personas No S No
distancia >400 km S No S
DISEO
La tabla muestra que el descuento del 15% lleva como prerrequisito que la distancia sea mayor de
400 km y, lgicamente, en ese caso tambin se cumple que la distancia es mayor de 200 km. Esto nos
indica un if anidado. Como vemos en la tabla, el descuento del 10% slo depende de si hay o no tres
personas. Al ser independiente de las condiciones anteriores lo que tenemos es un if concatenado. En
pseudocdigo, el algoritmo correspondiente sera,
Inicio
11Leer km y personas
11Escribir billete
Fin
IMPLEMENTACIN
import java.io.*;
class Viaje {
11public static void main(String [] args) throws IOException {
1111int personas=0;
1111double billete, km;
1111BufferedReader leer =new BufferedReader
11111111111111111(new InputStreamReader(System.in));
1111System.out.println(Introduzca kilometraje: );
1111km=Double.parseDouble(leer.readLine());
1111System.out.println(Introduzca personas: );
1111personas=Integer.parseInt(leer.readLine());
1111System.out.println();
1111if (personas>2) {
111111billete=billete*0.90; // Descuento del 10%
1111} // Fin if (concatenado al anterior)
Ejercicio 12
ANLISIS
Para poder usar un bucle for debemos conocer cuntas veces va a repetirse el mismo. Una forma sen-
cilla de conseguirlo es leer el nmero de valores desde el teclado y realizar la lectura de cada valor
dentro del bucle. En este mismo bucle podemos ir haciendo el sumatorio de valores.
DISEO
El pseudocdigo para el proceso solicitado sera,
Inicio
11Leer numero de valores, n
11media i 0.0
11Para i i 0 mientras i < n incremento i i i+1
1111Leer valor i
1111media i media+valor
11Fin_Para
11mediaimedia/n
11Escribir media
Fin
IMPLEMENTACIN
Implementando el algoritmo en Java el resultado sera el siguiente,
import java.io.*;
class Media {
11public static void main(String [] args) throws IOException {
1111int n;
1111double valor, media;
1111BufferedReader leer =new BufferedReader
111111111111111111111111(new InputStreamReader(System.in));
1111media=0.0;
1111for (int i=0; i<n; i++) {
286 Introduccin a la programacin con orientacin a objetos
1111System.out.println(Media: +media);
11}
}
Ejercicio 1
ANLISIS
Se trata de una simulacin. Nuestro programa debe realizar la simulacin del lanzamiento del dado
generando uno de los valores enteros comprendidos entre 1 y 6.
DISEO
El mtodo random() de la clase Math devuelve un double mayor o igual que 0 y menor que 1. Noso-
tros queremos que nos devuelva los valores 1, 2, 3, 4, 5, 6. Para ello debemos cambiar la escala del
resultado del mtodo. Cmo? Como queremos que el valor mximo sea 6 y el origen sea 1, multipli-
camos por 6 el resultado del nmero aleatorio. Obtendremos como mximo el valor 5.999999 y como
valor mnimo 0. Como queremos que el valor mnimo sea 1, sumamos 1 al resultado obtenido. Ten-
dremos entonces valor mnimo 1 y valor mximo 6.9999999. Como slo queremos nmeros enteros nos
quedamos con la parte entera, usando un molde. La simulacin del dado se encapsularen un mtodo.
IMPLEMENTACIN
/*
111Programa que simula el lanzamiento de un dado usando
111el mtodo random() Genera un numero double 0<=n<1
*/
import java.io.*;
class Aleatorio {
11public static void main(String [] args) throws IOException {
1111int n;111111111111//nmero de tiradas
1111int tirada;
1111BufferedReader leer = new BufferedReader
1111111111111111111111111(new InputStreamReader(System.in));
1111System.out.println(Numero de tiradas:);
1111n=Integer.parseInt(leer.readLine());
11111return ((int)(1+6*Math.random()));
11}// Fin del mtodo dado
} // Fin clase
Ejercicio 3
ANLISIS
Una vez ms en este ejemplo tan sencillo los requisitos estn claramente establecidos en el enuncia-
do. As, la lectura sera travs de la lnea de rdenes y la salida por pantalla. La funcionalidad est
clara: obtener un valor mximo y uno mnimo.
DISEO
a) Estructuras de datos
Lo ms cmodo es usar una matriz monodimensional para almacenar y procesar los datos. Esto per-
mite usar un bucle para el procesamiento. La otra alternativa es usar una variable para cada valor, pero
eso hace que se complique mucho el cdigo para obtener el mismo resultado.
b) Algoritmos
Para obtener el mximo y el mnimo vamos seleccionando los valores mayores o menores de la lista
que tengamos, usando un bucle. Veamos el pseudocdigo:
Inicio
11mximo ivalores (0)
11Para i i1 mientras i n incremento iii+1
1111Si (mximo < valores(i)) entonces
111111mximo ivalores (i)
1111Fin_Si
11Fin_Para
Fin
La determinacin del mnimo es trivial visto el ejemplo anterior, basta con sustituir la compara-
cin mximo < valores(i) por mnimo > valores(i).
c) Diagrama de estructura
Podemos modularizar el programa definiendo dos mtodos que determinen, uno el mximo (mtodo max)
otro el mnimo (mtodo min). Con esto el diagrama de estructura sera el recogido en la Figura A.5.1.
main (Principal)
max min
Figura A.5.1. Diagrama de estructura del programa para el clculo del mximo y del mnimo
288 Introduccin a la programacin con orientacin a objetos
IMPLEMENTACIN
class Maxmin {
11public static void main(String [] args) {
1111double maximo, minimo;
1111double [] valores =new double [3];
111111valores[i]=Double.parseDouble(args[i]);
111111System.out.println( valor [+i+]: +valores[i]);
1111}
1111maximo=max(valores);
1111minimo=min(valores);
11// Salida de resultados
1111System.out.println(valor maximo: +maximo);
1111System.out.println(valor minimo: +minimo);
11} // Fin mtodo main
} // Fin de la clase
Obsrvese que en el mtodo max hemos usado el identificador max para denominar al mtodo y a
una variable. No hay ningn problema en ello, el sistema sabe lo que es el mtodo y lo que es la varia-
ble.
Ejercicio 4
ANLISIS
El enunciado es una vez ms muy claro. Se trata de un ejemplo de sobrecarga de mtodos. La lectura
Soluciones a los ejercicios propuestos 289
DISEO
a) Estructuras de datos
Para poder ser un ejemplo de sobrecarga de mtodos debemos pasar los dos o tres nmeros al mtodo
correspondiente. Esto quiere decir que aqu no debemos usar una matriz monodimensional pues enton-
ces slo hara falta un mtodo.
b) Algoritmos
El algoritmo se ilustra con el caso ms complejo, el de tres elementos. El pseudocdigo correspon-
diente sera,
Inicio
11mximoia
11Si (b>mximo) entonces
1111mximoib
11Fin_Si
11Si (c>mximo) entonces
1111mximoic
11Fin_Si
Fin
c) Diagrama de estructura
La Figura A.5.2 ilustra la estructura arquitectnica del programa solicitado.
main (Principal)
max max
Figura A.5.2. Diagrama de estructura del programa para el clculo del mximo con dos o tres
nmeros
IMPLEMENTACIN
class MaxSobrecarga {
11public static void main(String [] args) {
1111double a, b, c, maximo;
11// Asignacin de los datos y eco de los mismos
1111a= Double.parseDouble(args[0]);
1111b= Double.parseDouble(args[1]);
1111System.out.println(Primer valor : +a);
1111System.out.println(Segundo valor: +b);
1111if (args.length==2) {
111111maximo = max(a, b);
1111}
290 Introduccin a la programacin con orientacin a objetos
1111else {
111111c= Double.parseDouble(args [2]);
111111System.out.println(Tercer valor : +c); // Eco del
11111111111111111111111111111111111111111111111// tercer dato
111111maximo = max(a,b,c);
1111}
} // Fin de la clase
Ejercicio 5
El tringulo de Pascal es una disposicin triangular de enteros donde los elementos de cada fila son
simtricos, empiezan (y terminan) en uno y los restantesColumnas
elementos de la fila vienen dados por la suma
de los elementos contiguos5 colocados
4 en
3 la fila
2 superior.
1 Las
0 primeras
1 filas
2 del3 tringulo
4 son:5
Fila 0 1
Fila 1 1 1
Fila 2 1 2 1
Fila 3 1 3 3 1
Fila 4 1 4 6 4 1
Fila 5 1 5 10 10 5 1
ANLISIS
Estos coeficientes estn relacionados con los coeficientes binmicos, es decir, con el nmero de com-
binaciones, formas diferentes de seleccionar, k elementos tomados de un conjunto de n elementos en
total. Se trata de determinar los subconjuntos, as que el orden de los elementos en el subconjunto no
cuenta ({A,B,C} es el mismo subconjunto que {B,C,A}). Un ejemplo con 3 elementos sera:
Subconjuntos de 0 elementos: 1
Soluciones a los ejercicios propuestos 291
Subconjuntos de 1 elementos: 3
Subconjuntos de 2 elementos: 3
Subconjuntos de 3 elementos: 1
(A 1 B)0 5 1
(A 1 B)1 5 1A1 1B
(A 1 B)2 5 1A21 2AB1 1B2
(A 1 B)3 5 1A3 1 3A2B1 3AB21 1B3
etc.
DISEO
Los coeficientes del tringulo se obtienen con la expresin dada en el enunciado. Para escribir el trin-
gulo en la entrada de datos se dara el nmero de filas del mismo, teniendo en cuenta que la primera
fila sera la de ndice cero. Dado un valor n y empezando en 0, los valores de k van desde 0 hasta n.
a) Estructuras de datos
Como estructuras de datos no tenemos que usar ninguna en especial. Trabajaremos con variables.
b) Algoritmo
En pseudocdigo tendramos,
Inicio
1Leer nmero de filas (filas)
1Para ni0 mientras n<filas incremento nin+1
111Para ki0 mientras k n incremento kik+1
11111nmeroic(n,k)
11111Escribir nmero sin saltar lnea
111Fin_Para
111Escribir lnea en blanco
1Fin_Para
Fin
c) Diagrama de estructura
En este caso podemos definir un mdulo para calcular cada coeficiente y otro para evaluar el factorial.
El diagrama correspondiente se muestra en la Figura A.5.3.
292 Introduccin a la programacin con orientacin a objetos
Principal (main)
c(n,k)
Factorial
IMPLEMENTACIN
/*****************************************************
111Programa para la obtencin del tringulo de Pascal
111El nmero de filas a generar se introduce como
111argumento en la lnea de rdenes
1*****************************************************/
class Pascal {
11public static void main(String [] args ) {
1111int filas, numero;
1111filas=Integer.parseInt(args[0]);
1111for (int n=0; n<filas; n++){
111111for (int k=0; k<=n;k++){
11111111numero=c(n,k);
11111111System.out.print(numero+ );
111111}
111111System.out.println();
1111}
11} // Fin mtodo main
11public static int c(int n, int k) {
1111return fact(n)/(fact(k)*fact(n-k));
11}11// Fin mtodo c(n,k)
Obsrvese que el resultado es un tringulo de Pascal escrito sin formato, es decir, que para las pri-
meras cinco filas tendramos,
1
11
121
1331
Soluciones a los ejercicios propuestos 293
14641
Ejercicio 7
Al hacer b=a hacemos que b refiera a lo mismo que a. Es decir, se realiza una copia de la referencia
tal como se ilustra en la Figura A.5.4.
{0, 1, 2}
b
Al hacer b[1]=3 el elemento original que es 1 se sustituye por 3. Teniendo en cuenta que tanto a
como b refieren a lo mismo, se pasan al metodo1 y se hace la suma con c, salvando el resultado en
la propia matriz c. Lo que se devuelve es la referencia al resultado, por lo que al final del programa,
a y c se refieren a lo mismo, dejando a de referirse a la matriz b. En un diagrama tendramos el resul-
tado ilustrado en la Figura A.5.5.
{0, 3, 2}
c
{3, 7, 7}
3 0 7 3 7 2
Ejercicio 8
A metodo1 se le pasa un entero y, por tanto, el paso es por valor. Tras las manipulaciones en el mtodo
la variable original queda inalterada. A metodo2 se le pasa una matriz y el paso, por tanto, es por refe-
rencia. Por ello, al acabar el mtodo los cambios realizados se mantienen. Lo mismo ocurre en meto-
do3, con la diferencia de que ahora se devuelve la matriz lista (en realidad es la referencia al objeto
de clase matriz). Esta referencia devuelta se asigna en el principal a la referencia llamada Valores1 con
lo que sta a partir de este momento refiere a la matriz llamada lista dentro de metodo3. El resultado
294 Introduccin a la programacin con orientacin a objetos
45 34 35
Ejercicio 9
ANLISIS
Vamos a considerar el caso de matrices cuadradas. Excluyendo la diagonal, la matriz queda dividida
en un tringulo superior y uno inferior. La transposicin se realiza intercambiando los elementos sim-
tricos con respecto a la diagonal.
DISEO
Para construir la transpuesta slo hay que considerar un tringulo de la matriz. Para cada elemento de
ese tringulo se intercambia su valor con el elemento de la matriz que tiene intercambiados los ndi-
ces. El algoritmo en pseudocdigo sera,
Inicio
11Leer matriz a de tamao n
11Para ii0 mientras i<n incremento iii+1
1111Para j i0 mientras j < i incremento jij+1
111111aux i a (i, j)
111111a(i, j) i a (j, i)
111111a(j, i) i aux
1111Fin_Para
11Fin_Para
Fin
IMPLEMENTACIN
Se incluye el mtodo transponer dentro de un programa que ilustra el uso de dicho mtodo.
class Transpon {
11public static void main(String [] args) {
1111int [][] a = {{0,1,2},{3,4,5},{6,7,8}};
1111transponer(a);
1111System.out.println();
1111}
11}
El mtodo tiene tipo de retorno void (no devuelve nada) porque la matriz al pasarse por referen-
cia mantiene los cambios al finalizar el mtodo de transposicin. Si queremos conservar la matriz ori-
ginal habra que usar otra matriz b para transponer.
Ejercicio 10
ANLISIS
Sabemos que el rea debajo de una curva es la integral entre dos puntos. Una forma de calcular num-
ricamente la integral es mediante la regla del trapecio.
La regla del trapecio se ilustra de la forma siguiente. Sea una funcin y 5 f (x) entre los valores de
x 5 a y x 5 b. Si unimos los puntos inicial (a) y final (b) de la curva con una recta, obtenemos un tra-
pecio, vase la figura A.5.6.
y f (b)
rea = (b a)
f (b) f (a)/2
y = f (x) f (a)
rea = (b a) f (a)
a b x a b
Figura A.5.6. Construccin del trapecio Figura A.5.7. Clculo del rea de un trapecio
296 Introduccin a la programacin con orientacin a objetos
La idea es aproximar la integral por el rea del trapecio que se obtiene, como indica la Figura A.5.7.
f(a) 1 f(b
Total 5 (b 2 a) }
2
Si usramos un solo trapecio el error sera enorme, por lo que utilizaremos muchos trapecios
pequeos. Cuanto ms pequeos sean menos errores cometeremos y ms se parecerla lnea curva a
la recta del trapecio. En el lmite coincidirn. Por otro lado, debemos tener en cuenta que cuanto mayor
sea el nmero de trapecios mayor sertambin el error de redondeo acumulado.
Veamos cmo se evala el rea total si tengo n trapecios con la misma base,
h 5 (b 2 a)/n.
a b x
f(a) 1 f(b) n 2 1
Integral 5 h 3 }}
2
1 6 f(a 1 i h)
i 5 1
4
En nuestro caso, f 5 sen (u), a = 0 y b = radianes.
DISEO
La expresin anterior se puede implementar en pseudocdigo. Para ello, supongamos que disponemos
de un mtodo que nos devuelve el valor de la funcin que estemos usando.
Inicio
11Leer n, a y b
11hi(b-a)/n
11integrali integral*h
11Devolver integral
Fin
IMPLEMENTACIN
Con el diseo previo, podemos escribir un mtodo en Java que aplique de forma genrica la regla del
trapecio. Lo nico que necesitamos es un mtodo adicional, funcion, que evale la funcin que que-
remos integrar. De esta forma cuando queramos integrar otra funcin basta con actualizar el mtodo
funcion. El cdigo sera,
// Mtodo que calcula una integral por medio de la regla
// del trapecio
1111return integral;
}1//Fin mtodo trapecio
b) Montecarlo
ANLISIS
Vamos a ilustrar el mtodo con un caso en una dimensin. La integral es el rea debajo de la curva defini-
da por la funcin a integrar y el eje x, vase la Figura A.5.9. Si distribuimos aleatoriamente Nt puntos sobre
el rectngulo definido por los puntos a, b, f 0 y f1, la densidad de puntos serla misma en todas partes ya
298 Introduccin a la programacin con orientacin a objetos
que la distribucin es uniforme (a ms puntos ms rea y a menos puntos menos rea). Por esta razn, el
nmero de puntos debajo de la curva, Na, es proporcional al rea de la misma y, por tanto, a la integral,
f1
y = f (x)
f0
a b x
Na ~ E a
b
f(x) dx
Nt ~ (b 2 a) ( f1 2 f0)
y como la constante de proporcionalidad en ambos casos tiene que ser la misma, se cumple el cocien-
te:
Na
}} 5 E b
f(x) d x
}
Nt [(b 2a a) ( f 1 2f
Despejando,
E a
b
N
f(x) dx 5 [(b 2 a) ( f1 2 f0)] } }a
Nt
DISEO
Como conocemos la funcin y de dnde a dnde queremos integrar nos basta con generar puntos ale-
atoriamente y ver cules estn por encima o por debajo de la funcin. Lo que haremos sergenerar
puntos sobre el intervalo de x entre a y b y sobre el intervalo de y entre f0 a f1. Para el punto genera-
do aleatoriamente (x, y) calculamos el valor de la funcin, z 5 f(x) y vemos si z . y. Si es as lo con-
tamos como un punto de Nt (todos los puntos generados contribuyen a este resultado) pero no como
un punto de Na. Esto se hace para un cierto nmero de puntos y se calcula la integral. Si el nmero
total de puntos se da como dato Nt ya es conocido. Con todo esto podemos disear un algoritmo cuyo
pseudocdigo sera,
Inicio
11Leer Nt, a, b, f0 y f1
11Na i 0
Soluciones a los ejercicios propuestos 299
11hx i b-a
11hy i f1-f0
11Para i i 1 mientras (i Nt) incremento i i i+1
1111x i a+hx*random
1111y i f0+hy*random
1111z i f(x)
1111Si (y z) entonces
111111Na i Na +1
1111Fin_Si
11Fin_Para
11integral i Na*hx*hy / Nt
11Devolver integral
Fin
En el algoritmo anterior se usa un mtodo random que devuelva un nmero aleatorio en el inter-
valo [0, 1).
IMPLEMENTACIN
Un mtodo que implemente el algoritmo anterior podra ser el siguiente,
Al mtodo hay que pasarle los valores f0 y f1 que pueden ser el valor mnimo y el mximo de la
funcin. Con esta eleccin ningn punto generado aleatoriamente entre las valores de abscisa a y b
podrgenerar un valor de la funcin fuera del intervalo f0, f1. Para ser general el mtodo usa otro
mtodo llamado funcion que devuelve el valor de la funcin deseada.
El mtodo de MonteCarlo es til cuando se trabaja en muchas dimensiones con funciones muy
difciles de manejar. El mtodo con algunas variantes se usa, por ejemplo, para simular el comporta-
miento de fluidos. El problema es que la precisin del resultado aumenta como -Nt. Por eso, si quere-
300 Introduccin a la programacin con orientacin a objetos
mos diez veces ms precisin (un dgito ms) hay que aumentar 100 veces el nmero de puntos.
PROGRAMA
Nuestro programa constarde 3 mtodos: main que llamara los otros dos mtodos: montecarlo y
trapecio que a su vez invocar n al mtodo auxiliar funcion que devuelve los valores de sen (x) obteni-
dos con el mtodo sin() de la clase Math. El diagrama de estructura sera el recogido en la Figura A.5.10.
Principal
trapecios MonteCarlo
funcin
class Integral {
11public static void main(String [] args) {
1111int n;
1111double i_trapecio;
1111double i_Montecarlo;
1111n=Integer.parseInt(args[0]); // numero de puntos
1111i_trapecio=trapecio(n, 0, Math.PI);
1111i_Montecarlo=montecarlo(n, 0, Math.PI, 0, 1);
1111System.out.println(La integral calculada por la regla del
111111111111111111111111+ trapecio es: +i_trapecio);
1111System.out.println(La integral calculada por el metodo de
111111111111111111111111+Montecarlo es: +i_Montecarlo);
} //Fin mtodo main
1111}
1111integral=integral*h;
1111return integral;
11}111//Fin mtodo trapecio
1111Na=0;
1111h_x=b-a;111111111111// Intervalo entre a y b
1111h_y=f1-f0;1111111111// Intervalo entre f0 y f1
1111return integral;
11}1111//Fin mtodo Monte Carlo
// Mtodo que implementa la funcin que se integra numricamente
11public static double funcion(double teta) {
1111return Math.sin(teta);
11}
} //Fin de la clase Integral
El lector puede comprobar cmo el resultado obtenido va variando con el nmero de puntos que
se introducen y, cmo al aumentar el nmero de puntos, se va alcanzando al resultado analtico de la
integral que es 2.0.
Ejercicio 12
ANLISIS Y DISEO
En este ejemplo tan sencillo tanto el anlisis como el diseo estn prcticamente implcitos en el enun-
ciado. Como labor de diseo nicamente particularizaremos el algoritmo de Euclides en pseudo-
cdigo,
Inicio
11Leer n, m
11resto i0
11sigue i verdadero
302 Introduccin a la programacin con orientacin a objetos
11Mientras (sigue)
1111resto i resto de m/n
1111Si (resto 0) entonces
111111m i n
111111n i resto
1111Si_no
111111resto i n
111111sigue i falso
1111Fin_Si
11Fin_Mientras
11Devolver resto
Fin
Obsrvese que en el peor de los casos el mximo comn divisor es 1. Este algoritmo lo imple-
mentaremos en un mtodo.
IMPLEMENTACIN
import java.io.*;
class Divisor {
11public static void main(String [] args) throws IOException {
1111int m, n, mcd;
1111while (sigue) {
Soluciones a los ejercicios propuestos 303
1111return resto;
} // Fin clase
CAPTULO 6. RECURSIVIDAD
Ejercicio 1
a) Con n 5 5 no alcanza el caso base, recursividad infinita
b) Con n 5 6, devuelve 10
Ejercicio 2
Obsrvese que cuando n alcance el valor 0 tenemos el caso base, y que con n $ 1 estamos en la parte
inductiva. En sta, primero se hace la llamada recursiva y luego aparece la impresin. Por ello, no se
escribe la R hasta que se alcanza el caso base y se va devolviendo el control de las llamadas recur-
sivas. Representando la pila de llamadas tendramos,
N53 R
e
N52 R
e
N51 R
e
N50 B
B
R
R
R
Ejercicio 3
ANLISIS
El problema es claro, tenemos que implementar el clculo de la funcin de Ackermann recursivamen-
te. No hay ningn requisito adicional.
DISEO
304 Introduccin a la programacin con orientacin a objetos
Inicio
Leer m y n
valor i 0
Si (m=0) entonces
valor i n+1
Si_no
Si (n = 0) entonces
Llamar al mtodo con (n-1) y 1
Si_no
Llamar al mtodo con (m-1) y el resultado de
llamar al mtodo con m y (n-1)
Fin_Si
Fin_Si
Fin
IMPLEMENTACIN
Un programa con un mtodo que implementa el algoritmo anterior y lo aplica al caso m=1, n=1 podra
ser el siguiente,
class Ackermann {
public static void main(String [] args) {
int m=1, n=1;
System.out.println(\nEl valor de la funcion para m= +m
+ y n= +n + es: +Acker(m,n));
}
public static int Acker(int m, int n) {
int valor=0;
if (m==0){
valor=n+1;
}
else {
if (n==0){
valor= Acker(m-1,1);
}
else {
valor=Acker(m-1,Acker(m,n-1));
}
}
return valor;
}
}
Con m 5 1, n 5 1 y siguiendo el cdigo del algoritmo habra tres llamadas a Acker desde que se
recibe el valor inicial de m y n. stas seran, en primer lugar una llamada que incluye otra llamada,
Acker(0,Acker(1,0)), y en segundo lugar otra llamada con Acker(0,1)). El resultado final de la
funcin sera 3.
Ejercicio 4
Soluciones a los ejercicios propuestos 305
ANLISIS
Aparte de la funcionalidad del mtodo no hay ningn requisito especial, por lo que abordaremos direc-
tamente el diseo.
DISEO
Debemos realizar un sumatorio desde el primer elemento hasta el nmero n. Teniendo en cuenta el 0-
origen de las matrices en Java los ndices deberan correr desde 0 hasta (n 2 1). Si no se pueden usar
bucles, se utilizaruna solucin recursiva como la del siguiente algoritmo,
Inicio
valor i 0
Leer matriz a y valor n
Si ((n-1) = 0) entonces
valor i a (0)
Si_no
valor i a (n-1) + resultado del propio mtodo con matriz a y (n-1)
Fin_Si
Devolver valor
Fin
IMPLEMENTACIN
El mtodo correspondiente al pseudocdigo anterior sera el siguiente,
double suma(double [] a, int n) {
double valor=0;
if (n-1==0){
valor = a[0];
}
else {
valor = a[n-1]+suma(a, n-1);
}
}
Ejercicio 5
ANLISIS
El problema de las torres de Hanoi es un problema clsico en recursividad. La complejidad del mis-
mo puede observarse resolvindolo para un caso sencillo, como el de tres discos. El lector puede rea-
lizar el ejercicio usando tres monedas de distintos tamaos. La secuencia de transferencias de discos
queda ilustrada en la Figura A.6.1 donde se transfieren tres discos de la varilla de la izquierda a la vari-
lla central (la derecha acta como auxiliar). Obsrvese que incluso en este caso tan sencillo la solu-
cin no es inmediata. La complejidad del problema crece exponencialmente con el nmero de discos
a transferir.
306 Introduccin a la programacin con orientacin a objetos
Figura A.6.1. Resolucin del problema de las torres de Hanoi con tres discos
DISEO
Obtener un algoritmo que resuelva en el caso general el problema de las torres de Hanoi parece una
tarea Homrica. Sin embargo, vamos a ver cmo el teorema de induccin nos permite una solucin
recursiva muy elegante. Para ello, fijmonos en que si queremos mover n discos (tres en el ejemplo)
de la primera a la segunda varilla lo que hacemos es montar los n 2 1 (2 en este caso) discos menores
en la varilla auxiliar (la tercera en este caso). El problema concluye al mover el disco sobrante (el ms
grande) a la varilla final y mover los n 2 1 discos de la varilla auxiliar a la final. Si tuviramos cuatro
discos tendramos que montar primero los tres anteriores en la auxiliar, etc. Esta solucin es clara-
mente recursiva. Para resolver el problema con n discos tenemos que resolverlo con n 2 1. Para resol-
verlo con n 2 1 discos necesitamos resolverlo con n 2 2 y as hasta que no quede ningn disco.
Fijmonos en que si dados n discos resolvemos antes para n 2 1 no incumplimos los requisitos del jue-
go, porque el disco que queda suelto es siempre mayor que los otros n 2 1. Por eso, cuando lo mova-
mos a su varilla final quedarsiempre debajo de cualquier otro disco que coloquemos posteriormente.
En cierto sentido es como decir que ese disco ha desaparecido. Cuando no queden discos estamos
en el caso base y el problema estresuelto.
La solucin general, por tanto, consiste en mover los n 2 1 discos superiores a la varilla auxiliar.
Luego mover el disco restante a la varilla final y ahora mover los n 2 1 discos desde la varilla auxiliar
a la final. Fijmonos que la ltima accin representa una versin simplificada del caso general.
Tras esta discusin organicemos el algoritmo. Etiquetemos los varillas como 1, 2 y 3. El objetivo
es pasar n discos de 1 a 2, con 3 como varilla auxiliar. Los pasos seran:
Un truco cmodo para saber cul es la varilla auxiliar es el siguiente. Debemos conocer cules son
las dos varillas (origen y destino) que queremos usar. Si etiquetamos las tres varillas como 1, 2 y 3
tenemos que 1 1 2 1 3 5 6. Si llamanos i, j a las varillas a usar tendremos que 6 2 i 2 j nos da el nme-
ro de la tercera varilla (la auxiliar) independientemente del valor de i y j.
a) Caso base n 5 0
b) Caso inductivo
Para mover n discos de la varilla i a la j movamos (de alguna forma que no viole las reglas del jue-
go) los (n 2 1) discos superiores a la varilla auxiliar (6 2 i 2 j). Despus, llevemos el disco que queda
en la varilla i a la varilla j. Finalmente, movamos (de alguna forma que no viole las reglas del juego)
los (n 2 1) discos de la varilla auxiliar a la varilla j.
Obsrvese que slo necesitamos especificar la estrategia de actuacin y no el conjunto completo
de movimientos disco a disco. El pseudocdigo para el algoritmo recursivo sera,
Inicio
Leer n, i (origen), j(destino)
Si (n>0) entonces
Llamar al propio mtodo con n-1, i, 6-i-j
Escribir de i a j
Llamar al propio mtodo con n-1, 6-i-j, j
Soluciones a los ejercicios propuestos 307
Fin_Si
Fin
IMPLEMENTACIN
El algoritmo anterior se implementa con facilidad. El siguiente es un programa que soluciona el pro-
blema de las torres de Hanoi con un mtodo recursivo,
/******************************************************************
Programa para resolver el problema de las torres de Hanoi
El programa lee por la lnea de rdenes el nmero de discos y
el nmero (1, 2, 3) de las varillas inicial y final
*****************************************************************/
class Torres_de_Hanoi {
public static void main(String [] args) {
int n, i, j;
n=Integer.parseInt(args[0]);
i=Integer.parseInt(args[1]);
j=Integer.parseInt(args[2]);
hanoi(n, i, j);
} // Fin metodo main
} // Fin clase
Con datos iniciales de n=3, i=1 y j=2 (el ejemplo de los tres discos) el resultado del programa es:
1>2
1>3
2>3
1>2
3>1
3>2
1>2
Ejercicio 6
ANLISIS
La funcionalidad del cdigo estexplcitamente indicada en el enunciado. Se trata de evaluar x n recur-
sivamente. Veamos cmo.
308 Introduccin a la programacin con orientacin a objetos
DISEO
La potencia n de un nmero x se puede definir recursivamente como:
potencia(x, n) 5 x potencia( x, n 2 1)
El caso base sera que n tomase valor 0. Como sabemos un nmero elevado a cero es 1.
Inicio
Leer x y n
valor i1
Si (n>0) entonces
valor ivalor * resultado del propio mtodo con x y (n-1)
Fin_Si
Devolver valor
Fin
IMPLEMENTACIN
class Potencia{
public static void main(String [ ] args) {
int x, n;
x=Integer.parseInt(args[0]);
n=Integer.parseInt(args[1]);
System.out.println(La potencia +n+ de
+ x+ es: +calcular_ potencia(x,n));
} // Fin del main
Como puede observarse el programa lee los valores de x y n por lnea de rdenes.
Ejercicio 7
Al mtodo suma le falta la palabra clave return en la parte que realiza la llamada recursiva. Sin
return el programa devolvera algn valor slo en el caso de numero=0. El programa corregido
sera:
Esta versin presenta una caracterstica no recomendable de acuerdo a los principios de la progra-
macin estructurada. Se trata de la existencia de dos puntos de retorno desde el mtodo (hay dos
return). Una solucin ms elegante y acorde adems a las reglas de estilo del Apndice D, sera la
siguiente,
Ejercicio 8
ANLISIS
Una vez ms el problema es tan sencillo que no hay ms que considerar una sola tarea.
DISEO
La solucin consiste en recorrer la matriz cambiando el ndice de uno en uno. Un algoritmo posible
sera el siguiente,
Inicio
Leer matriz y tamao (n)
ndice i 0
Si (n > 0) entonces
Llamar al propio mtodo con matriz a y (n-1)
Escribir a (n-1)
Fin_Si
Fin
Obsrvese que la impresin estcolocada despus de la llamada recursiva. Como el caso base
resulta ser n 5 0 los elementos empezarn a escribirse desde el ndice 0 (que es cuando se empiezan a
devolver desde el caso base las llamadas recursivas) hasta el ndice (n 2 1).
IMPLEMENTACIN
A continuacin, se muestra para un caso particular, un programa que aplica, a travs de un mtodo, el
algoritmo anterior.
class ImprimeMatriz{
public static void main(String [ ] args) {
int a[]={1,2,3,4,5};
imprime(a, a.length);// Se pasa la matriz y su longitud
System.out.print(a[n-1]);
}
} //Fin del mtodo imprime
}
12345
Ejercicio 9
Obsrvese que el mtodo A llama al mtodo B que a su vez invoca al mtodo A. Es un caso de recur-
sividad indirecta. Siguiendo la traza del programa observamos que el resultado es:
A antes
B antes
A antes
B antes
B despues
A despues
B despues
A despues
Ejercicio 10
Directamente podemos abordar el diseo. Teniendo en cuenta el algoritmo tal y como se describe en
el Ejercicio 12 del Captulo 5 tenemos,
a) Caso Base
m
Cuando resto de } } = 0
n
b) Caso inductivo
Si resto 0 se invoca el algoritmo con m 5 n y n 5 resto
Inicio
Leer n y m
valor i0
resto iparte entera de m/n
Si (resto = 0)
valor in
Si_no
valor iresultado del propio algoritmo con resto y n
Fin_Si
Devolver valor
Fin
Soluciones a los ejercicios propuestos 311
IMPLEMENTACIN
Una variante del programa del Ejercicio 12 del Captulo 5 con un mtodo que implementa la versin
recursiva del algoritmo es la siguiente,
import java.io.*;
class Divisor {
public static void main(String [] args) throws IOException {
int m, n, mcd;
}
312 Introduccin a la programacin con orientacin a objetos
if (resto==0) {
valor=n;
}
else {
valor = euclides(resto, n);
}
return valor;
Obsrvese cmo la ordenacin de n y m se ha colocado en el mtodo main para evitar que se rea-
lice ms de una vez.
Ejercicio 1
De acuerdo al diagrama de clases debemos incluir un atributo para la identificacin del producto (ID)
que podemos considerar como un nmero de identificacin e implementarlo como un entero. El atri-
buto coste que aparece en el diagrama se puede implementar con una variable real. Parece lgico
incluir un nuevo atributo, nombre, que describa la naturaleza del producto y que podramos imple-
mentar con una cadena. Desde el punto de vista de los mtodos, podemos disear un constructor que
inicialice los atributos anteriores. De acuerdo a los requisitos, tambin debemos incluir mtodos de
consulta para los tres atributos. Tambin parece lgico incluir un mtodo para la actualizacin del cos-
te del producto. Los atributos los declararemos como privados y los mtodos propuestos, que repre-
sentan la interfaz pblica de la clase, como pblicos. En resumen, en un diagrama de clase tendramos
el resultado mostrado en la Figura A.7.1.
Producto
- ID:int
- coste:double
- nombre:String
+ Producto (nombre:String,
coste:double, ID:int)
+ nombre ( ):String
+ ID ( ):int
+ coste ( ):double
+ actualizar_coste (double coste):void
IMPLEMENTACIN
El diagrama anterior se puede implementar como se indica a continuacin,
class Producto {
private int ID;
private double coste;
private String nombre;
public Producto(String nombre, double coste, int ID) {
this.nombre=nombre;
this.coste=coste;
this.ID=ID;
}
public String nombre() {
return nombre;
}
public int ID() {
return ID;
}
public double coste() {
return coste;
}
public void actualizar_coste(double coste) {
this.coste=coste;
}
}
En el constructor se ha usado la palabra reservada this que identifica al propio objeto que esta-
mos manejando. El identificador this se refiere siempre al objeto con el que estamos trabajando, hace
referencia al ejemplar y no a la clase. Este modificador se usa para deshacer la ambigedad entre un
miembro de la clase y otro elemento. Para entenderlo tengamos en cuenta que el nombre completo de
un miembro de la clase (dato o mtodo) es:
this.miembro
Por ejemplo, cuando tenemos una variable local a un mtodo con el mismo nombre que una varia-
ble de ejemplar podemos usar this para referirnos a la variable de ejemplar dentro del mtodo.
Ejercicio 2
Realizando una traduccin a UML de la descripcin del sistema obtenemos el resultado mostrado en
la Figura A.7.2.
1 * 1 *
Empresa Equipo_desarrollo Programador
Programador_Contratado Programador_Fijo
Director
314 Introduccin a la programacin con orientacin a objetos
Ejercicio 3
Una vez ms, el planteamiento es traducir la descripcin del sistema a UML. El resultado se ilus-
tra en la Figura A.7.3.
1 1..* 1
Universidad Departamento
1..*
1..* 1..*
Estudiante Curso Profesor
Estudiante Estudiante
curso de Profesor Profesor
completo verano titular asociado
La relacin entre Estudiante y Curso no es simple, pues un estudiante sigue varios cursos
y un curso consta de varios estudiantes. Aqu se ha considerado una relacin de asociacin.
Problemas similares surgen entre Profesor, Estudiante y Curso. En este caso se ha representa-
do como una relacin de dependencia entre Curso y Profesor y de asociacin entre Curso y
Estudiante. Este tipo de dudas deben resolverse en la etapa de anlisis recabando ms infor-
macin sobre el sistema.
Ejercicio 4
ANLISIS Y DISEO
Como habitualmente, en estos ejemplos sencillos el enunciado sirve como indicacin de requisitos. En
el dominio del problema definido por los mismos slo van a ser necesarias dos clases, una clase Lnea
y una clase Punto. Entre ambas existe una relacin de dependencia. El diagrama de clases sera el
recogido en la Figura A.7.4.
Abordemos cmo conseguir que el comportamiento de las clases sea el deseado. Veamos cmo
obtener la pendiente y la ordenada en el origen. La ecuacin de la recta es: y 5 a 1 bx. Con dos pun-
Soluciones a los ejercicios propuestos 315
tos tendramos:
Punto Linea
x:double b:double
y:double a:double
y1 5 a 1 bx1 , y2 5 a 1 bx2
a 5 y1 2 b x1
CLASES Y RELACIONES
Tal y como estplanteado tenemos una relacin de dependencia. Podemos declarar una clase Lnea
que usa una clase Punto. Para poder cumplir los requisitos podemos usar dos constructores (sobre-
carga) en la clase Lnea.
IMPLEMENTACIN
class Punto {
private double x;
private double y;
class Linea {
private double a;
private double b;
Fijmonos que la relacin de uso se traduce en que los objetos de la clase dependiente (Lnea)
aceptan como dato en algn mtodo objetos de la clase independiente (Punto).
Como ejemplo de uso de la clase Lnea tenemos el siguiente programa principal que define dos
puntos, determina la recta que pasa por ellos y calcula la y para una x dada.
class Ejercicio {
public static void main(String [] args) {
Punto p1 =new Punto(1.0, 1.0);
Punto p2 =new Punto(2.0, 3.0);
if (p1.igual(p2)) {
System.out.println(Los dos puntos son iguales);
}
else {
Linea recta = new Linea(p1, p2);
System.out.println(Ordenada en el origen: +recta.a());
System.out.println(Pendiente: +recta.b());
System.out.println(y para x=5 : +recta.y(5.0));
}// Fin del if-else
}// Fin del main
}// Fin de la clase Ejercicio
El resultado sera,
Ejercicio 5
ANLISIS Y DISEO
En el dominio del problema podemos identificar dos clases relacionadas, una clase Nombre y una cla-
se Persona. La relacin entre ellas sera de asociacin. El correspondiente diagrama de clase, inclu-
yendo atributos y procedimientos, sera de la forma ilustrada en la Figura A.7.5.
Nombre Persona
nombre:String 1 1 nombre_persona:Nombre
apellido_1:String dni:int
apellido_2:String
Persona (nombre,int)
Nombre (String,String,String) imprime_datos ( ):void
nombre ( ):String nombre ( ):Nombre
apellido_1:String dni ( ):int
apellido_2:String
imprime_nombre ( ):void
Es costumbre poner a un mtodo de consulta el mismo nombre que a la variable que se devuelve.
Otra opcin es poner un devuelve_Nombre_de_la_variable(). En ingls se suele usar getNa-
me().
IMPLEMENTACIN
class Nombre {
private String nombre;
private String apellido_1;
private String apellido_2;
public Nombre(String nombre, String apellido_1,
String apellido_2) {
this.nombre=nombre;
this.apellido_1=apellido_1;
this.apellido_2=apellido_2;
}
public String nombre() {
return nombre;
}
public String apellido_1() {
return apellido_1;
}
public String apellido_2() {
return apellido_2;
}
}
} // Fin clase Nombre
class Persona {
private Nombre nombre_ persona;
private int dni;
Fijmonos en que el mtodo nombre devuelve un objeto de clase Nombre. Esto se puede hacer sin
problemas.
Veamos ahora un ejemplo de programa principal que usa algunos de los mtodos anteriormente
definidos y que sirve para exponer cmo crear matrices de objetos.
class Ejercicio {
public static void main(String [] args) {
System.out.println();
System.out.println(Personas en la lista \n);
System.out.println();
for (int i=0; i<=2; i++){
individuos [i].imprime_datos();
}
Soluciones a los ejercicios propuestos 319
En este ejemplo se ha usado el constructor de dos formas distintas, creando un objeto auxiliar, aux, o
bien creando el objeto directamente como parmetro actual (al igual que con la clase BufferedReader).
El resultado del programa sera,
Personas en la lista
Ejercicio 6
ANLISIS Y DISEO
Para cumplir los nuevos requisitos debemos incluir dos atributos nuevos en la clase Persona que sean
dos referencias (no dos objetos) de la propia clase Persona. Vamos a llamarlos madre y padre.
IMPLEMENTACIN
El nuevo cdigo para la clase Persona sera:
class Persona {
private Nombre nombre_ persona;
private int dni;
private Persona madre=null;
private Persona padre=null;
En los mtodos madre y padre hemos usado el identificador this. Esto nos permite usar el
mismo nombre para el parmetro formal que para el atributo madre. Al indicar this.madre est
claro que nos referimos al atributo, si no lo pusiramos tendramos madre=madre lo que es total-
mente ambiguo.
La clase Nombre sera la misma que en el problema anterior.
Un posible programa principal que ilustra el uso de las nuevas capacidades es el siguiente:
class Ejemplo {
public static void main(String [] args) {
Persona [] individuos = new Persona [3];
Nombre aux;
individuos[2].madre(individuos[1]);
individuos[2].padre(individuos[0]);
System.out.println();
System.out.println(Personas en la lista \n);
System.out.println();
for (int i=0; i<=2; i++){
individuos [i].imprime_datos();
if (individuos[i].da_madre() !=null) {
System.out.println(La madre es:);
individuos[i].da_madre().imprime_datos();
System.out.println(El padre es:);
individuos[i].da_ padre().imprime_datos();
}// Fin del if
Soluciones a los ejercicios propuestos 321
Resultado:
Personas en la lista
La madre es:
Marta Salvador Rodriguez
DNI: 10567953
El padre es:
Juan Guijarro Sobrino
DNI: 67453219
Ejercicio 7
El resultado es 3.2.
Si se quisiera hacer referencia a la variable de clase para que el resultado de imprimir fuera 9.8
se tendra que usar la clusula this.
Ejercicio 8
ANLISIS Y DISEO
Un polinomio queda definido como,
n
u 5 6 ai x i
i 5 0
donde los coeficientes ai son nmeros reales. Nuestro programa contendruna clase Polinomio que
represente el polinomio a manejar y permita realizar la operacin deseada. La inicializacin del poli-
nomio se realizara travs del mtodo constructor. El diagrama para la clase Polinomio podra
ser el mostrado en la Figura A.7.6
322 Introduccin a la programacin con orientacin a objetos
Polinomio
- n:int
- coeficientes:double [ ]
+ Polinomio (coeficientes:double [ ])
+ y (x:double):double
IMPLEMENTACIN
El cdigo para la clase Polinomio podra ser el siguiente,
class Polinomio{
private int n;
private double [] coeficientes;
import java.io.*;
class Ejercicio {
public static void main(String [] args)throws IOException{
int n;
double [] coeficientes=null;
double x, y;
Polinomio poli;
boolean sigue=true;
coeficientes[i]=Double.parseDouble(leer.readLine());
}
System.out.println(\nCalculando ordenadas);
while (sigue){
System.out.println(Introduzca abscisa: );
x=Double.parseDouble(leer.readLine());
y=poli.y(x);
System.out.println(y= +y);
System.out.println(\nDesea continuar? Si(1)/No(Otro valor));
sigue=(1==Integer.parseInt(leer.readLine()));
}
} // Fin mtodo main
} // Fin clase Ejercicio
Obsrvese que la asignacin del valor de la variable lgica sigue dentro del bucle while se reali-
za directamente como el resultado de la comparacin de la lectura de la opcin de continuacin con el
valor 1.
CAPTULO 8. HERENCIA
Ejercicio 1
El resultado es,
4455
Fijmonos en que lo que se hereda es el valor inicial de prop1 y prop2, pero no su valor tras haber
sido modificado en el objeto obj1. En otras palabras, se heredan las definiciones de la clase pero no
las modificaciones en los ejemplares de esa clase.
Ejercicio 2
El resultado del programa es,
Obsrvese cmo en el mtodo frase de la clase Dos la variable i es local, pues estdeclarada den-
tro del mtodo frase. Por lo tanto i vale 3 aunque la variable i heredada de uno no se sobrescribe y
sigue existiendo implcitamente con el valor 2. En el caso de la clase Tres no se declara ninguna
variable i local. Por lo tanto, el identificador i corresponde a la variable de ejemplar heredada de la
clase Uno. Por esta razn i es igual a 2. La variable i de la clase Dos no se hereda, puesto que es una
variable local a un mtodo.
Ejercicio 3
Valor de las variables pasadas: 5.0,4
Valor de la variable: 4,5.0
La clase Dos tendra el mtodo imprime que heredara de la clase Uno y ese mtodo ya heredado
se sobrecarga en la clase Dos. La clase Dos tendra entonces 2 mtodos que difieren en el orden de
los parmetros: imprime(j,x) e imprime(x,j).
Ejercicio 4
Valor de la variable pasada: 5.0
Valor de la variable: 4
La clase Dos tendra el mtodo imprime que heredara de la clase Uno y ese mtodo ya here-
dado se sobrecarga en la clase Dos. La clase Dos tendra entonces 2 mtodos imprime (j) e
imprime (x).
Ejercicio 5
ANLISIS Y DISEO
El problema se puede organizar con tres clases, Empleado, Encargado y Trabajador. De stas, Emple-
ado sera la clase padre de una jerarqua de clases. El correspondiente diagrama se recoge en la Figura
A.8.1.
Soluciones a los ejercicios propuestos 325
Empleado
Trabajador
Encargado a comisin
La clase Empleado seruna clase abstracta que contenga como atributos el nombre y el apellido
de cada empleado. Como mtodos tendrel constructor, dos mtodos que devuelvan el valor de los
atributos y un mtodo llamado calcularSalario que serabstracto, puesto que el salario serdis-
tinto dependiendo de la categora del empleado. Los miembros de cada clase se recogen en los dia-
gramas mostrados en la Figura A.8.2.
IMPLEMENTACIN
// Superclase
abstract class Empleado{
private String nombre;
private String apellido1;
private String apellido2;
// Constructor
//Devuelve el nombre
public String nombre(){
return nombre;
}
//Devuelve el apellido1
//Mtodo abstracto
//Constructor
//Mtodo calcularSalario
//Constructor
// Mtodo calcularSalario
public double calcularSalario(){
return SALARIO+COMISION*vendido;
}
public String toString(){
return Trabajador a comision: +nombre()+ +apellido1()
+ +apellido2();
}
// Clase principal
class Empresa{
public static void main(String [ ] args){
Empleado trabajador;
Soluciones a los ejercicios propuestos 327
Encargado encargado1;
TrabajadorComision trabajadorComision1;
encargado1 = new Encargado(Maria, Garcia,Romo);
trabajadorComision1 = new TrabajadorComision(Raul,Paz,
Martin,200);
System.out.print(trabajador.toString());
System.out.println( gana +trabajador.calcularSalario());
}//Fin main
}//Fin clase Empresa
Obsrvese que en la clase padre Empleado se declaran los atributos privados. Esto implica que las
clases descendientes no los heredan. Sin embargo, su manejo implcito a travs de los mtodos here-
dados es perfectamente correcto, como se muestra en el ejercicio. De todas formas se recomienda
declarar como protected los atributos que van a ser heredados, as siempre serposible usarlos
directamente en la clase descendiente.
Ejercicio 6
La definicin de la interfaz es sencilla,
interface Valores{
double SALARIO=1000.0;
double COMISION=0.1;
}
//Constructor
public TrabajadorComision(String nombre, String apellido1,
String apellido2, double vendido){
super(nombre, apellido1, apellido2); //Utiliza el constructor
// del padre
this.vendido=vendido;
}
//mtodo calcularSalario
public double calcularSalario(){
return SALARIO+COMISION*vendido;
}
Ejercicio 7
Con los requisitos especificados en el enunciado la implementacin es directa,
//clase abstracta
abstract class ObjetoGrafico{
int x,y;
ObjetoGrafico(){
x = 0;
y = 0;
}
public void moverObjeto(){
x = x + 10;
y = y + 10;
}
Estas dos clases heredan de la clase padre los atributos y los mtodos. Para poder crear objetos de
clase Rectangulo o Circunferencia es necesario implementar el mtodo abstracto dibujar que
han heredado. Dicho mtodo tendrimplementaciones distintas segn cada clase.
Soluciones a los ejercicios propuestos 329
Ejercicio 8
De acuerdo a los requisitos tendramos las siguientes interfaces y clase,
//Primera interfaz
interface Primera{
void A();
void B();
}
//Segunda interfaz
interface Segunda extends Primera{
void C();
}
class Ejercicio {
public static void main(String [ ] args){
Objetos objeto1= new Objetos();
objeto1.A();
objeto1.B();
objeto1.C();
}
}//Fin clase Ejercicio
CAPTULO 9. FICHEROS
Ejercicio 1
Cada registro son 4 1 8 5 12 bytes. Por lo tanto, los 24 bytes que saltamos con seek(24) representan
24 4 12 5 2 registros. Dicho de otra forma, saltamos dos registros, as que con fichero.readInt()
seguido de fichero.readDouble() se leeran el valor entero 3 y el real 40.9.
Ejercicio 2
Cada registro son 4 1 8 5 12 bytes. Deberamos saltar los tres primeros registros, es decir, los 3 12 5
36 primeros bytes, haciendo nombre_objeto.seek(36).
330 Introduccin a la programacin con orientacin a objetos
Ejercicio 3
import java.io.*;
class Ejercicio {
public static void main(String [] args) throws IOException{
int cuenta,clientes;
String nombre,fichero;
double saldo;
DataOutputStream salida=null; // Referencias
BufferedReader entrada;
try {
fichero=args[0];
entrada= new BufferedReader
(new InputStreamReader(System.in));
salida= new DataOutputStream
(new FileOutputStream(fichero));
System.out.println(Introduzca numero de clientes);
clientes=Integer.parseInt(entrada.readLine());
salida.close();
Ejercicio 4
Teniendo en cuenta que debemos capturar primero las excepciones ms especficas, el orden tendra
Soluciones a los ejercicios propuestos 331
Ejercicio 5
A fin de capturar la IOException en todos los casos y no repetir el catch que la captura vamos a
usar dos try-catch. El externo se encargarde capturar especficamente la IOException y todas
las dems se capturarn en el try-catch interno. El cdigo sera,
import java.io.*;
class Ejercicio {
public static void main(String [] args) {
//Inicializacion
int i=0;
DataInputStream entrada=null;
try {
// Abriendo el fichero y leyendo de l
try {
entrada=new DataInputStream
(new FileInputStream(datos));
do {
i=entrada.readInt();
System.out.println(i);
} while (true);
} //Fin try interno
finally {
if (entrada!=null){
entrada.close();
}
}
} //Fin try externo
catch(IOException io) {
System.out.println(Error en la E/S\n\n+io.toString());
}
Ejercicio 6
ANLISIS Y DISEO
De acuerdo con los requisitos, la nueva clase usarla clase FileOutputStream. Aqu slo necesita-
mos un mtodo constructor que acepte el nombre del fichero, otro para escribir y vamos a incorporar
otro para cerrar el fichero. El diagrama de clase sera el recogido en la Figura A.9.1.
Secuencial
# salida:FileOutputStream
- Secuencial (nombre:String)
+ escribe (entero:int):void
+ cerrarFichero ( ):void
IMPLEMENTACIN
La clase quedara como sigue,
class Secuencial{
protected FileOutputStream salida=null;
salida.write(entero);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}
import java.io.*;
class Primero {
public static void main(String [] args) {
Secuencial fichero=new Secuencial(nuevo);
fichero.escribe(333);
fichero.cerrarFichero();
}
}
Ejercicio 7
ANLISIS Y DISEO
Es un caso similar al anterior pero aqu vamos a usar la clase DataOutputStream. Al existir varios
mtodos de escritura vamos a aprovechar ms las propiedades de encapsulacin y sobrecarga de mto-
dos. Tendremos un mtodo constructor que aceptarel nombre del fichero, varios mtodos de escri-
tura sobrecargados y un mtodo para cerrar el fichero. El diagrama de clase es el recogido en la
Figura A.9.2.
SecuencialDatos
# salida:DataOutputStream
+ SecuencialDatos (nombre:String)
+ escribe (entero:int):void
+ escribe (doble:double):void
+ escribe (numero:float):void
+ escribe (cadena:String):void
+ escribe (caracter:char):void
+ cerrarFichero ( ):void
334 Introduccin a la programacin con orientacin a objetos
IMPLEMENTACIN
El cdigo correspondiente es el siguiente,
class SecuencialDatos{
protected DataOutputStream salida=null;
try{
salida.writeInt(entero);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}
try{
salida.writeDouble(doble);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}
try{
salida.writeFloat(numero);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
Soluciones a los ejercicios propuestos 335
}
}
try{
salida.writeUTF(cadena);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}
try{
salida.writeChar(caracter);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}
import java.io.*;
// Clase para usar ficheros secuenciales con datos
// Slo escritura
class Primero{
public static void main(String [] args) {
SecuencialDatos fichero=new SecuencialDatos(nuevo);
fichero.escribe(333);
fichero.escribe(mi casa);
fichero.escribe(S);
fichero.escribe(3.45);
fichero.cerrarFichero();
}
}
Obsrvese cmo la sobrecarga del mtodo escribe simplifica el uso de la clase.
336 Introduccin a la programacin con orientacin a objetos
Ejercicio 8
ANLISIS Y DISEO
Vamos a usar dos clases, una para el registro y otra para el fichero directo (aleatorio). La intencin es
independizar totalmente el uso del fichero de la naturaleza del registro. La relacin queda recogida en
la Figura A.9.3. La clase registro deberadaptarse a cada caso. En este ejercicio se supone que cada
registro estformado por tres campos que podran corresponder a los datos de un cliente de un
almacn: un nmero de cuenta, un nombre y un saldo. Esta clase contiene un mtodo que devuelve la
longitud del registro. La clase para el fichero de acceso directo trabaja con los registros y acepta la
posicin en el fichero definida en trminos de registros y no de bytes.
Fichero_directo Registro
Figura A.9.3. Diagrama de clases para el ejercicio del fichero de acceso directo
Los atributos de cada clase se muestran en las Figuras A.9.4. y A.9.5.
Registro
# cuenta:int
# nombre:String
# saldo:double
# l_registro:int
Fichero_directo
# fichero:RandomAccessFile
# registro:Registro
# l_registro:int
IMPLEMENTACIN
El cdigo correspondiente a las dos clases anteriores es el siguiente,
import java.io.*;
class Registro {
protected int cuenta;
protected String nombre;
protected double saldo;
protected int l_registro=44;
nombre=fichero.readUTF();
saldo=fichero.readDouble();
}
catch(IOException e) {
System.out.println(Error leyendo registro);
e.toString();
}
return this; // Se devuelve el presente objeto
// de clase registro
} // Fin mtodo lee_registro(versin para fichero directo)
class Fichero_directo {
protected RandomAccessFile fichero;
protected int l_registro=0;
protected Registro registro=new Registro(0, , 0.0);
try{
fichero.seek((nregistro-1)*l_registro);
}
catch(EOFException e) {
System.out.println(Ese registro no existe);
e.toString();
}
catch(IOException e) {
System.out.println(Error de entrada salida);
e.toString();
}
/***************************************************************
Programa que ilustra el uso de un programa de acceso directo
organizado en registros de longitud:
4 bytes(int) + 32 bytes (30 caracteres UTF+2
+8 bytes (double)=44
***************************************************************/
class FicheroAleatorio {
public static void main(String [] args) throws IOException {
int fin, longitud, n, cont=0;
double saldo;
char si_no;
char [] aux =new char[30];
boolean seguir=true;
String nombre_in;
Registro registro;
while(seguir) {
cont++;
System.out.println(Introduzca nombre del cliente);
nombre_in=leer.readLine();
// Bsqueda de un registro
fichero.cerrar_fichero();
Soluciones a los ejercicios propuestos 341
}//Fin de main
}//Fin clase FicheroAleatorio
Ejercicio 1
ANLISIS Y DISEO
Es posible reformular la solucin iterativa habitual del mtodo de ordenacin por seleccin para dar-
le forma recursiva. La idea es eliminar el primero de los bucles, el que va recorriendo los elementos.
Almacenando en la variable fin el nmero de elementos de la lista, el pseudocdigo para el mtodo
sera el siguiente,
Inicio
Leer lista, inicio, fin
Si (inicio fin) entonces
indice_min i inicio
Para i i inicio+1 mientras i< fin incremento i i i+1
Si (lista (i) < lista (indice_min)) entonces
indice_min i i
Fin_Si
Fin_Para
IMPLEMENTACIN
La traduccin a Java del algoritmo anterior se muestra a continuacin,
public static void seleccion(int [] lista, int inicio,
int fin) {
int aux, indice_min;
if (inicio != fin) {
indice_min=inicio;
for (int i=inicio+1; i<fin; i++) {
if (lista[i]<lista[indice_min]) {
indice_min=i;
}
}
aux=lista[inicio];
lista[inicio]=lista[indice_min];
lista[indice_min]=aux;
seleccion(lista, inicio+1, fin);
}
}
Ejercicio 2
342 Introduccin a la programacin con orientacin a objetos
ANLISIS Y DISEO
La bsqueda lineal se puede formular de forma recursiva. Para ello, distingamos en primer lugar los
casos base e inductivo,
a) Casos base
Hemos acabado de recorrer la lista sin encontrar el elemento clave.
Hemos encontrado el elemento clave.
b) Caso inductivo
Si no hemos encontrado el elemento clave en la posicin actual buscamos si esten la posi-
cin siguiente.
Si usamos 21 como valor centinela para indicar que el elemento clave buscado no esten la lista,
y si la variable fin almacena el ndice del ltimo elemento, el pseudocdigo para el mtodo sera,
Inicio
Leer lista, inicio, fin, clave
Si (lista (inicio)=clave) entonces
valor i inicio
Si_no
Si (inicio=fin) entonces
valor i -1
Si_no
valor i Resultado del propio mtodo con lista, inicio+1,
fin, clave
Fin_Si
Fin_Si
Devolver valor
Fin
IMPLEMENTACIN
El cdigo en Java quedara de la forma siguiente,
public static int secuencial(int [] lista, int inicio,
int fin, int clave){
int valor;
if (lista[inicio]==clave) {
valor = inicio;
}
else {
if (fin==inicio) {
valor=-1;
}
else {
valor =secuencial(lista, inicio+1, fin, clave);
}
}
return valor;
Ejercicio 3
Soluciones a los ejercicios propuestos 343
ANLISIS Y DISEO
La bsqueda binaria admite una versin recursiva. Consideremos el problema. Como siempre, dis-
tingamos el caso base (casos bases aqu) del caso inductivo:
a) Casos base
Hemos encontrado la clave en la lista.
No quedan elementos en la lista.
b) Caso inductivo
Se repite la bsqueda binaria sobre la mitad de la lista en la que puede estar la clave.
Inicio
Leer lista, clave, derecha, izquierda
posicion i Parte entera de (derecha+izquierda)/2
IMPLEMENTACIN
Un mtodo en Java que implementa este algoritmo se presenta a continuacin,
int valor;
int posicion = (izquierda + derecha) / 2;
if (izquierda > derecha) { // clave no encontrada
valor= -1;
}
else {
if (clave == lista[posicion]) { // clave encontrada
valor=posicion;
}
else {
if (clave > lista[posicion]) {
valor= binaria_rec(lista, clave, derecha, posicion+1);
}
else {
344 Introduccin a la programacin con orientacin a objetos
return valor;
} // Fin mtodo de bsqueda binaria_rec
Tenemos un caso base con dos posibilidades. As, la recursin se para cuando se cruzan los lmi-
tes izquierda y derecha, lo cual significa que hemos realizado una bsqueda exhaustiva sin encon-
trar el elemento, o cuando se ha encontrado el elemento buscado. Los criterios, como vemos, son los
mismos que para la bsqueda binaria simple.
Si no estamos en el caso base, el mtodo ejecutaruna de las dos llamadas recursivas, dependien-
do de la mitad de la lista en la que busquemos. El valor buscado se compara con el valor del medio y
los lmites de la bsqueda se modifican con los parmetros que se pasan al mtodo. De manera espec-
fica, indice+1 se usa como nuevo valor izquierda si el valor buscado es mayor que el valor del
medio. Si el valor buscado es menor se usa indice-1 como nuevo valor derecha.
B
Prcticas y casos
de estudio propuestos
Sumario
En este apndice se presentan una serie de ejercicios de mayor entidad que los propuestos en los cap-
tulos. Estos ejercicios son susceptibles de ser utilizados como base para una serie de prcticas orien-
tadas a la aplicacin de los conceptos presentados a lo largo de los captulos del texto. Los ejercicios
estn pensados para permitir el trabajo personal, por lo que slo se dan unas sugerencias orientativas
sobre la solucin ms sencilla. Tambin se incluye al final de este apartado una propuesta de casos de
estudio orientados a que el lector se familiarice con las actividades propias del desarrollo de un pro-
yecto software.
De acuerdo a la organizacin del libro, las prcticas y los casos de estudio propuestos se han divi-
dido en dos partes. La primera considera ejercicios relacionados con la programacin estructurada y
modular mientras que la segunda se centra en la programacin orientada a objetos.
PRCTICAS PROPUESTAS
sese un mtodo iterativo para calcular dicho trmino. Sugerencia: Use un bucle que se recorra
hasta el valor del trmino (n) y dos variables que vayan almacenando los valores ltimo y penltimo
de la serie para evaluar el nuevo trmino de la misma.
PRCTICA 7. Construya un programa que calcule los valores de e(x), cos(x) y sen(x) a partir de las
series de Taylor (es una expansin alrededor del punto x=0) siguientes:
n
xi
ex 5 6 } }
i50 i !
n
x2i
cos(x) 5 6 (21)i } }
i50 (2 i)!
n
x2i 1 1
sen(x) 5 6 (21)i }}
i50 (2 i 1 1)!
El nmero de trminos de la serie, n, ser el suficiente para que la diferencia absoluta entre dos
valores sucesivos, para n-1 y n, sea menor de 1023. Use un mtodo para cada caso, imprimiendo los
distintos resultados en el mtodo principal. Sugerencia: Use un bucle para simular el sumatorio.
PRCTICA 8. Disee un programa iterativo que informe si una cadena es un palndromo (una
cadena es un palndromo si se lee igual de izquierda a derecha que de derecha a izquierda). Sugeren-
cia: Use el mtodo charAt(int indice), el cual devuelve el carcter que se encuentra en la posi-
cin dada por indice, para ir comprobando si el carcter inicial y el final de la cadena son iguales.
Use un bucle para irse desplazando hacia el centro de la cadena.
PRCTICA 9. Construya un programa que obtenga la matriz suma de dos matrices, a (con dimen-
siones m y m) y b (con dimensiones p y q). Sugerencia: Use un mtodo para leer las dos matrices, otro
para sumarlas y otro para mostrarlas. Tenga en cuenta que si c es la matriz suma, sus elementos se
obtienen como c(i, j) 5 a(i, j) 1 b(i, j). Use un bucle para cada dimensin.
PRCTICA 10. Reutilizando los mtodos que implement en el anterior ejercicio para leer y mos-
trar matrices implemente un programa que obtenga la matriz producto de dos matrices, a (con dimen-
siones m y n) y b (con dimensiones p y q). Sugerencia: Tenga en cuenta que si c es la matriz producto,
sus elementos se obtienen como,
n
c(i, j) 5 6 a(i, k) b(k, j)
k
Use tres bucles, uno para realizar el sumatorio sobre k y otros dos para recorrer las dimensiones i, j.
PRCTICA 11. Disee un programa que indique si una palabra es un palndromo usando un mto-
do recursivo. Sugerencia: Contraste si el carcter inicial y final de la cadena son iguales y, si lo son,
contine la exploracin eliminando de la cadena el primer y ltimo carcter con ayuda del mtodo
substring() de la clase String.
PRCTICA 12. Construya un programa recursivo que obtenga el trmino n de la serie de Fibo-
nacci. La serie de Fibonacci es una secuencia de enteros, cada uno de los cuales es la suma de los dos
anteriores. Los dos primeros nmeros de la secuencia son 0 y 1. La serie se define como:
346 Introduccin a la programacin con orientacin a objetos
Usando varios valores de n, compare la eficiencia del programa actual con la versin iterativa de
la Prctica 6.
PRCTICA 13. Desarrolle un programa que, por medio de un mtodo recursivo, determine el
nmero de dgitos de un nmero entero positivo o negativo. Por ejemplo, al recibir el nmero 100
debera devolver 3 y si se le pasa el nmero 1 devolvera 1. Sugerencia: Vaya sumando uno mientras
el nmero se vaya dividiendo por diez y de un resultado mayor o igual a 10 en valor absoluto.
PRCTICA 14. Desarrolle un programa que permita representar una serie de alumnos. Para cada alum-
no se debe poder especificar su nombre, nmero de DNI y recoger la calificacin obtenida en los dos
parciales que se hacen en el curso. El programa debe evaluar la nota media de cada estudiante y la nota
media promedio de todos los estudiantes del curso. Sugerencia: Implemente una clase Alumno que
contenga las caractersticas especificas de cada alumno. Adems, codifique una clase Curso donde
una de sus variables de ejemplar sea una matriz de alumnos. En esta clase se debe calcular la nota
media del curso.
PRCTICA 15. Construya un programa que permita realizar algunas operaciones sobre los nme-
ros racionales. El programa aceptar dos nmeros racionales definidos por su numerador y denomina-
dor y devolver su suma, producto y cociente. El programa tambin debe devolver el cociente de los
dos nmeros racionales ledos. Deber darse formato a la salida, imprimiendo tanto los nmeros racio-
nales de entrada como los de salida. Sugerencia: Al crear la clase Racional tenga en cuenta que los
mtodos suma, producto y cociente slo necesitan un parmetro, el otro nmero racional.
PRCTICA 16. Dada una matriz monodimensional de enteros, desarrolle un programa que sume
los elementos de la matriz comprendidos entre dos posiciones que se introducirn por teclado. El
programa debe capturar las excepciones que se puedan producir, tales como introduccin de nme-
ros no enteros como ndices, ndices introducidos fuera del intervalo de la matriz, error en la entra-
da-salida y error que se puede producir si el primer ndice introducido es mayor que el segundo.
Puesto que la ltima excepcin no existe, deber crearla para poder capturarla. Sugerencia: Use una
sentencia try-catch para capturar la excepcin nueva que debe definir y las excepciones del siste-
ma NumberFormatException, ArrayIndexOutOfBoundsException e IOException. Cree
una clase para la nueva excepcin.
PRCTICA 17. Desarrolle la estructura de clases necesaria para un sistema de cobro de peaje de
camiones en una autopista. Los camiones llegan hasta una cabina de peaje donde se determinan el
nmero de ejes y el tonelaje a partir de los datos tcnicos del vehculo. Los camiones deben pagar
5 euros por eje ms 10 euros por tonelada de peso total del camin. En la cabina de peaje se emite un
recibo y una pantalla muestra la cantidad total correspondiente a los recibos de peaje cobrados, as
como el nmero total de camiones que han pasado. Sugerencia: Defina una clase camin y una clase
cabina de peaje.
PRCTICA 18. Desarrolle un programa que permita hacer algunas operaciones sobre crculos y
rectngulos. Estas manipulaciones consisten en determinar la posicin x e y de la figura en un sistema
de coordenadas cartesianas, mover la figura a otra posicin x e y, as como redimensionar la figura.
Prcticas y casos de estudio propuestos 347
Adems, se quiere calcular el rea de cada una de las figuras y, en el caso del rectngulo, intercambiar
altura por anchura. Tambin se pretende que cada figura responda al mensaje toString() devol-
viendo una descripcin de sus atributos. Sugerencia: Cree una clase abstracta de donde hereden la cla-
se Crculo y Rectngulo los atributos y los mtodos que tengan en comn. Tenga en cuenta qu
mtodos deben ser abstractos y cules no.
PRCTICA 19. Declare una clase VehiculoMotorizado que sirva como clase padre para veh-
culos de tipo Motocicleta, Automvil y Camin. Todos los vehculos poseen un fabricante, modelo,
ao de fabricacin y kilometraje. Los automviles son de distintos estilos y las motocicletas se dedi-
can a usos determinados. A su vez, los camiones pueden tener uno o varios remolques y tienen un nivel
de seguridad, dependiendo de si sobrepasan o no el nmero mximo de pasajeros autorizados. Cree
tambin una interfaz llamada CapacidadLimite implementada por las clases Automvil y Camin.
Esta interfaz debe incluir constantes que indiquen el lmite de pasajeros admitidos en automviles y
camiones. Los lmites para automviles deben incluir el lmite de pasajeros para automviles norma-
les y para furgonetas. Con esta estructura de clases escriba un programa principal que usando una refe-
rencia polimrfica construya un objeto de clase Automvil, Motocicleta o Camin segn
decisin del usuario. El programa deber imprimir la informacin del vehculo considerado. Sugeren-
cia: Utilice el mtodo toString().
PRCTICA 20. Construya un programa que registre sobre un fichero una serie de clientes de un
banco. Para cada cliente se debe especificar el nmero de cuenta (que ser el nmero de orden del
cliente), el nombre (con un mximo de 30 caracteres), y el saldo actual. El programa debe permitir
consultar directamente los datos del cliente de una cuenta determinada y actualizar su saldo. El nom-
bre del fichero se debe introducir por lnea de rdenes. Sugerencia: Cree una clase registro que repre-
sente los datos de los clientes y las operaciones sobre ellos. Cree adems, una clase fichero que
contenga un mtodo para insertar un registro en una determinada posicin, otro para leer un registro y
otro para saltar a un determinado registro del fichero.
CASOS DE ESTUDIO
En ambas clases, las primeras filas correspondientes a los dos tercios del nmero total de asientos
de dichas clases, son para los NO FUMADORES y el tercio restante para los FUMADORES.
Cuando un cliente desee solicitar la reserva de un billete, tendr la posibilidad de elegir clase, sec-
cin de fumador o no fumador y posicin en la fila (ventana, central o pasillo). Las prioridades para
seleccionar un asiento son las siguientes: clase, fumador y posicin. Es decir, si un cliente desea un
348 Introduccin a la programacin con orientacin a objetos
billete en una clase, en NO FUMADOR y en ventanilla, pero ya no quedan billetes de esas carac-
tersticas, se le consultar si desea en pasillo, si lo hubiera; en caso contrario, se le ofrecera la posibi-
lidad de sentarse en FUMADOR y, si no, cambiar de clase. Una vez encontrado el asiento que el
cliente desea se le dar la posibilidad de anular o confirmar. En este ltimo caso, el asiento quedar
reservado. El sistema indicar qu asiento ha sido reservado y ya no se le podr asignar a otro cliente
en ese mismo vuelo. En caso de que no haya billetes con las caractersticas que desea el usuario, se le
indicar que tendr que esperar al siguiente vuelo. Si desea anular, el asiento queda disponible para
otro posible cliente. Cuando el vuelo est completo, el sistema debe indicarlo. Si un cliente desea
reservar ms de un asiento, se intentar que estn juntos y, si no, lo ms cercanos posible.
Los valores de N y M se introducirn desde la lnea de rdenes al llamar al programa. Se debe con-
trolar la entrada de datos incorrecta, en cuyo caso el programa no finalizar su ejecucin sino que ofre-
cer la posibilidad de introducir nuevamente los datos.
d) Al finalizar el programa se debe mostrar una estadstica que informe del nmero total de par-
tidas jugadas, del nmero de victorias del contrincante, del nmero de victorias del ordenador
y del nmero de partidas en tablas. Como conclusin debe mostrarse un mensaje que informe
del jugador que mejor juega.
En todos los casos debe quedar reflejado el nmero de ejemplares de un mismo elemento que se
catalogan. Bajo consulta, el sistema debe poder determinar independientemente: el nmero total de
ejemplares catalogados; el nmero de publicaciones peridicas y no peridicas; el nmero total de
libros o de trabajos de investigacin; el nmero de informes tcnicos; el nmero de tesis doctorales.
El sistema tambin debe poder indicar el nmero de publicaciones (sin especificar) que corresponden
a una materia dada.
Disee e implemente este sistema usando una aproximacin orientada a objetos, haciendo uso apro-
piado de las caractersticas de encapsulacin, herencia y polimorfismo. En particular, se debe usar una
nica estructura de datos para representar todas las publicaciones. Sugerencia: Establezca una jerarqua
de clases por herencia e implemente el programa usando una matriz de referencias polimrficas. En una
segunda versin, implemente el programa usando ficheros. No utilice la clase Vector de Java.
d) Cada producto de la tienda tiene sus propias caractersticas (los discos duros su capacidad, los
monitores su tamao, etc.). Nota: Aada a cada producto las caractersticas que considere
oportunas.
e) Debemos controlar las existencias de los productos y las ventas realizadas, siendo necesario
avisar cuando las existencias queden por debajo de un margen establecido para cada pro-
ducto.
f) Debemos controlar los datos personales de cada cliente, a los que se emitir una factura cuan-
do realicen una compra.
g) Las facturas presentarn por pantalla los datos del producto comprado, los del cliente y el total
que se debe abonar.
Disee e implemente este sistema usando una aproximacin orientada a objetos, haciendo un uso
apropiado de las caractersticas de encapsulacin, herencia y polimorfismo no utilizando la clase
Vector de Java.
C
Sumario
El lenguaje unificado de modelado o UML (Unified Modeling Language) es un sucesor de los dife-
rentes mtodos de anlisis y diseo orientados a objetos que aparecieron en la dcada de los aos
ochenta y principios de los noventa del siglo XX. En palabras de sus autores (Rumbaugh et al.,
2000) UML es un lenguaje grfico de modelado que sirve para la especificacin, visualizacin,
construccin y documentacin de los elementos de un sistema software. En resumen, UML es un
lenguaje de modelado de propsito general, pero no un mtodo de modelado especfico. Dicho de
otra forma, UML define las herramientas a usar pero no cmo usarlas para obtener un diseo. Hoy
por hoy UML ha devenido en un estndar para el modelado de sistemas orientados a objetos. UML
provee de gran cantidad de herramientas de modelado como los diagramas de casos de uso, de cla-
se, de interaccin, de estado, de actividad o fsicos. Lgicamente, la exposicin de estos conceptos
cae fuera de un texto introductorio como ste. Este apartado recoge un resumen elemental de nota-
cin UML apropiada al nivel de conocimientos considerados en este texto. Esencialmente lo que se
presenta es el punto de vista esttico a travs de los diagramas de clase. El objetivo es dotar al lec-
tor de los conocimientos bsicos que le permitan empezar a aprovechar la potencia de UML y fami-
liarizarle con su uso desde un primer momento. El lector interesado en un mayor nivel de detalle
puede consultar las referencias genricas sobre UML (Rumbaugh et al., 2000; Booch et al., 2000)
o referencias ms especficas sobre el uso del mismo (Fowler&Scott, 2000; Larman, 1999; Oeste-
reich, 1999).
LA VISTA ESTTICA
UML puede considerarse organizado en varias reas, que a su vez se subdividen en las denomina-
das vistas, a las que corresponden los distintos diagramas (Rumbaugh et al., 2000). Dentro de las
reas, una vista se puede definir como un subconjunto de UML que modela un aspecto concreto del
sistema bajo estudio. Una de las reas consideradas es la estructural, donde se describen los ele-
mentos del sistema y sus relaciones. Una de las vistas dentro del rea estructural es la vista estti-
ca, que modela los conceptos tanto del dominio del problema como de la solucin. Esta vista se
considera esttica porque no muestra la evolucin del sistema a lo largo del tiempo. En esencia se
trata de modelar las clases y sus relaciones, y la herramienta usada son los diagramas de clase. Estos
diagramas son los que se han introducido a lo largo del texto y son los que se consideran aqu con
un poco ms de detalle.
DIAGRAMAS DE CLASES
Los diagramas de clase son una herramienta de gran importancia en cualquier metodologa orientada
a objetos y UML no es una excepcin. Un diagrama de clases describe las diferentes entidades (cla-
ses) del sistema, as como sus relaciones estticas. Un diagrama de clases tambin muestra los com-
ponentes (miembros) de cada clase.
CLASES Y OBJETOS
A) CLASES
Las clases se representan como rectngulos y quedan identificadas por un nombre que las distingue
unas de otras. Es posible representar una clase slo con su nombre o indicando el denominado nom-
bre de camino o ruta que corresponde al nombre de la clase precedido por el nombre del paquete en el
Resumen de la notacin UML 353
Nombre_de_la_clase
Paquete::Nombre_de_la_clase
Nombre_de_la_clase
Atributos
Procedimientos
Cliente
nombre:String
nmero:int
saldo:double=0.0
ActualizarSaldo (valor:double):void
daSaldo ( ):double
daNmero ( ):int
daNombre ( ):String
Nombre clase
atributo esttico
Figura C.4.
La visibilidad de los miembros de una clase se indica con los smbolos 1 , # y 2 para representar
visibilidad pblica, protegida o privada. Estos conceptos se relacionan con los modificadores public,
protected y private de Java. Los smbolos anteriores se usan precediendo al identificador del
Nombre_de_la_clase
+ Atributo pblico
# Atributo protegido
- Atributo privado
+ Procedimiento pblico
# Procedimiento protegido
- Procedimiento privado
B) OBJETOS
Los objetos, entendidos como ejemplares de clase, se representan con un rectngulo, como las clases,
con la salvedad de que el nombre debe estar subrayado. El nombre de la clase a la que pertenece el
Resumen de la notacin UML 355
Nombre_de_la_clase
{abstracta}
Atributos
Procedimientos
PrimerCliente:Cliente
objeto se puede indicar con la sintaxis, nombre objeto:nombre_de_la_clase, vease la Figura C.7.
El estado del objeto, entendido como el contenido real de los atributos en un momento dado, se
puede representar indicando explcitamente cul es el valor de los atributos del objeto.
Clase Padre
B) RELACIN DE ASOCIACIN
Esta relacin se definira como una relacin estructural entre ejemplares de la misma o diferentes cla-
Clase A Clase B
ses. La idea bsica es que uno de los ejemplares contiene ejemplares (en realidad referencias a ejem-
plares) de la otra clase. La asociacin se representa con una lnea continua que conecta las clases a las
que pertenecen los objetos involucrados, como se muestra en la Figura C.9.
El nmero de ejemplares involucrados en la relacin de asociacin (multiplicidad) se indica con
el siguiente convenio. Un nmero especfico de ejemplares se indica explcitamente, colocando dicho
valor numrico al lado de la clase correspondiente. Si el valor est indeterminado se indica con un
asterisco, *. Finalmente, si lo que tenemos es un intervalo de valores posibles esto se indica como
1 1..*
Clase A Clase B
valor mnimo..valor mximo. Por ejemplo, si tenemos un mnimo de uno y un mximo de 4 la nota-
cin sera 1..4. Cualquier otra situacin se construye como combinacin de estas reglas bsicas, va-
se la Figura C.10.
Frecuentemente, una relacin de asociacin implica que desde un ejemplar de una clase se puede
acceder a ejemplares de la otra, pero no al revs. Un ejemplo sera que en la primera clase existieran
como atributos referencias a ejemplares de la segunda clase. Esta situacin define la navegacin o
navegabilidad de la asociacin (en ingls navigability). Si se desea indicar explcitamente la nave-
gabilidad de la asociacin, se incluye una cabeza de flecha en la lnea continua que representa la aso-
Clase A Clase B
ciacin y que va en direccin a la clase a cuyos ejemplares se puede acceder. As, si desde los ejem-
plares de la Clase A se puede acceder a ejemplares de la Clase B el diagrama sera el mostrado en la
Figura C.11.
En la asociacin normal las dos clases relacionadas estn al mismo nivel conceptual, es decir, la
relacin entre ellas existe por la naturaleza especfica del sistema considerado. Una relacin de aso-
ciacin especial es la agregacin. En la agregacin existe una relacin conceptual todo-parte entre las
clases. Una de las clases (la agregada) corresponde al todo y la otra a las partes. Cuando se da esta
Clase A Clase B
situacin, se puede indicar especficamente incluyendo un rombo vaco sobre la lnea continua de la
relacin al lado de la clase agregada. As, si cualquier ejemplar de la Clase A est formado por ejem-
plares de la Clase B el diagrama correspondiente sera el mostrado en la Figura C.12.
Existen casos en los que la relacin de agregacin es ms estrecha, de tal forma que la existencia
de los ejemplares de las partes depende de la existencia del ejemplar del todo. Un ejemplo es el de un
ejemplar de clase factura que contenga varios elementos de clase Producto Facturado. Si desaparece
Clase A Clase B
la factura desaparecen sus productos facturados, no pueden existir sin ella. Este tipo de asociacin se
denomina composicin y se representa igual que la agregacin pero con un rombo negro, tal y como
se muestra en la Figura C.13.
C) RELACIN DE DEPENDENCIA
Clase A Clase B
La relacin de dependencia es una relacin de uso o utilizacin, donde un elemento de una clase
usa elementos de otra, de tal forma que la variacin del estado de los elementos usados implica la
358 Introduccin a la programacin con orientacin a objetos
variacin del estado del elemento que los usa. Esta relacin se indica con una flecha de trazo discon-
tinuo, donde la cabeza de la flecha apunta hacia la clase a la que pertenecen los ejemplares usados. Por
ejemplo, si la Clase A tiene una dependencia de uso de la Clase B el diagrama correspondiente sera
el mostrado en la Figura C.14.
Un ejemplo tpico donde se aprecia la relacin de dependencia lo tenemos cuando una clase posee
algn procedimiento (mtodo en Java) que acepta como parmetro un ejemplar de otra clase.
REFERENCIAS
BOOCH, G., RUMBAUGH, J. y JACOBSON, I.: El Lenguaje Unificado de Modelado, Addison-Wesley, Primera reim-
presin, 2000.
FOWLER, M. y SCOTT, K.: UML Distilled, Addison-Wesley, Second Edition, 2000.
LARMAN. C.: UML y Patrones, Prentice-Hall, 1999.
OESTEREICH, B.: Developing software with UML, Addison-Wesley, 1999.
RUMBAUGH, J., JACOBSON, I. y BOOCH G.: El Lenguaje Unificado de Modelado. Manual de Referencia, Addison-
Wesley, 2000.
D
Sumario
Este apndice proporciona unas indicaciones sobre cmo llevar a cabo la codificacin en Java. Sien-
do un lenguaje de formato totalmente libre, cualquier estilo de codificacin es posible. El hecho de
abordar desarrollos software en equipo, o simplemente de intercambiar software entre programadores,
implica que el uso de un estilo uniforme, comprensible para todos, sea de gran ayuda. Por otro lado,
si tenemos en cuenta que un sistema software emplea la mayor parte de su existencia en labores de
mantenimiento y que dicho mantenimiento no se realiza normalmente por la misma o las mismas per-
sonas que desarrollaron el sistema, un estilo estandarizado es una ayuda invaluable. En resumen, un
estilo estandarizado acta como un vehculo que permite la transmisin fiable de informacin, es en
este caso, del significado del cdigo. Este estilo de codificacin implica no slo el formato de las sen-
tencias ejecutables del programa, sino tambin de los comentarios.
Respecto al lenguaje Java, la gua aqu propuesta est basada en las recomendaciones oficiales de
Sun (Sun, 2002). Guas similares pueden encontrarse en otros textos (Lewis y Loftus, 1998) o en Inter-
net (Javaranch, 2002).
FORMATO DE LNEAS
1. No usar ms de 80 caracteres por lnea (imagen de tarjeta). De esta forma se pueden visuali-
zar las lneas completas con un editor de texto o en una hoja impresa tamao DIN A4.
2. Cuando la lnea sea mayor de 80 caracteres, divdala en varias partes, cada una sobre una lnea.
Salte de lnea al final de una coma o al final de un operador. Si se trata de una expresin con
parntesis salte, si es posible, a lnea nueva despus de finalizar el parntesis. Por ejemplo,
casos vlidos seran,
3. Use lneas en blanco como elemento de separacin entre bloques de cdigo conceptualmente
diferentes.
4. Sangre adecuadamente cada nuevo bloque de sentencias. Entre dos y cuatro espacios en blan-
co son suficientes para cada nivel de sangrado. De esta forma se aprecia visualmente la dife-
rencia de nivel entre bloques de sentencias, sin rebasar, normalmente, los 80 caracteres por
lnea. A la hora de sangrar, y para evitar problemas de compatibilidad entre editores de texto,
use espacios y no tabuladores. Por ejemplo,
FICHEROS
1. Incluya una sola clase o interfaz por fichero, o al menos una sola clase o interfaz pblica.
2. Si hay comentarios globales para los contenidos del fichero colquelos en primer lugar.
Gua de estilo en Java 361
3. Si la clase forma parte de un paquete, la sentencia package deber ser la primera del fichero.
4. Si se importan paquetes, la sentencia import debe aparecer despus de la sentencia packa-
ge.
5. Coloque la declaracin de las clases o interfaces a continuacin de la sentencia package.
CLASES
1. Coloque en primer lugar los comentarios sobre la clase (vea el apartado comentarios).
2. Coloque los atributos (datos) a continuacin. Coloque primero las variables estticas y a con-
tinuacin las variables de ejemplar en el orden: pblico (se recomienda no incluir variables
pblicas), protegidas y privadas, es decir, public, protected y private en Java.
3. A continuacin, se declaran los mtodos, con los constructores en primer lugar. El resto de
mtodos se colocar en orden de interrelacin y no por visibilidad. As, si un mtodo pblico
usa dos mtodos privados, se debera colocar primero el pblico seguido de los dos privados.
La idea es que al leer el cdigo se siga con facilidad la funcionalidad de los mtodos. Las reco-
mendaciones de los dos ltimos puntos se resumiran de la forma siguiente,
class NombreClase {
variables estticas
variables de ejemplar pblicas (debe evitarse su uso)
variables de ejemplar protegidas
variables de ejemplar privadas
mtodos constructores
resto de mtodos
}
4. No deje espacio en blanco entre el identificador del mtodo y los parntesis, es decir, escriba,
en lugar de,
IDENTIFICADORES
1. Escoja identificadores significativos y a ser posible breves. En cualquier caso prefiera la cla-
ridad a la brevedad. Por ejemplo, es preferible el identificador,
362 Introduccin a la programacin con orientacin a objetos
integralDefinida
que el de,
intdef
2. Para clases e interfaces escoja como identificador un sustantivo. Use minsculas excepto para
la letra inicial. Si el identificador consta de varias palabras colquelas juntas, con la inicial de
cada una en maysculas. Por ejemplo, seran identificadores apropiados,
class Cliente
class ClientePreferencial
double valorTotal=0.0;
int cantidadInicial=0;
String nombre_cliente=Alfonso;
5. En el caso de los mtodos, el identificador debe ser preferentemente un verbo y debe usarse
en minsculas. Si el identificador contiene varias palabras, la inicial de todas las posteriores a
la primera va en maysculas. Ejemplos vlidos seran,
DECLARACIONES
1. Declare las variables al principio del bloque en el que se vayan a utilizar. En los mtodos,
declare las variables al principio del mtodo. Intente inicializar todas las variables que se
declaren.
2. En relacin con el punto anterior, si en los bucles no se precisa que alguna variable exista antes
o despus del mismo, declrela dentro del bucle.
3. Declare las matrices con los corchetes al lado del nombre del tipo y no del identificador, es
decir, con la sintaxis,
Gua de estilo en Java 363
int [] lista;
y no como,
SENTENCIAS DE CONTROL
1. No use nunca saltos incondicionales. Ms concretamente, no utilice la sentencia break excep-
to en la sentencia switch, donde es forzoso hacerlo. Nunca use la sentencia continue.
2. No use ms de un return en cada mtodo y coloque ste al final del mtodo.
3. Coloque la apertura de bloque ({ ) al final de la lnea inicial de la sentencia. El fin de bloque
( } ) colquelo en lnea aparte y alineado con el principio de la sentencia. Si la sentencia de
control es un if-else la clusula else debe comenzar en la lnea siguiente al fin del bloque
de la clusula if. Por ejemplo, escriba,
if (i==j) {
valor=1;
}
else {
valor=2;
}
en lugar de,
if (i==j)
{
valor=1;
} else
{
valor=2;
}
4. Ponga cada nuevo bloque de sentencias entre llaves aunque conste de una sola sentencia. Por
ejemplo, escriba
if (i==j) {
valor=1;
}
else {
valor=2;
}
y no,
if (i==j)
valor=1;
else
valor=2;
if (valor1==0) {
resultado=total;
} else if (valor1>10) {
resultado = total*0.9;
} else {
resultado = total*0.95;
}
Aunque sta es una recomendacin muy extendida que intenta que los if se interpreten en
cascada, aqu recomendamos el formato obtenido al aplicar las normas del Apartado 3 de esta
seccin, que para el ejemplo anterior producira el siguiente resultado,
if (valor1 == 0){
resultado=total;
}
else {
if (valor1 > 10) {
resultado = total*0.9;
}
else {
resultado = total*0.95;
}
}
Este formato permite identificar el alcance de las distintas estructuras if-else por el nivel de
sangrado de las sentencias.
DOCUMENTACIN
1. Documente el cdigo. No se trata de sustituir la informacin de anlisis y diseo, pero la docu-
mentacin interna es un complemento que puede servir como gua del sistema en el peor de
los casos, cuando no hay otra documentacin disponible.
2. Como cabecera de una clase incluya como informacin el autor o autores, la fecha de la lti-
ma modificacin, el propsito general de la clase y una breve explicacin de los datos y mto-
dos incorporados.
3. Como cabecera de los mtodos cuya accin no sea evidente indique el autor o autores, la fecha
de la ltima modificacin, el propsito del mtodo, el significado de los parmetros formales,
el significado de la informacin devuelta con return y las excepciones que se capturen.
4. En el cuerpo de los mtodos use comentarios para indicar el propsito de las tareas que no
sean evidentes, tales como algoritmos especficos. En cualquier caso aumente la legibilidad
del cdigo usando identificadores significativos para variables o mtodos.
REFERENCIAS
Javaranch: http://www.javaranch.com/style.jsp ltima visita realizada en junio de 2002.
LEWIS J. y LOFTUS W., Java Software Solutions, Addison-Wesley, 1998.
Sun: http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html ltima visita realizada en junio de 2002.
E
Interfaces grficas de usuario
Sumario
En la actualidad las interfaces grficas de usuario han devenido en el mtodo estndar que el usuario
utiliza para interaccionar con una herramienta software. Por esta razn, Java presenta una serie de cla-
ses agrupadas en paquetes que permiten la creacin de interfaces grficas de usuario (o IGU), de for-
ma independiente de la plataforma. Para la construccin de una IGU necesitamos aplicar tanto un
punto de vista esttico como uno dinmico. El punto de vista esttico corresponde a la seleccin y dis-
tribucin de los elementos grficos que comporta la interfaz, tales como botones o ventanas. El punto
de vista dinmico se refiere a cmo, a travs de estos componentes, se puede llevar a cabo la ejecu-
cin del programa usando la denominada programacin dirigida o conducida por sucesos 1. En este
apndice se describen brevemente los dos grupos de herramientas grficas que Java ofrece para la
construccin de interfaces y la programacin conducida por sucesos. Para una visin ms profunda y
detallada del desarrollo de interfaces grficas de usuario bajo Java el lector interesado puede consul-
tar cualquier texto sobre el lenguaje (Eckel, 2002; Savitch, 2001; Froufe, 2000; Geary, 1999; Winder
y Roberts, 2000). Pasemos a continuacin a presentar los componentes (punto de vista esttico) y la
programacin dirigida por sucesos.
COMPONENTES AWT
El AWT representa una biblioteca de clases con un amplio conjunto de clases y mtodos que permiten
la creacin y gestin de ventanas, administracin de tipos de letra, texto de salida y grficos, tanto en
applets como en otros entornos grficos de interfaz de usuario, por ejemplo sistemas de ventanas. El
AWT es la biblioteca original implementada por Java para la creacin de interfaces grficas de usuario.
El paquete java.awt, que est organizado de manera jerrquica, contiene todas las clases del AWT.
Jerarqua de clases
La idea fundamental para construir una interfaz de usuario en Java es considerar que una ventana es
un conjunto de componentes anidados. Dicha anidacin crea una jerarqua de componentes que va des-
de la ventana general de la pantalla hasta, por ejemplo, el botn ms pequeo. Los componentes prin-
cipales que se pueden usar son:
1
En ingls, Event Driven Programming.
Interfaces grficas de usuario 367
Object
Component
Container
Window Panel
Frame
Un ejemplo de subclase de Container es la clase Panel, la cual no aade nuevos mtodos sino
que implementa los que hereda de la clase padre, que es abstracta. Un objeto de la clase Panel es un
componente de pantalla, concretamente una ventana que no contiene ni barra de ttulos, ni bordes, ni
barra de mens. Para aadir componentes sobre un objeto de la clase Panel se utiliza un mtodo here-
dado de la clase Container llamado add(). En relacin con las applets de Java (que se conside-
rarn en el apndice F de este texto) comentaremos que la clase Panel es la superclase de la clase
Applet. Siempre que la salida por pantalla se dirige a una applet, se dibuja sobre un objeto de la cla-
se Panel.
Otra clase que hereda de Container es la clase Window, vese la Figura E.1, que crea ventanas
que se sitan directamente sobre el escritorio. Generalmente, no se crean objetos de esta clase sino de
la clase que hereda de ella, definida como clase Frame, y que representa lo que normalmente se
entiende por ventana. Una ventana de esta clase posee una barra de ttulo y una barra de men, adems
de bordes y esquinas para cambiar de tamao.
COMPONENTES SWING
En la versin 1.2 de Java y posteriores, aparte de las clases AWT, se ha introducido otro conjunto de
368 Introduccin a la programacin con orientacin a objetos
clases que proporcionan componentes ms potentes y flexibles que los AWT originales. stos reciben
el nombre de componentes Swing. Los componentes Swing frente a los componentes AWT (llamados
componentes pesados) presentan la ventaja de ser independientes de la plataforma, por eso reciben el
nombre de componentes ligeros. Los componentes Swing son ms numerosos que los AWT (por ejem-
plo, fichas, paneles con scroll, rboles), algunos de los cuales se describen a continuacin.
La clase padre de casi todos los componentes Swing es la clase JComponent que es una especia-
lizacin de la clase abstracta java.awt.Component. Las clases relacionadas con Swing se encuen-
tran en el paquete javax.swing.
Los componentes Swing se pueden anidar unos dentro de otros, y casi todos permiten que se les
incluya una pequea imagen grfica o icono. Adems, facilitan la creacin de ayudas asociadas a los
componentes (ayudas contextuales), y se puede mejorar el aspecto de los componentes aadiendo un
borde visible. Por estas razones, permiten crear interfaces ms atractivas y con mayor funcionalidad
que los componentes AWT.
A continuacin, se describen algunos de los componentes Swing. Muchos de ellos reciben el mis-
mo nombre que su correspondiente componente AWT, pero con la diferencia de que los Swing
comienzan por una J mayscula.
La clase JFrame
Hereda de la clase Frame. La clase JFrame se utiliza para crear la ventana principal de una aplica-
cin, vese la Figura E.2. Esta ventana incluye los controles habituales de cambio de tamao y cierre.
Las ventanas JFrame poseen un panel raz (de la clase JRootPanel) que gestiona el interior de la
ventana. La forma estndar de proporcionar el comportamiento de la ventana es mediante un gestor u
oyente de sucesos.
La clase JPanel
JPanel es un contenedor bsico que sirve para agrupar a otros componentes. Normalmente se utiliza
para dividir una zona de pantalla en secciones. A cada seccin se le puede aplicar un diseo diferen-
te. Si se desea crear un panel con barra de desplazamiento se puede usar la clase JscrollPanel.
La clase JLabel
Esta clase implementa una etiqueta que puede contener una cadena de texto, un icono o ambas cosas,
vase la Figura E.2. Una etiqueta puede mostrarse en una ventana o en cualquier contenedor. El texto
que muestra no es editable por el usuario. El uso ms frecuente de las etiquetas es dar un nombre a
otros componentes de la interfaz grfica. Una novedad introducida en los Swing es que en una etiqueta
se pueden escribir varias lneas, con distinto formato y fuente. Los constructores de la clase JLabel
son:
La clase JButton
Esta clase aade un botn grfico que el usuario puede utilizar (mediante el ratn o teclado) para inte-
Interfaces grficas de usuario 369
JFrame
raccionar con el sistema, vase la Figura E.2. En todos los botones se puede incorporar adems de tex-
to un icono, e incluso se pueden asignar varios iconos a un mismo botn para indicar los distintos esta-
dos en los que puede estar un botn. Algunos de sus constructores son:
La clase JRadioButton
Implementa los botones de opcin que son una especializacin de un botn con estado (los posibles esta-
dos son botn seleccionado o no seleccionado). La principal caracterstica de este tipo de botones es que
son excluyentes. En un grupo de botones de opcin slo uno puede estar seleccionado. La representacin
grfica del botn seleccionado difiere del resto de botones. Como ejemplo vase la Figura E.3.
Con el objetivo de conseguir la exclusin mutua, los botones se agrupan en un objeto de la clase
ButtonGroup. Esta agrupacin indica al usuario que los botones estn relacionados. Normalmente se
incluyen en un contendedor tipo panel con algn tipo de borde. Algunos ejemplos de constructores de
la clase JRadioButton son:
est seleccionado.
JRadioButton(String s). Inserta el texto asociado al botn.
JRadioButton(String s, Icon i, boolean estado). Inserta un texto asociado al botn,
un icono e indica si el botn estar seleccionado inicialmente.
La clase JCheckBox
Esta clase ofrece las caractersticas operativas de una casilla de verificacin, donde las casillas tie-
nen dos estados, seleccionado o no seleccionado, que normalmente se denota por una cruz o marca de
seleccin. Las casillas de verificacin se utilizan cuando se quiere que el usuario decida si elegir o no
una opcin. Si hay varias casillas, todas pueden estar seleccionadas, es decir, no son excluyentes.
Como ilustracin vase la Figura E.4.
Algunos de sus constructores se muestran a continuacin:
La clase JComboBox
Dicha clase implementa una caja, llamada normalmente caja combo, que es una combinacin de un
campo de texto y una lista desplegable. Una caja combo muestra una seleccin por defecto. Si el usua-
rio desea elegir otra opcin, puede desplegar la lista o incluso teclear la seleccin en el campo de tex-
to. A continuacin, se indican dos de sus constructores.
La clase JTable
En una tabla, los datos se distribuyen en filas y columnas. La clase JTable permite la implementa-
cin de tablas. Un constructor comn de la clase JTable es:
donde datos es una matriz (array) bidimensional que contiene la informacin de la tabla. El objeto
colTitulos contiene los ttulos de las columnas.
La clase JMenuBar
Implementa una barra de mens que habitualmente se aade a un panel raz. Normalmente, despus
de crear una barra de men se le aaden los mens desplegables usando la clase JMenu. El construc-
tor de la clase JMenuBar no necesita parmetros. El mtodo add(JMenu) permite aadir un men
desplegable a una barra de mens.
La clase JTextComponent
Proporciona las funcionalidades de trabajo con texto, permitiendo seleccionar, copiar, cortar y pegar
el texto. Algunos de los mtodos ms usados de esta clase son:
La clase JTextField
Es una subclase de la clase JTextComponent, por lo que hereda sus mtodos. La clase JTextField
permite mostrar y editar una lnea de texto, vese la Figura E.2. Generalmente, se utiliza para solici-
tar al usuario entradas breves. Su constructor es:
JTextField(String s, int ancho), donde se indica el contenido inicial y la anchura del com-
ponente en nmero de caracteres, respectivamente.
La clase JTextArea
Permite mostrar y editar varias lneas de texto sencillo en el que slo se puede utilizar un tipo de letra.
Algunos de sus constructores son:
ADMINISTRADORES DE ORGANIZACIN
Los gestores o administradores de organizacin (tambin llamados de disposicin o diseo) posicio-
nan de forma automtica los componentes dentro de un contenedor. La apariencia de una ventana
depende del gestor que se est utilizando. Tambin es posible colocar manualmente los componentes
dentro de una ventana. Sin embargo, resulta mucho ms cmodo usar un gestor que determine autom-
ticamente la forma y posicin de cada componente. El gestor deseado se establece con el mtodo,
utiliza el gestor por defecto. Si se prefiere no usar un gestor porque se desea colocar los componentes
manualmente layaoutObjeto debe ser null. A continuacin, se describen algunos de los gestores exis-
tentes:
FlowLayout es el gestor de organizacin por defecto. Organiza un componente uno detrs de otro (de
izquierda a derecha y de arriba hacia abajo).
BoderLayout distribuye los componentes en cinco zonas: norte, sur, este y oeste y deja una zona
central pare el resto de los componentes.
CardLayout tiene varias posibilidades de organizacin. Permite manejar fichas o tarjetas de tal forma
que slo una est visible cada vez y ocupe todo el rea.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
Interfaces grficas de usuario 373
int i;
i=0;
double factorial;
factorial=1.0;
do {
i=i+1;
factorial=factorial*i;
} while (i<n);
return factorial;
}
TextField otrocampoTexto;
MiactionListener(TextField campoTexto){
otrocampoTexto=campoTexto;
}
import java.awt.*;
import javax.swing.*;
//El siguiente paquete se usa para gestionar los sucesos
import java.awt.event.*;
public Calculadora() {
try{
dibujaVentana();
}
catch (Exception e){
System.out.println(Se ha producido la exception+e.toString());
}
}
btResta.setText(-);
btResta.setFont(new Font(Dialog, 1, 18));
btResta.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
btResta_actionPerformed(e);
}
});
btMultiplicacion.setText(*);
btMultiplicacion.setFont(new Font(Dialog, 1, 18));
btMultiplicacion.addActionListener
(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
Interfaces grficas de usuario 377
btMultiplicacion_actionPerformed(e);
}
});
btDivision.setText(/);
btDivision.setFont(new Font(Dialog, 1, 18));
btDivision.addActionListener
(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
btDivision_actionPerformed(e);
}
});
btResultado.setText(=);
btResultado.setFont(new Font(Dialog, 1, 18));
btResultado.addActionListener
(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
btResultado_actionPerformed(e);
}
});
else return 0;
}//fin dividir
void btResta_actionPerformed(ActionEvent e) {
Double aux=new Double(txtEntrada.getText());
operando1=aux.doubleValue();
txtEntrada.setText();
txtEntrada.requestFocus();
operador=2;
lblResultado.setText();
}
void btMultiplicacion_actionPerformed(ActionEvent e) {
Double aux=new Double(txtEntrada.getText());
operando1=aux.doubleValue();
txtEntrada.setText();
txtEntrada.requestFocus();
operador=3;
lblResultado.setText();
}
void btDivision_actionPerformed(ActionEvent e) {
Double aux=new Double(txtEntrada.getText());
operando1=aux.doubleValue();
txtEntrada.setText();
txtEntrada.requestFocus();
operador=4;
Interfaces grficas de usuario 379
lblResultado.setText();
}
void btResultado_actionPerformed(ActionEvent e) {
Double aux=new Double(txtEntrada.getText());
operando2=aux.doubleValue();
switch(operador){
case 1:
resultado=sumar(operando1,operando2);
break;
case 2:
resultado=restar(operando1,operando2);
break;
case 3:
resultado=multiplicar(operando1,operando2);
break;
case 4:
resultado=dividir(operando1,operando2);
break;
}
txtEntrada.setText(resultado+);
operador=-1;
lblResultado.setText( Operacin realizada);
}
}
REFERENCIAS
ECKEL, B., Piensa en Java, Segunda Edicin, Prentice-Hall, 2002.
FROUFE, A., Java 2. Manual de usuario y tutorial. Segunda Edicin.Ra-Ma, 2000.
GEARY, D. M., Java. Mastering the JFC. Third Edition. Volume II: Swing. Sun Microsystems. Press 1999.
SAVITCH W., Java An Introduction to Computer Science & Programming, Second Edition, Prentice-Hall, 2001.
WINDER, R. y ROBERTS, G., Developing Java Software, Second Edition, John Wiley & Sons Ltd, 2000.
F
Applets
Sumario
Java permite crear dos tipos de programas: las aplicaciones y las applets. Todos los ejemplos que se
han presentado en este libro eran aplicaciones, caracterizadas porque se pueden ejecutar en una com-
putadora usando su sistema operativo. Sin embargo, el lenguaje Java es muy popular por sus applets.
Las applets son aplicaciones diseadas para ser incluidas en un documento HTML, y que pueden ser
transmitidas por Internet y ejecutadas en un navegador Web compatible con Java 1. En este apndice
se recogen las diferencias existentes entre las aplicaciones y las applets, se describe cmo construir
applets, la forma en que funcionan y cmo transformar una aplicacin en una applet.
import java.applet.*;
public class NombreClase extends Applet{
- - - Cuerpo de la clase - - -
}
Como muestra el cdigo anterior la clase de una applet siempre debe ser pblica para ser accesi-
ble desde cualquier parte.
El siguiente paso es definir los mtodos de la clase. Para mostrar un ejemplo completo escribire-
mos una applet que muestre en una ventana (bien de un navegador o de un visor) la tpica frase Hola
Mundo, vase el Programa F.1.
import java.applet.*;
import java.awt.*;
public class HolaMundo extends Applet{
public void paint (Graphics g){
g.drawString (Hola Mundo!!!, 50,50);
}
}
1
Actualmente todos los navegadores incorporan un JVM (Java Virtual Machine) para poder interpretar bytecode y eje-
cutar las applets.
Applets 381
Como se ha indicado anteriormente, las applets utilizan los componentes AWT para la entrada y
salida, por eso se ha importado tambin el paquete java.awt que contiene la clase Graphics, cuyo
mtodo drawString hemos utilizado para mostrar en la ventana una frase. Los nmeros 50,50 indi-
can las coordenadas donde debe aparecer el texto, medidas desde el ngulo superior izquierdo de la
zona definida por la applet.
El mtodo paint es el que utiliza Java cada vez que actualiza o repinta una applet y cuando la
applet se ejecuta por primera vez. El mtodo paint acepta un parmetro de clase Graphics que des-
cribe el entorno grfico en el que la applet se ejecuta. ste se utiliza siempre que una applet tiene que
presentar su salida.
El siguiente paso es la ejecucin del cdigo. Lo primero que se debe hacer es compilarlo como si
fuera una aplicacin clsica, creando por lo tanto el fichero HolaMundo.class. Despus tendremos
que decidir si queremos probar el cdigo en un navegador o en un visor.
Para poder transmitir la applet por Internet y ejecutarla en un navegador, es necesario escribir un
pequeo fichero HTML con una etiqueta <APPLET> tal y como se muestra a continuacin:
Las sentencias WIDTH (anchura) y HEIGHT (altura) especifican las dimensiones del rea de pan-
talla utilizada por la applet. Una vez creado este fichero, se puede ejecutar el navegador y despus car-
gar este fichero (esto es, pinchar con el ratn en el enlace vinculado a este fichero o cargar la URL del
fichero), lo que permite la ejecucin de la applet HolaMundo. La Figura F.1 muestra el resultado que
se obtiene al ejecutar HolaMundo desde el Microsoft Internet Explorer.
Para ejecutar la applet desde un visor como el appletviewer basta con escribir desde el smbolo del
sistema:
i=0;
factorial=1.0;
do {
i=i+1;
factorial=factorial*i;
} while (i<n);
System.out.println (Factorial: +factorial);
} // Fin mtodo
} // Fin clase Factorial
Veamos paso a paso cmo pasar de la aplicacin a la applet, para lo cual usaremos algunos con-
ceptos relativos a los componentes AWT.
En la aplicacin se lee directamente el nmero del cual se desea calcular el factorial. Para leer un
texto en la applet debemos usar un componente AWT adecuado. En este caso usaremos un
TextField. Lo mismo ocurre con la salida por pantalla, hay que elegir otro componente, en este
ejemplo hemos utilizado una etiqueta para mostrar el resultado.
Cuando se utiliza un rea de texto (TextField) se necesita un receptor de sucesos que detecte
cundo se pulsa la tecla INTRO. En nuestra applet hemos creado la clase MiactionListener que
ser la encargada de recibir y gestionar los sucesos relacionados con el TextField. En esta clase,
adems de escribir el mtodo constructor, se ha sobrescrito el mtodo actionPerformed que es el
encargado de capturar el suceso de pulsacin de la tecla INTRO. Cuando este suceso sea capturado,
se lee el nmero que se ha introducido en el TextField y se invoca al mtodo
factorialIterativo, mostrando posteriormente el resultado obtenido.
Es importante resaltar que para que la applet funcione correctamente, el gestor de sucesos debe
ponerse a escuchar al comenzar a ejecutarse dicha applet. Por esta razn, dentro del mtodo start
se ha creado el receptor de sucesos del componente TextField. El Programa F.2 muestra el cdigo
obtenido
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
int i;
i=0;
double factorial;
factorial=1.0;
do {
i=i+1;
factorial=factorial*i;
} while (i<n);
return factorial;
}
TextField otrocampoTexto;
MiactionListener(TextField campoTexto){
otrocampoTexto=campoTexto;
}
numero=Integer.parseInt(otrocampoTexto.getText());
resultado=factorialIterativo(numero);
etiqueta1.setText(El resultado es +resultado);
}
}//Fin de la clase interna MiactionListener
La clase MiactionListener es una clase interna (est declarada dentro de otra clase). Para ms
informacin sobre el uso y comportamiento de las clases internas vase Eckel, 2002; Arnold et al.,
2001. El lector interesado en profundizar en el tema del diseo de applets puede consultar Eckel, 2002;
Wu, 2001; Jaworski, 1999.
Applets 385
REFERENCIAS
JAWORSKI, J.: Java 1.2. Al Descubierto, Prentice-Hall, 1999.
ARNOLD, K., GOSLING, J. y HOLMES, D.: El Lenguaje de Programacin Java, Tercera Edicin, Addison-Wesley,
2001.
ECKEL B.: Piensa en Java, Segunda Edicin, Prentice-Hall, 2002.
WU, C. T.: Introduccin a la Programacin Orientada a Objetos con Java, McGraw-Hill, 2001.