Anda di halaman 1dari 395

See

discussions, stats, and author profiles for this publication at:


https://www.researchgate.net/publication/31737252

Introduccin a la programacin con


orientacin a objetos / C. Muoz Caro, A.
Nio Ramos, A. Vizcano Barcel.

Book January 2002


Source: OAI

CITATIONS READS

0 567

3 authors, including:

Alfonso Nio Aurora Vizcaino


University of Castilla-La Mancha University of Castilla-La Mancha
108 PUBLICATIONS 926 CITATIONS 32 PUBLICATIONS 260 CITATIONS

SEE PROFILE SEE PROFILE

Available from: Alfonso Nio


Retrieved on: 12 October 2016
Introduccin a la Programacin
con Orientacin a Objetos

Camelia Muoz Caro


Alfonso Nio Ramos
Aurora Vizcano Barcel
INTRODUCCIN A LA PROGRAMACIN
CON ORIENTACIN A OBJETOS
INTRODUCCIN A LA
PROGRAMACIN
CON ORIENTACIN A OBJETOS

Camelia Muoz Caro


Alfonso Nio Ramos
Aurora Vizcano Barcel
Universidad de Castilla-La Mancha

Madrid Mxico Santaf de Bogot Buenos Aires Caracas Lima Montevideo San Juan San Jos
Santiago So Paulo White Plains
Datos de catalogacin bibliogrfica

MUOZ CARO, C.; NIO RAMOS, A.;


VIZCANO BARCEL, A.
Introduccin a la programacin con orientacin a objetos
PEARSON EDUCACIN, S.A., Madrid, 2002

ISBN: 84-205-3440-4
MATERIA: Informtica 681.3

Formato: 195 3 250 mm Pginas: 408

Todos los derechos reservados.


Queda prohibida, salvo excepcin prevista en la Ley, cualquier forma de reproduccin, distribucin,
comunicacin pblica y transformacin de esta obra sin contar con autorizacin de los titulares
de propiedad intelectual. La infraccin de los derechos mencionados puede ser constitutiva de delito
contra la propiedad intelectual (arts. 270 y sgts. Cdigo Penal).

DERECHOS RESERVADOS
2002 respecto a la primera edicin en espaol por:
PEARSON EDUCACIN, S.A.
Nez de Balboa, 120
28006 Madrid

MUOZ CARO, C.; NIO RAMOS, A.; VIZCANO BARCEL, A.


Introduccin a la programacin con orientacin a objetos

ISBN: 84-205-3440-4
ISBN eBook: 978-84-8322-584-4

PRENTICE HALL es un sello editorial autorizado de PEARSON EDUCACIN, S.A.

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.

Composicin: JOSUR TRATAMIENTOS DE TEXTOS, S.L.


Powered by Publidisa

IMPRESO EN ESPAA - PRINTED IN SPAIN

Este libro ha sido impreso con papel y tintas ecolgicos


1

Sistemas basados
en computador

Sumario

1.1. Introduccin 1.4. Arquitectura clsica o de von Neumann


1.2. Concepto de computacin de un computador
1.2.1. Definicin de computacin 1.5. Redes de computadores
1.2.2. Dispositivos de computacin 1.5.1. Generalidades
1.3. Concepto de Informtica 1.5.2. Internet
1.3.1. Definicin de informtica 1.5.3. La World-Wide-Web (WWW)
1.3.2. Datos e informacin
1.3.3. Representacin de la informacin
1.3.4. Sistemas informticos
1.3.5. Consideraciones sobre el software
2 Introduccin a la programacin con orientacin a objetos

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.2. CONCEPTO DE COMPUTACIN


En este apartado vamos a definir el concepto de computacin y a presentar el desarrollo de las tcni-
cas y dispositivos de computacin que han conducido al ordenador moderno.

1.2.1. DEFINICIN DE COMPUTACIN


Como definicin clsica de computacin tenemos la dada por el Merriam-Websterss Collegiate Dic-
tionary en su dcima edicin que define computacin como el acto y accin de computar, donde com-
putar equivale a determinar, especialmente por medios matemticos. En el mundo anglosajn,
originalmente un computer era una persona que realizaba clculos para resolver un problema. Slo
hacia 1945 el nombre se empieza a aplicar a la maquinaria que realiza dicha tarea (Ceruzzi, 1999).
Los computadores (ordenadores) actuales son todava capaces de computar (resolver problemas por
medios matemticos), especialmente en el campo cientfico-tcnico donde sta es su misin principal.
Sin embargo, la tremenda repercusin del ordenador en la vida actual no proviene slo de su capacidad
de cmputo, sino de su capacidad para almacenar y recuperar datos, para manejar redes de comunica-
ciones, para procesar texto, etc. Es decir, de su capacidad para procesar informacin. sta es la causa
de la gran importancia del computador en la vida actual. Para la mayora de la gente el computador
representa una forma eficiente de gestionar informacin, sea en forma de texto, cartas enviadas por
correo electrnico, informes generados automticamente o transacciones de todo tipo. Para la mayor
parte de la poblacin la realizacin de clculos es lo menos importante que un computador realiza.
Sin embargo, los computadores se desarrollaron con el principal objetivo de realizar clculos, aun-
que la potencia final y los usos del ingenio habran sido impensables para muchos de los que a lo lar-
go del tiempo participaron en la tarea. Resulta interesante presentar el desarrollo histrico de los
medios de computacin y el desarrollo del computador moderno. Esta presentacin implcitamente lle-
va aparejado el desarrollo del concepto de programacin como tcnica para describir al computador,
en trminos inteligibles para l, las tareas que se desea que realice.

1.2.2. DISPOSITIVOS DE COMPUTACIN


Los dispositivos originales de computacin eran dispositivos de ayuda para la realizacin de clculos
aritmticos (computacin en sentido etimolgico), que poco a poco incrementaron sus posibilidades
hasta llegar al concepto moderno de computador. Veamos brevemente la evolucin histrica.

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

El punto de partida para cualquier tcnica de computacin es la preexistencia de un sistema de nume-


racin. Esto no es en absoluto trivial, hoy por hoy la educacin elemental ensea a los nios a contar
usando un sistema de notacin decimal, y la tcnica parece algo consustancial con la existencia huma-
na. Sin embargo, el concepto de nmero como una abstraccin de las entidades numeradas es un paso
fundamental en la evolucin cultural del hombre. Que no siempre esto ha sido as se puede todava
observar en algunos lenguajes tribales donde se usan distintos nombres para las cantidades depen-
diendo de la naturaleza de lo numerado. Desde este punto de vista no es lo mismo cuatro piedras que
cuatro naranjas y el paso de abstraccin fundamental es la consideracin de que en ambos casos tene-
mos cuatro elementos. Una vez que se dispone de un sistema de numeracin el siguiente paso es su
uso para contar elementos.

Dispositivos de cmputo antiguos

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

Figura 1.1. baco

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

Figura 1.2. Regla de clculo

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

Figura 1.3. La Minerva, una mquina de Baldwin-Odhler de fabricacin espaola

Hasta la introduccin de las calculadoras electrnicas las mquinas de Baldwin-Odhner se seguan


usando en oficinas y laboratorios.

Las mquinas de Babbage


Los instrumentos mecnicos mencionados en el aparatado anterior no son sino ayudas mecnicas de
computacin. El primer gran paso hacia lo que es el concepto moderno de computador, lo dio Charles
Babbage (1791-1871) con sus trabajos sobre computacin automtica. El nivel tecnolgico de su tiem-
po no era suficiente para poder llevar a cabo sus diseos, pero las ideas de Babbage eran muy avan-
zadas para la poca. Tanto es as que Babbage se considera uno de los pioneros del desarrollo del
computador moderno, al mismo nivel de Konrad Zuse o Howard Aitken que trabajaron en el desarro-
llo de los primeros modelos de computador en las dcadas de 1930-1940.
Charles Babbage era matemtico y hombre de ciencia en el sentido ms general que esta palabra
tena en el siglo XIX. En aquella poca, se haca amplio uso de tablas matemticas como las de loga-
ritmos, por ejemplo, para reducir el trabajo de clculo. Dada su formacin e intereses, Babbage haca
uso intensivo de las tablas matemticas y era consciente de la gran cantidad de errores que posean. La
pasin por la precisin de Babbage le llev a abordar la construccin de tablas matemticas libres de
errores. Para ello incluso dise sistemas tipogrficos para reducir la probabilidad de la aparicin de
errores en las tablas. Sin embargo, el problema era siempre el mismo. Una persona calculaba los valo-
res y escriba un borrador de la tabla con la consiguiente posibilidad de error. Luego, el borrador era
traducido a mano en tipos de imprenta para imprimir las tablas, con la adicional posibilidad de error.
Babagge lleg a la conclusin de que la nica forma de evitar los errores humanos era automatizar
8 Introduccin a la programacin con orientacin a objetos

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.

La mquina analtica constaba de tres partes que Babbage denomin:

The store (El almacn)


The mill (La fbrica o taller)
The control barrell (El cilindro o tambor de control)

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.

1.3. CONCEPTO DE INFORMTICA


Como hemos visto, el origen del ordenador o computador se debe a la necesidad de realizar clculos
de forma automtica. Sin embargo, el procesamiento numrico no es la nica utilidad de un ordena-
dor. La posibilidad de realizar operaciones lgicas le dota de la capacidad de usarse para el procesa-
miento de informacin, entendida desde un punto de vista general. El cuerpo de conocimiento que se
encarga de todo lo relacionado con el desarrollo y uso de ordenadores para el tratamiento de informa-
cin (numrica o no) es la informtica. Veamos una definicin ms precisa.
10 Introduccin a la programacin con orientacin a objetos

1.3.1. DEFINICIN DE INFORMTICA


Informtica del francs informatique es la designacin castellana de Computer Science and Engineering
en ingls. Segn la Real Academia Espaola se define como el conjunto de conocimientos cientficos y
tcnicas que hacen posible el tratamiento automtico de la informacin por medio de ordenadores.
La siguiente pregunta es obvia: qu se entiende por informacin?

1.3.2. DATOS E INFORMACIN


En primer lugar es necesario distinguir con precisin entre los conceptos de datos e informacin:

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.

1.3.3. REPRESENTACIN DE LA INFORMACIN


Habiendo definido el concepto de informacin el problema es cmo representarla para poder mane-
jarla de forma automtica. Es posible idear muchas maneras de hacerlo, pero la clasificacin bsica
nos lleva a la distincin entre tcnicas analgicas y digitales. Veamos la diferencia.

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 .

72 111 108 97 44 32 80 101 112 101 46

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.

Tabla 1.1. Equivalencia decimal-binario

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:

1 byte < > 8 bits

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.

Tabla 1.2. Nmero de posibilidades en


funcin del nmero de bits

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

1.3.4. SISTEMAS INFORMTICOS


Llega ahora el momento de considerar dos conceptos nuevos de importancia, el de sistema y a partir
de l, el de sistema informtico (o sistema basado en computador). Comencemos por la definicin de
sistema.
Sistemas basados en computador 13

Un sistema es un conjunto de entidades que interrelacionan para un fin comn.

El concepto de sistema es ubicuo en el campo informtico y aparece en mltiples contextos. As,


hablamos de sistemas fsicos, de sistemas de informacin o de sistemas software.
Un sistema informtico es un conjunto de ciertas entidades que tienen como objetivo un determi-
nado tipo de procesamiento de informacin. Estas entidades pueden ser:

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.

1.3.5. CONSIDERACIONES SOBRE EL SOFTWARE


Comnmente se considera software como sinnimo de programa. Sin embargo, desde el punto de vis-
ta informtico el concepto de software es ms general, abarcando no slo el cdigo generado, sino
tambin toda la documentacin asociada. Tradicionalmente, el software se clasifica en dos grandes
grupos, software de sistemas y software de aplicacin.
El software de sistemas es software usado por otro software. El ejemplo clsico es el del sistema
operativo, que es el programa que permite comunicarse con, y usar de forma cmoda, el hardware. El
sistema operativo es la conexin con el hardware que usan el resto de programas que se ejecutan en
un ordenador. Por otro lado, el software de aplicacin es el destinado a su utilizacin por un usuario y
no por otros programas. Una hoja de clculo, un procesador de textos o un programa de juego son
ejemplos tpicos.
La comunicacin del usuario con el software se realiza hoy casi generalizadamente a travs de inter-
faces grficas de usuario. Lo primero es indicar qu interfaz representa en informtica una entidad (hard-
ware o software) que acta como intermediario o conexin entre otras dos, por ejemplo, entre un usuario
y un programa. En una interfaz grfica de usuario las capacidades del programa estn representadas por
medio de smbolos grficos (iconos) a los que se puede acceder por medio de algn dispositivo apunta-

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

Figura 1.5. Diagrama mostrando la interrelacin entre el usuario y un sistema software

1.4. ARQUITECTURA CLSICA O DE VON NEUMANN DE UN COMPUTADOR


La estructura de un computador tpico responde a una organizacin concreta. Bsicamente se trata de
la organizacin definida por Babbage con algunas caractersticas adicionales. En la actualidad esta
organizacin o arquitectura tpica se denomina arquitectura clsica o de von Neumann, dado el papel
que ste jug en el desarrollo del EDVAC, donde por primera vez se conjugaban estos factores. La
arquitectura de von Neumann esencialmente incorpora las siguientes caractersticas:

Uso de la estructura funcional ya determinada por Babbage:

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

El esquema funcional de un ordenador estndar (basado en la arquitectura clsica) se muestra en


la Figura 1.6.
Obsrvese que la unidad aritmtico-lgica y la unidad de control conforman bsicamente la uni-
dad central de proceso o CPU (Central Processing Unit). Esta unidad suele estar fsicamente construi-
da sobre un chip, tambin denominado microprocesador. Se considera el cerebro del ordenador.
Obsrvese tambin que la CPU y la memoria central o principal definen la unidad central (no de pro-
ceso, sino el ordenador central propiamente dicho) y que los dems dispositivos no. Estos otros dis-
positivos se denominan perifricos (aunque fsicamente pueden estar dentro de la misma carcasa que
el ordenador o computador central). Comentemos los distintos componentes:
Sistemas basados en computador 15

Figura 1.6. Estructura funcional clsica de un computador

a) Entrada o unidad de entrada


Representa el dispositivo a travs del cual se introducen los datos y las instrucciones. Ambos sern
almacenados en la memoria (central o secundaria). Los dispositivos de entrada pueden ser variados:
un teclado, un escner, un lector de cdigos de barras, etc. Puede haber ms de uno conectado y fun-
cionando en un sistema.

b) Salida o unidad de salida


Es el dispositivo que muestra el resultado de la ejecucin del programa en el ordenador. Tambin
podemos tener distintos tipos de dispositivos como monitores, impresoras o plotters.

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

Figura 1.7. Representacin esquemtica de la organizacin de la memoria de un ordenador

Tabla 1.3. Medidas de memoria

Unidad Smbolo Valor (bytes)


kilobyte KB 210 (1024)
20
megabyte MB 2 (1024*1024)
gigabyte GB 230 (1024*1024*1024)
terabyte TB 240 (1024*1024*1024*1024)

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-

Figura 1.8. Interrelacin entre la CPU y las memorias cach y RAM


Sistemas basados en computador 17

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.

d) Unidad aritmtico-lgica o ALU (Arithmetic Logic Unit)


Es el conjunto de circuitos que permiten realizar las operaciones aritmticas y las operaciones lgicas
tales como las comparaciones o la aplicacin del lgebra de Boole binaria. La ALU realiza su trabajo
usando la informacin contenida en unas posiciones de memoria internas a la CPU denominadas regis-
tros. Estos registros estn especializados, existiendo registros de instrucciones (van almacenando las
instrucciones del programa) de uso general o el denominado contador de programa que almacena la
direccin de memoria en que se encuentra la siguiente instruccin del programa que se est ejecutando.

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.

Figura 1.9. Ciclo de recuperacin-descodificacin-ejecucin


18 Introduccin a la programacin con orientacin a objetos

1.5. REDES DE COMPUTADORES


Por su importancia actual es til presentar, aunque sea informalmente en un texto introductorio como
ste, el concepto de red de computadores.

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:

a) Conexin punto a punto, vase la Figura 1.10 (a)


sta sera la solucin ms sencilla, con todos los ordenadores directamente conectados a todos los
dems. Lgicamente, esta tcnica slo es factible para unos pocos ordenadores que estn fsicamente
prximos, basta con imaginar los problemas de cableado, ya que el nmero de conexiones entre N
ordenadores es de N(N-1)/2. Una solucin mucho ms elegante es la de lnea compartida.

b) Lnea compartida, vase la Figura 1.10 (b)


En este caso, slo hay una lnea de conexin a la que se van conectando los ordenadores. La conexin
es muy sencilla, pero un software de gestin de red debe controlar cmo se enva la informacin de
los diferentes usuarios. Actualmente la tcnica utilizada es la denominada packet-switched (conmuta-
cin de paquetes). En esta aproximacin el software de gestin de la red divide la informacin en par-
tes (paquetes) que se van enviando por turnos a lo largo de la lnea. La ventaja es que el sistema es el
que se ocupa de la divisin en paquetes y del ensamblaje posterior de los mismos para generar la infor-
macin original. El sistema de paquetes permite que mltiples comunicaciones entre ordenadores pue-
dan producirse concurrentemente sobre una misma lnea compartida. Para el usuario este proceso es
transparente, el software se ocupa de todo el trabajo.
Las redes basadas en paquetes que abarcan grandes distancias (de pas a pas, por ejemplo) son
diferentes de que las que cubren una pequea distancia, como una habitacin o un edificio. Para dis-
tinguir unas de otras se dividen las redes de computadores en dos tipos, LANs y WANs.

Figura 1.10. Sistemas basados en computador


Sistemas basados en computador 19

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:

.com: negocio comercial


.org: organizacin sin finalidad de lucro
.edu: institucin educativa

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.

1.5.3. LA WORLD-WIDE-WEB (WWW) 7


El web es una forma de intercambiar informacin a travs de Internet. En el web se usan los concep-
tos de hipertexto e hipermedia que podemos definir de la forma siguiente:

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?

Ejercicio 2.* Cmo se almacena la informacin en los ordenadores, con repre-


sentacin analgica o digital?

Ejercicio 3.* Qu diferencias existen entre el cdigo ASCII y el UNICODE?

Ejercicio 4.* Cuntos caracteres se podran representar con 12 bits?

Ejercicio 5.* Por qu unidades est formada la CPU?

Ejercicio 6.* Cuntos bytes son un GB (gigabyte)?

Ejercicio 7.* Qu caractersticas tiene la memoria RAM?

Ejercicio 8.* Qu es la memoria cach?

Ejercicio 9.* Qu tipologa de conexin se suele usar actualmente para conec-


tar varios ordenadores en red?

Ejercicio 10.* Cmo se denomina el software que gestiona las comunicaciones


en Internet?

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 2.7. Algoritmos


2.2. Concepto de programa 2.8. Ingeniera del software
2.3. Lenguajes de programacin 2.8.1. Concepto de Ingeniera del softwa-
2.4. Sintaxis y semntica re
2.5. Estructura de un programa 2.8.2. Ciclo de vida del software
2.6. Errores de un programa
24 Introduccin a la programacin con orientacin a objetos

2.1. INTRODUCCIN

En este captulo se introducen algunos conceptos fundamentales relativos a la programacin y a los


lenguajes de programacin. Adems, se presentan los conceptos bsicos necesarios para el desarrollo
de programas. Se introducen tambin los diferentes tipos de lenguajes de programacin y sus diferen-
cias. En el apartado 2.4 se describen los conceptos fundamentales de sintaxis y semntica, que ayuda-
ran al lector a comprender la diferencia entre la forma de un programa y las tareas que realiza.
Seguidamente, se presentar la estructura general de un programa y los tipos de errores que se pueden
cometer al implementarlo. De esta forma se pretende dotar al lector desde el principio de una visin
de conjunto relativa al problema de la programacin. Dada la importancia del concepto de algoritmo,
este captulo le dedica una seccin donde, adems de definirlo, se describen las tcnicas utilizadas para
su evaluacin. No queremos concluir sin introducir el concepto de Ingeniera del Software, mostran-
do al lector que el desarrollo de software es una actividad profesional, racionalizada, que va ms all
de la simple generacin de lneas de cdigo en un lenguaje dado.

2.2. CONCEPTO DE PROGRAMA

La primera pregunta que podramos plantear al introducir el problema de la programacin, entendi-


da como el desarrollo de programas, sera qu es un programa? Podemos definir un programa como
una serie de instrucciones que indican de forma precisa y exacta al computador qu tiene que hacer
(Kamin et al., 1998). El programa es el medio de comunicacin con el computador. Por medio de l
conseguimos que la mquina utilice sus capacidades para resolver un problema que nos interesa. Este
punto de vista es importante. Un computador se puede entender como una mquina virtual, capaz de
realizar una serie de tareas genricas pero no concretada hacia ninguna tarea especfica. Es siempre
necesario un programa, que usando un lenguaje inteligible por la mquina, le indique qu tiene que
hacer. Para ello, como veremos ms adelante, es necesario saber qu queremos indicar al computa-
dor y cmo hacerlo.
En cierto sentido, un programa modela algo (Arnow y Weis, 1998). Entendemos por modelo una
representacin simplificada de un problema. El modelo considera las caractersticas relevantes del pro-
blema y las representa en el computador. La ciencia y el arte de la programacin consisten en saber
construir un modelo de solucin para un problema dado y en indicar una serie de instrucciones que
permitan describir dicho modelo al computador.
Como ejemplo de programa veamos un caso sencillo en Java (Programa 2.1).

Programa 2.1. Ejemplo de programa en Java

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

Cuando el programa se ejecute imprimir la siguiente lnea de texto:

Donde fueres haz lo que vieres

El Programa 2.1 est formado por varios elementos:


Elementos de programacin y lenguajes 25

a) Un comentario (en la primera lnea) que indica para qu sirve el programa.


b) Llaves, que se utilizan para definir bloques.
c) Definicin de una clase, a partir de la segunda lnea (en los Captulos 5 y 7 se trata el concepto
de clase).
d) Una instruccin, en la cuarta lnea. Esta sentencia indica lo que hay que hacer, en este caso
escribir una frase por pantalla.

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.

2.3. LENGUAJES DE PROGRAMACIN


La comunicacin con el computador se realiza utilizando un lenguaje determinado, un lenguaje de pro-
gramacin. Existen distintos tipos de lenguajes de programacin y, dentro de cada tipo, diferentes len-
guajes. Estos tipos pueden definirse desde diferentes puntos de vista. Una clasificacin tpica agrupa
los lenguajes segn su nivel de abstraccin operativa. Esto implica que sea necesario detallar ms o
menos las operaciones a realizar para desarrollar una tarea concreta. Cuanto menos haya que indicar
al ordenador, de mayor nivel se considera el lenguaje. La clasificacin a la que estamos haciendo refe-
rencia es la siguiente:

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

c) Lenguajes de alto nivel (a veces llamados de tercera generacin)


Se caracterizan porque son independientes de la mquina en la que se usan (generalmente, en la prc-
tica los fabricantes suelen proveer estos lenguajes con algunas capacidades especficas para mquinas
concretas). Estos lenguajes no hacen referencia al funcionamiento de la CPU, sino a tareas ms orien-
tadas al usuario (sumar, restar o multiplicar dos nmeros, por ejemplo). Usan instrucciones que se ase-
mejan al lenguaje ordinario. Cada una de las instrucciones en uno de estos lenguajes equivale
normalmente a varias a nivel de mquina. Como en el caso del ensamblador, es necesario convertir el
programa a lenguaje mquina. A tal efecto existen programas que realizan la traduccin, por lo que el
programador slo tiene que preocuparse del trabajo de escribir su programa en el lenguaje deseado. En
principio, un programa escrito en uno de estos lenguajes puede ejecutarse sobre cualquier ordenador.
Ejemplo de esos lenguajes son: Fortran, Cobol, Pascal, C o Java.

d) Lenguajes de cuarta generacin


Son lenguajes que trabajan a mayor nivel de abstraccin. Suelen incorporan capacidades para la gene-
racin de informes o interaccionar con bases de datos. Se denominan de cuarta generacin, 4GL
(fourth generation languages).
Existe otra clasificacin de los lenguajes de programacin que se basa en el estilo de programa-
cin y que clasifica los lenguajes en,
a) Lenguajes imperativos o procedimentales
b) Lenguajes declarativos

a) Lenguajes imperativos o procedimentales


Son lenguajes basados en la asignacin de valores. Se fundamentan en la utilizacin de variables para
almacenar valores y en la realizacin de operaciones con esos valores.

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.

Figura 2.1. Procesos de compilacin y enlazado (linkado)

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

Figura 2.2. Proceso de compilacin e interpretacin en Java

2.4. SINTAXIS Y SEMNTICA


Estos dos conceptos subyacen en todo lenguaje de programacin correspondiendo al qu puede hacer
el lenguaje de programacin y al cmo indicar que lo haga. Veamos una definicin formal. Segn el
Diccionario de Uso del Espaol de Mara Moliner:

Semntica: Es el estudio del significado de las unidades lingsticas.


Sintaxis: Es la manera de enlazarse y ordenarse las palabras en la oracin o las oraciones
en el perodo.

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.

2.5. ESTRUCTURA DE UN PROGRAMA


En este texto nos vamos a centrar en la programacin desde el punto de vista de los lenguajes imperati-
vos. Desde este punto de vista, consideremos la estructura genrica de un programa. Esto nos propor-
cionar un esquema general que nos servir para irnos introduciendo en la disciplina de la programacin.
Lo primero que debemos indicar es que un programa siempre realiza una o varias tareas. Para
poder llevarlas a cabo se necesita uno o varios algoritmos. El concepto de algoritmo se trata en deta-
lle ms adelante en este captulo, de momento baste saber que un algoritmo puede definirse como:
Elementos de programacin y lenguajes 29

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-

Figura 2.3. Estructura genrica de un programa

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

Figura 2.4. Tipos de programas


30 Introduccin a la programacin con orientacin a objetos

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-

Figura 2.5. Delimitacin del programa principal en Java

mando los otros bloques funcionales.


En particular en Java el programa o seccin principal, es el que permite que el programa comien-
ce a funcionar y se define, delimitado por llaves, como un mtodo denominado main dentro de una
clase 4 como en el ejemplo del programa refrn, vase la Figura 2.5.
En orientacin a objetos la clase es la unidad elemental de organizacin y las tareas que deba
realizar se definen en ella por medio de los denominados mtodos. De momento no entraremos en
detalles sobre clases y mtodos.
En un programa tenemos una serie de instrucciones, denominadas sentencias. stas pueden eje-
cutarse de dos formas: secuencialmente (se van ejecutando una detrs de otra), o de manera no line-

Figura 2.6. Secuencia de ejecucin de instrucciones o flujo lgico de un programa

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.

2.6 ERRORES DE UN PROGRAMA


Al escribir un programa siempre se producen errores. Uno de los puntos clave en el proceso de desa-
rrollo de software es la depuracin de los errores de un programa. Los errores de programacin se pue-
den clasificar en distintos tipos y abordar su eliminacin con un proceso sistemtico de correccin.
Algunos errores los detecta el compilador, otros los debe encontrar el propio programador. La clasifi-
cacin usada organiza los errores en tres tipos:

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.

El proceso de localizacin y correccin de errores se denomina depuracin (debugging 5). Es un


proceso que todo programador debe conocer y realizar en todos los programas. Conocer los tipos de
errores que un programa puede tener ayuda a detectar con mayor facilidad los errores reales de un pro-
grama concreto. Existen algunas estrategias para depurar un programa:

a) Eliminacin de errores de compilacin


Una buena estrategia es acceder al listado de errores y corregirlos en orden, desde el primero hacia el
ltimo. Este orden es el ms til porque muchos de los errores iniciales determinan la existencia de los
siguientes.

b) Eliminacin de los errores de ejecucin


Para ello debemos localizar el punto del programa en el que se produce el error. Esto se hace siguien-
do la traza (flujo lgico del programa) hasta que ste falla. As, siguiendo sentencia a sentencia el pro-

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.

c) Eliminacin de los errores de lgica


Es la tarea ms difcil. Si el programa funciona pero da un resultado errneo, lo mejor es tomar un
ejemplo conocido e ir contrastando los resultados intermedios del ejemplo con los que da el progra-
ma. En este ltimo caso hay que ir, normalmente, consultando los resultados parciales del programa
en puntos concretos del mismo, estableciendo lo que se denomina breakpoints. Un breakpoint (punto
de ruptura) es simplemente un lugar del programa donde nos detenemos para consultar qu es lo que
el programa ha hecho hasta ese momento. Normalmente lo que se hace es consultar el contenido de
las variables que el programa maneja. Los entornos de desarrollo de software integrados modernos
incorporan la capacidad de fijar breakpoints y analizar sentencia a sentencia un programa. Cuando se
localice el primer resultado diferente entre el ejemplo conocido y el programa, probablemente se habr
localizado la causa que produce el error. El siguiente paso es corregirlo.

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-

Tabla 2.1. Comportamiento de cinco algoritmos con distintas complejidades

Tamao mximo manejable (n)


Algoritmo Complejidad
1 segundo 1 hora
1 n 1000 3.6 106
2 n log2 n 140 2.0 105
3 n2 32 1897
3
4 n 10 153
n
5 2 10 21

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.

2.8. INGENIERA DEL SOFTWARE


En la actualidad el desarrollo de software es una actividad de gran inters tanto econmico como
social. Los sistemas software que se desarrollan son complejos y deben ser tan fiables como sea posi-
ble. El desarrollo de software es por tanto una actividad que debe realizarse de forma organizada y
34 Introduccin a la programacin con orientacin a objetos

controlada. Esto que ahora parece evidente no lo ha sido siempre.

2.8.1. CONCEPTO DE INGENIERA DEL SOFTWARE


En la primera poca de las computadoras, el hardware no era potente y por lo tanto el software no poda
ser altamente complejo. En estas condiciones los programas eran desarrollados por personal que aborda-
ba dicha labor sin utilizar tcnicas de trabajo formalizadas. Dicho de otra manera, el desarrollo de soft-
ware tena mucho de arte, dependiendo su calidad final de la experiencia y habilidad del programador.
En este contexto se dice que el desarrollo de software era artesanal. A mediados de los aos sesenta del
siglo XX el hardware disponible comenzaba a ofrecer mayores posibilidades. Los sistemas software que
se comenzaban a desarrollar eran sistemas donde la complejidad era ya un factor a tener en cuenta. Al
abordar el desarrollo de estos sistemas grandes y complejos con las tcnicas artesanales, el resultado era
que las planificaciones de tiempo y coste se quedaban muy cortas, rebasndose ampliamente las predic-
ciones originales. La conclusin estaba clara: las tcnicas artesanales no servan para tratar el desarrollo
de sistemas complejos. El problema desde el punto de vista del desarrollo de software era muy impor-
tante, por lo que desat mucho inters y trabajo. La falta de control sobre el desarrollo de sistemas soft-
ware se denomin crisis del software, denominacin que se sigue usando hoy en da (Gibbs, 1994).
El problema de la crisis del software era de tal magnitud que en la reunin del Comit Cientfico de
la OTAN realizada en 1968 se estudi el problema y se propuso una solucin. La solucin consista en
tratar el software (y en particular su desarrollo) con las tcnicas ingenieriles usadas por las industrias de
productos fsicos. Este ideal (an no alcanzado) se denomin ingeniera del software. La ingeniera del
software se puede definir como la aplicacin de una aproximacin sistemtica, disciplinada y cuantifi-
cable al desarrollo, operacin y mantenimiento del software (Pressman, 2002). La idea es aplicar al soft-
ware la filosofa ingenieril que se aplica al desarrollo de cualquier producto. La ingeniera del software
pretende el desarrollo de software de manera formal, cumpliendo unos estndares de calidad 6.
El punto de vista de la ingeniera del software trasciende a la mera programacin. El software se
considera dentro de un ciclo de vida que va desde la concepcin del producto, pasando por las etapas
de anlisis del problema, diseo de una solucin, implementacin de la misma y mantenimiento del
producto, hasta su final retirada por obsolescencia.

2.8.2. CICLO DE VIDA DEL SOFTWARE


Un concepto fundamental en ingeniera del software es la consideracin de un producto software en el

Figura 2.7. Esquema genrico del ciclo de vida de un producto software

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.

a) Anlisis. En la etapa de anlisis se especifica el qu queremos. En este apartado se determinan los


requisitos que deber cumplir el programa de cara al usuario final. Tambin se imponen las con-
diciones que debe satisfacer el programa, as como las restricciones en el tiempo de desarrollo. La
persona o personas que encargan el desarrollo del software imponen los requisitos iniciales. stos
suelen ser incompletos o ambiguos. El equipo de desarrollo de software debe trabajar para refinar
esos requisitos hasta que todas las caractersticas clave estn bien prefijadas. En esta etapa se rea-
liza una determinacin de requisitos y su posterior organizacin, para acabar obteniendo un mode-
lo lgico del sistema software a construir. Es fundamental entender y recoger todos los requisitos
de nuestra herramienta. Un error en esta etapa se transmite a las siguientes y cuanto ms tarde se
corrija ms esfuerzo implicar. Para estas tareas existen tcnicas formales basadas en diagramas
de distintos tipos. Aunque no sea especficamente orientado a objetos, el texto de Yourdon sobre
tcnicas de anlisis y modelado es una referencia obligada (Yourdon, 1989).
b) Diseo. En esta etapa se determina cmo se consiguen realizar los requisitos recogidos y

Figura 2.8. Etapas del ciclo de vida del software

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

Figura 2.9. Proporcin entre el esfuerzo de desarrollo y el de mantenimiento en el ciclo


de vida del software

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 2.* Qu tipo de lenguaje es ms rpido, el compilado o el interpreta-


do? Por qu?

Ejercicio 3.* Qu tipos de errores se pueden dar en un programa?

Ejercicio 4.* Qu tipo de error ha sucedido si el programa ha compilado bien


pero el resultado es incorrecto?

Ejercicio 5.* Qu tcnica de debugging utilizara para detectar un error lgico?

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 8.* En qu etapa se determinan los requisitos y las restricciones?


Ejercicio 9.* Qu algoritmo es ms complejo, el A con una complejidad tempo-
ral n3 o el B con una complejidad temporal de n?

Ejercicio 10.* Cuntos subprogramas hay en un programa monoltico?

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 3.5. Operadores


3.2. Conceptos generales 3.5.1. Operadores aritmticos
3.3. Tipos de datos 3.5.2. Operadores de incremento
3.3.1. Tipos de datos primitivos y decremento
3.3.2. Variables y constantes 3.5.3. Operadores relacionales
3.3.3. Representacin interna de datos 3.5.4. Operadores lgicos
3.3.4. Conversiones de tipo 3.5.5. Operadores de asignacin
3.4. Instrucciones
3.4.1. Instrucciones de asignacin
3.4.2. Instrucciones de entrada/salida
3.4.3. Instrucciones de ramificacin
40 Introduccin a la programacin con orientacin a objetos

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.

3.2. CONCEPTOS GENERALES


En un lenguaje de programacin se puede definir un programa como un conjunto de sentencias, y una
sentencia como una asercin matemtica o lgica, o una frase o conjunto de frases informativas. Las
sentencias pueden ser de distintos tipos:

a) De especificacin o declaracin: No implican una operacin matemtica o lgica.


b) Ejecutables: Implican una operacin matemtica o lgica.
c) Comentario: Informativas, ignoradas por el computador.

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.

3.3. TIPOS DE DATOS


Cualquier programa, independientemente del lenguaje usado, se puede entender como un conjunto de
operaciones que se aplican a ciertos datos en una cierta secuencia (Pratt y Zelkowitz, 1996). La dife-
rencia bsica entre los lenguajes se refiere a los tipos de datos permitidos, a los tipos de operaciones
disponibles y a los mecanismos provistos para el control de la secuencia en la que se aplican las ope-
raciones a los datos. Estos tres conceptos, datos, operaciones y control, forman el marco de discusin
y comparacin de lenguajes. Un concepto de importancia capital en programacin es el de tipo de dato.
Los datos que se manejan en un programa estn organizados ms o menos rgidamente en diferentes
categoras (los tipos). Sobre cada tipo se pueden realizar determinadas operaciones que no tienen por
qu ser equivalentes de un tipo a otro. El concepto de dato est muy relacionado con las operaciones
que se pueden realizar sobre l. En el ordenador, los diferentes tipos se representan de forma diferen-
te, y por lo tanto, para l son distintos. Un ejemplo tpico de tipo de dato simple es el tipo entero
(nmero entero) o real (nmero real). Internamente se representan de forma diferente y dependiendo
del lenguaje puede ser ms o menos fcil asignar un tipo a otro (convertir de uno a otro). Los lengua-
Introduccin a la programacin 41

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

3.3.1. TIPOS DE DATOS PRIMITIVOS


Los tipos de datos primitivos son prcticamente los mismos en todos los lenguajes de programacin. La
clasificacin tpica se recoge en la Figura 3.1. Esta clasificacin bsica se particulariza en cada lenguaje
de programacin. En concreto en Java, la clasificacin genrica da lugar a ocho tipos primitivos de datos:

a) 4 tipos de enteros.
b) 2 tipos de reales.
c) 1 tipo carcter.
d) 1 tipo lgico.

Figura 3.1. Organizacin tpica de los tipos primitivos de datos


42 Introduccin a la programacin con orientacin a objetos

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.

Tabla 3.1. Tipos de datos enteros

Nombre Memoria usada (bits) Valor mnimo Valor mximo


byte 8 2128 127
short 16 232768 32767
int 32 22147483648 2147483647
long 64 <29.22 1018 >9.22 1018

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.

Tabla 3.2. Tipos de datos reales

Nombre Memoria usada (bits) Valor mnimo Valor mximo


float 32 . 23.4 10 38
.3.4 10 38
double 64 . 21.7 10 308 .1.7 10 308

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

3.3.2. VARIABLES Y CONSTANTES


Como se ha comentado anteriormente, en los programas se manejan datos. Es necesario, por lo tanto,
disponer de un mecanismo que permita el almacenamiento y la manipulacin de los datos. Estas labo-
res se realizan en un programa por medio de las entidades denominadas variables y constantes. Ana-
licemos cada una de ellas.

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;

(en Java el ; sirve para indicar el fin de sentencia)


Por ejemplo,

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,

int total, cuenta, suma;

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:

int total = 0, cuenta = 20;


float precioUnitario = 49.32;

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

final tipo_de_dato nombre_de_la_variable;


Un ejemplo sera:

final double e=2.718281828;

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.

Programa 3.1. Ilustracin del alcance de las variables en Java

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.

3.3.3. REPRESENTACIN INTERNA DE DATOS


Como ya hemos indicado, toda la informacin, incluyendo los datos de los diferentes tipos, se repre-
senta en el ordenador usando cdigo binario. Por ejemplo, qu quiere decir la cadena de bits
00101100101? No es posible saberlo slo con mirar la cadena. Para poder interpretar una cadena bina-
ria tenemos que poder descodificarla 7 de acuerdo a algunas reglas. Hay reglas para codificar y alma-
cenar cada tipo primitivo. Es interesante conocer cmo se realiza la codificacin de los tipos primitivos
usando cdigo binario. Vamos a considerar este punto a continuacin, analizando cmo se codifica
cada uno de los tipos primitivos en Java.

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-

Figura 3.2. Representacin de enteros con signo

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:

1: Se pone un 9 por cada dgito de nuestro nmero y a esto se resta el nmero

299
263
236

2: se suma 1

136
101
137

Por tanto, el complemento a 9 de 63 es 37.


La ventaja de los complementos es que es posible transformar las restas en sumas, esencialmente
sumando el minuendo con el complemento del sustraendo.
En binario se trabaja con el complemento a dos. En caso de querer convertir un numero binario a
su complemento a dos, las operaciones anteriores se transforman en una serie de operaciones senci-
llas. Para hacer el cambio basta con cambiar 1 por 0 y 0 por 1 en el nmero y sumar 1 al resultado. La
traduccin a complemento a dos con signo se realiza de la siguiente manera:

Un valor positivo es un nmero binario directamente.


Un valor negativo se representa invirtiendo todos los bits del correspondiente valor positivo y
aadindole 1.
Para descodificar un valor negativo, se invierten todos los bits y se suma 1.

Por ejemplo, evaluemos el complemento a dos de 101010 (binario):

1: Se pone 1 por cada dgito y al resultado se le resta el nmero

2111111
2101010
2010101

2: se suma 1

1010101
1000001
1010110 <....... complemento a dos

Usando la receta abreviada:

1: Invertir 1 y 0: 010101.
2: Sumar 1. Es el mismo caso que en el paso 2o anterior, obtenemos 010110

Para invertir el signo se hace lo siguiente:

El nmero 25 se representa en 8 bits (un byte) como


48 Introduccin a la programacin con orientacin a objetos

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:

1. Representacin de punto flotante de 129,34 (129.34 usando el punto decimal)


11*12934110 22

Aqu, signo= 11, mantisa= 12934, y exponente= 22

2. Representacin de punto flotante de 2843,977


21 * 843977 * 10 23

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:

signo * mantisa * 2 exponente

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

Tipo signo mantisa exponente


simple (32 bits) 1 bit 23 bits 8 bits
doble (64 bits) 1 bit 52 bits 11 bits
Introduccin a la programacin 49

simple o doble el convenio mostrado en la Tabla 3.3.


Los valores en punto flotante se almacenan guardando cada uno de los tres componentes en el
espacio asignado.
En la norma IEEE 754 hay tres valores especiales que se pueden representar: infinito positivo
(positive infinity), infinito negativo (negative infinity) y no nmero (Not a Number). Este ltimo apa-
rece como NaN.
Como hemos visto, los valores numricos se representan usando un formato finito, un nmero
determinado de bits. Por ello, podemos decir que no tenemos nmeros enteros o reales, sino una simu-
lacin de nmeros enteros o reales. Al usar un formato finito para esa simulacin hay siempre un valor
mximo y uno mnimo que se pueden representar en un tipo numrico de datos. Intentar almacenar un
valor que caiga fuera de ese intervalo 8, en el mejor de los casos produce resultados incorrectos y en el
peor, un error. Las dos situaciones se denominan Overflow y Underflow:

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;

almacenar el carcter F en la variable car (valor Unicode de F, 70).


Otro ejemplo sera una letra en maysculas a la que se le suma 32 (diferencia entre A y a). Se
conseguir la letra equivalente en minsculas.

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

3.3.4. CONVERSIONES DE TIPO


En un programa es habitual cambiar de tipo de datos. Esto quiere decir que un determinado valor alma-
cenado en una variable de un cierto tipo lo queremos colocar en otra variable de tipo diferente. A tal
efecto, en todos los lenguajes hay algn, o algunos, mecanismos para realizar la conversin. Lo ms
importante es no perder informacin en la transformacin. Por ejemplo, supongamos que tenemos una
variable entera de tipo short (16 bits) que almacena el valor 1000 y queremos convertirla a tipo byte
(8 bits). Como 1000 no se puede representar con 8 bits la conversin nos dar un resultado que no
corresponde con el valor original.
No todas las conversiones entre los distintos tipos son posibles. Por ejemplo, en Java los valores
lgicos (booleanos) no se pueden convertir a ningn otro tipo de dato y viceversa. Incluso aunque una
conversin sea posible, debemos tener cuidado en no perder informacin en el proceso. Desde un pun-
to de vista general, las conversiones de tipo se clasifican en dos categoras:

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-

Tabla 3.4. Conversiones de ensanchamiento

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

Tabla 3.5. Conversiones de estrechamiento

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

tipos en Java se muestran en la Tabla 3.5.


Debido al signo, la conversin de byte y short a char se considera de estrechamiento, aunque
un byte o un short ocupen menos o igual nmero de bits que el tipo char. Un byte o un short
usan un bit como signo y el char usa 16 bits sin signo. Por eso, un entero negativo (con signo) se con-
vierte en un carcter (sin signo) que no tiene relacin con el entero que representaba. La conversin
pasa directamente los 8 bits o los 16 bits de entero, incluyendo el bit de signo, a carcter y no los 7
15 bits que son los que realmente representan el nmero en valor absoluto.
Recordemos que los valores booleanos (lgicos) no pueden convertirse a otro tipo de datos.
Como hemos visto, hay conversiones por promocin o por estrechamiento pero, cmo se realizan
las conversiones de tipo? Las conversiones de tipo se realizan por tres mecanismos diferentes:

a) Conversin por asignacin.


b) Conversin por promocin aritmtica.
c) Conversin con moldes (casting).

Consideremos cada una de ellas por separado:

a) Conversin por asignacin


Se realiza cuando un valor de un tipo determinado se asigna a una variable de otro tipo. Se pro-
duce una promocin automtica. Por ejemplo, si dinero es una variable de tipo float y euros es
una variable de tipo int (que almacena el valor 82), entonces en la sentencia
dinero = euros;

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

resultado = suma / contador;

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

Figura 3.3. Asignacin de valores a una variable

int total, contador;


float resultado;
resultado=(float) total/contador;

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.

3.4.1. INSTRUCCIONES DE ASIGNACIN


La o las instrucciones de asignacin sirven para dotar de valor a las variables. Lo que se hace es colo-
car un valor dado en la porcin de memoria simbolizada por el identificador de la variable. En Java (y
en otros muchos lenguajes) la asignacin se representa por el smbolo de igualdad 5 . As, para asig-
nar el valor 5.0 a una variable real llamada total haramos:
total=5.0;
54 Introduccin a la programacin con orientacin a objetos

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.

Programa 3.2. Programa simple en Java

1 class Ejemplo {

2 // Ejemplo de la estructura de un programa en Java

3 public static void main(String [] args) {

4 // Declaracin de variables

5 double total, suma;


6 total=10.0;
7 suma=5.0;

8 // Operacin
9 total=total+suma;

10 } // Fin mtodo main


11 } // Fin clase Ejemplo

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.

Figura 3.4. Representacin simblica de la entrada/salida en Java


Introduccin a la programacin 57

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.

// comentarios que van hasta fin de lnea

/* comentarios que van hasta el smbolo de terminacin, incluso a


travs de saltos de lnea */

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.

3.4.2. INSTRUCCIONES DE ENTRADA/SALIDA


Los programas necesitan aceptar entradas de datos y producir salidas de datos. Sin embargo, la fuente
de la entrada o el destino de la salida puede variar. Frecuentemente se lee de ficheros y se escribe en
ficheros. Otras veces se acepta la entrada desde el teclado y la salida se realiza por el monitor. En cual-
quier caso, para poder leer o escribir, los lenguajes de programacin proveen de algn mecanismo. Exis-
ten instrucciones para lectura/escritura que muchas veces forman parte del lenguaje. En Java (en el

Tabla 3.6. Corrientes de lectura y escritura estndar en Java

Corriente (Stream) Propsito Dispositivo (defecto)


System.in lectura teclado
System.out escritura monitor
System.err salida de errores monitor

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

manejen formalmente de la misma manera, independientemente de dnde proceda la entrada o a dn-


de vaya la salida. Por ejemplo, la entrada podra ser desde un fichero, desde el teclado o desde una
conexin a Internet.
Originalmente, en Java 1.0 los streams eran entidades de 8 bits. Estas corrientes de entrada y sali-
da de 8 bits se denominan:

InputStream: Corrientes de entrada de 8 bits.


OutputStream: Corrientes de salida de 8 bits.

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:

Reader: Corrientes de entrada de caracteres Unicode (16 bits)


Writer: Corrientes de salida de caracteres Unicode (16 bits).

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,

Tabla 3.7. Algunas secuencias de esca-

Secuencia de escape Significado


\t Tabulador
\n Lnea nueva
\ Comilla simple
\ Comilla doble
\\ Barra invertida

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:

Sin salto de linea i: 1 j: 3


Con salto de linea
i: 1
j: 3

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.

Programa 3.4. Ilustracin del uso de secuencias de escape

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

short numero = 32766; // <..... El tipo short solo puede


// representar hasta 32767

System.out.println(numero: + numero); //<... 32766


numero=(short) (numero+1);
System.out.println(numero: + numero); //<... 32767
numero=(short) (numero+1); // <.... Se suma 1

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

System.out.println(numero: + numero); //<.... -32768


numero=(short)(numero+1);

System.out.println(numero: + numero); //<.... -32767

} // Fin mtodo main


} // Fin clase Overflow

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

Si usramos la clase DataInputStream no habra problema respecto a la conversin de sistemas


de caracteres. Sin embargo, puede haber problemas relativos a la no estandarizacin de cmo se repre-
senta la informacin (convencin big or little endian para el almacenamiento de datos 12). Para la lec-
tura de datos estndar, en este libro vamos a usar las corrientes de 16 bits.
Para trabajar con 16 bits debemos usar una clase de tipo Reader. Lo que debemos tener claro es que de
esta forma obtenemos una corriente de caracteres que primero habr que leer y, en su caso, convertir a for-
mato numrico. En el Programa 3.6 vamos a ilustrar cmo realizar la lectura de una cadena de caracteres.

Programa 3.6. Lectura por teclado de una cadena de caracteres

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

mensaje=leer.readLine(); // Leyendo una lnea del teclado


System.out.println(Mensaje: +mensaje);

} // Fin mtodo main


} // Fin clase Lectura

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:

BufferedReader leer = new BufferedReader


(new InputStreamReader (System.in) );

El mtodo constructor 13 de BufferedReader recibe como argumento un objeto de clase


InputStreamReader creado con el constructor de esta clase, que a su vez recibe como argumento la
entrada estndar System.in. Estamos creando el objeto leer con las propiedades adecuadas de entra-
da-salida. La idea es conectar (adaptar) la entrada estndar (System.in), que es de 8 bits, con una
corriente de entrada de 16 bits, que es la InputStreamReader. A su vez, se conecta la corriente de
16 bits con otra corriente que provee de buffer de lectura, la BufferedReader. Con esta clase Buffe-
redReader se crea un objeto que llamamos leer. Es importante indicar que el nombre del objeto, leer
en este caso, es arbitrario, pudiendo escogerse cualquier identificador. El nuevo objeto puede usar el
mtodo readLine() que lee una cadena de caracteres introducida por teclado. Desde un punto de vista
abstracto podemos entender el objeto leer como el equivalente al teclado dentro de nuestro programa.
De momento y hasta que se exponga la definicin de clases y creacin de objetos en el Captulo 7, el lec-
tor puede considerar la creacin del objeto leer como una indicacin genrica, una receta, para lectura
desde el teclado.
Tras construir el objeto leer se declara una cadena de caracteres. No existe tipo primitivo para
cadenas de caracteres sino una clase, la clase String. Debido a que el manejo de cadenas es tan habi-
tual, la clase String permite construir objetos con la misma sintaxis que si de un tipo primitivo se tra-
tase, como vemos en la creacin del objeto mensaje. A continuacin, el programa imprime con
print una frase preguntando por la cadena a introducir. El siguiente paso es leer una cadena que intro-
duzcamos por teclado. Esto se consigue invocando el mtodo readLine del objeto leer haciendo
leer.readLine(). El resultado de la lectura se almacena en mensaje, escribindose posteriormen-
te con un println(). El resultado sera:

Introduzca una cadena de caracteres: Hola


Mensaje: Hola
Introduccin a la programacin 63

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:

a) Leer la cadena que contiene los dgitos numricos.


b) Convertir a formato numrico.

El punto a) se lleva a cabo con el mtodo readLine() de la clase BufferedReader. Este


mtodo lee una lnea entera hasta el retorno de carro. El punto b) se lleva a cabo con el mtodo
parseTipo_de_dato() que corresponda. Por ejemplo, parseInt() para la clase contenedora
Integer o parseDouble() para la clase contenedora Double. Ilustremos la tcnica con un progra-
ma, vase el Programa 3.7.

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;

BufferedReader leer =new BufferedReader


(new InputStreamReader(System.in));

System.out.println(Introduzca primer numero:);

a=Integer.parseInt(leer.readLine()); /*Lectura y
conversin a
entero */

System.out.println(Introduzca segundo numero:);


b=Integer.parseInt(leer.readLine());

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.

Programa 3.8. Programa para convertir de grados centgrados a Fahrenheit

import java.io.*;
64 Introduccin a la programacin con orientacin a objetos

class Fahrenheit {

// Programa para la conversin de grados centgrados a


// Fahrenheit. La relacin es grados_F = grados_C*9/5+32

public static void main(String[] args) throws IOException {

// Declaracin de variables
double grados_C, grados_F;
final double NUEVE_QUINTOS=9.0/5.0;
final double TREINTAYDOS=32.0;

BufferedReader leer =new BufferedReader


(new InputStreamReader (System.in));

// Entrada de datos
System.out.println (Introduzca la temperatura en grados
+ centigrados:);
grados_C=Double.parseDouble(leer.readLine());

// Aplicacin del algoritmo de conversin


grados_F=grados_C*NUEVE_QUINTOS+TREINTAYDOS;

// Salida de informacin
System.out.println (La temperatura en Fahrenheit es:
+grados_F + F);

} // Fin mtodo main


} //Fin clase Fahrenheit

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.

3.4.3. INSTRUCCIONES DE RAMIFICACIN


Hasta ahora slo hemos visto la secuencia de operaciones, una detrs de otra, es decir, que la ejecu-
cin del programa procede de manera lineal. Los ejemplos de programas que hemos mostrado comen-
zaban ejecutndose en la primera lnea del mtodo main() e iban paso a paso hasta el final del mismo.
Sin embargo, en un lenguaje de programacin es posible realizar ramificaciones del flujo de control.
Los lenguajes de programacin proveen de dos formas tpicas de realizar las bifurcaciones:

a) Por medio de condiciones.


b) Por medio de bucles.

Consideremos cada una de estas posibilidades.

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

Si la condicin es verdadera, se realiza el primer bloque y si no lo es, se realiza el segundo. Los


dos bloques son excluyentes, se ejecuta uno u otro pero no los dos. La clusula else (en ingls sino)
es opcional, se puede usar slo el if como en el caso siguiente,

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:

valor1 == valor2 (valor1 igual a valor2)

Esta condicin podra aparecer en un if 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.

Programa 3.9. Programa que muestra la instruccin condicional 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

} // Fin mtodo main


} // Fin clase Condicion

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;

// Ejemplo de bucle while controlado por contador

while (i<=FIN) {
System.out.println(Valor del contador: +i);
Introduccin a la programacin 67

Tabla 3.8. Operadores aritmticos en

Operacin realizada Operador


Suma 1
Resta 2
Multiplicacin *
Divisin /
Resto %

i=i+1; // Incremento del contador


}
} //Fin mtodo main
} // Fin clase Contador

La salida sera:

Valor del contador: 0


Valor del contador: 1
Valor del contador: 2
Valor del contador: 3
Valor del contador: 4

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.

3.5.1. OPERADORES ARITMTICOS

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.

c) Supongamos que cociente_entero es entero y N,M son enteros. Entonces si hacemos:


N=9;
M=4;
cociente_entero=N%M;

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.

d) Supongamos que cociente_entero es entero y N,M son enteros. Entonces, haciendo:


N=29;
M=4;
cociente_entero=N%M;
obtendramos el valor 21 (el resto del cociente con el signo del numerador).
e) Supongamos que cociente_entero es entero y N,M son enteros. Entonces si hacemos:
N=6;
M=7;
cociente_entero=N%M;
obtendramos el valor 6. ste es el resultado cuando el numerador es menor que el denominador.

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)

y dentro de cada grupo la precedencia es la misma. Ilustremos el problema de la precedencia de ope-


radores con algn ejemplo. Consideremos la expresin,

514/3

Cmo se interpretara? Hay dos posibilidades,

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,

Tabla 3.9. Operadores de incremento


y decremento en Java

Operacin realizada Operador


Incremento en una unidad 11
Decremento en una unidad --

5112/5210%3

teniendo en cuenta la precedencia y la evaluacin de izquierda a derecha, el orden en el que se apli-


caran los operadores sera:

/, %, 1, 2

Con esto, el resultado paso a paso sera,

a) 5 1 2 (cociente entero de 12/5)210%3.


b) 51221 (resto entero de 10 entre 3).
c) 721.
d) El resultado final sera: 6.

Es muy importante destacar que en un lenguaje de programacin la precedencia se puede alterar


usando parntesis. Las expresiones entre parntesis se evalan como un bloque. Los parntesis se pue-
den anidar y siempre se evaluarn las expresiones desde el parntesis ms interno hacia el ms exter-
no. Es recomendable usar parntesis en expresiones complicadas, incluso aunque no sean
estrictamente necesarios, porque as queda ms claro cmo se evaluarn las expresiones. Para que una
expresin sea sintcticamente correcta debe tener el mismo nmero de parntesis izquierdos que dere-
chos y deben estar correctamente anidados. Ilustremos cmo es posible alterar la precedencia en el
70 Introduccin a la programacin con orientacin a objetos

ltimo ejemplo considerado anteriormente. Sea ahora la expresin,

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

almacena 1 en la variable a. Es decir, almacena el resultado final de las operaciones.

3.5.2. OPERADORES DE INCREMENTO Y DECREMENTO


Estos operadores son unarios (monarios) que suman (incremento) o restan (decremento) una unidad a
un operando entero o real. La sintaxis se recoge en la Tabla 3.9.
Por ejemplo, las sentencias,

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

Sentencia Resultado en valor Resultado en contador


valor=++contador 6 6
valor=contador++ 5 6
valor=--contador 4 4
valor=contador-- 5 4

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;

11contador equivale a contador11


--valor equivale a valor--

Usando el operador de incremento o decremento sobre una variable, la variable se incrementa o


Introduccin a la programacin 71

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

Es decir, en valor=contador11, primero se realiza la asignacin y despus se incrementa la


variable contador. sta es la forma de actuar del operador incremento o decremento como sufijo. El
incremento o decremento se realiza despus de usar el valor de la variable en la expresin. Por el con-
trario, para el caso del prefijo tendramos el siguiente comportamiento,

contador=5;
valor=++contador;
System.out.println(valor: +valor);
System.out.println(contador: +contador);

el resultado ahora sera:

valor: 6
contador: 6

Ahora, en la sentencia valor=++contador se incrementa primero contador y luego se usa su


valor. Actuando el operador como prefijo, la variable se incrementa en la expresin antes de usarse.
Como vemos, la variable contador siempre queda incrementada al final, pero el resultado que se alma-
cena en valor depende del orden del operador. Como resumen, veamos en la Tabla 3.10 el resultado
de la aplicacin de los operadores de incremento o decremento como prefijos y sufijos en el caso de
contador=5 como valor inicial.
A continuacin, se muestra otro ejemplo que refleja la diferencia de usar los operadores de incre-
mento o decremento como prefijo o sufijo. Si la variable suma contiene 30, entonces la sentencia

System.out.println (suma++ + + ++suma +


+ suma + + suma--);

imprimir:

Tabla 3.11. Operadores relacionales


en Java

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

y suma contendr 31 despus de que se complete la lnea.


Es interesante indicar que como los caracteres internamente se manejan como enteros, los opera-
dores de incremento y decremento se pueden aplicar a caracteres. En este caso, el operador incremen-
to cambia el valor de la variable al siguiente carcter en el conjunto Unicode. Si se aplica el
decremento, se obtiene el carcter Unicode anterior. Las versiones sufijo y prefijo tienen el mismo
efecto que en valores aritmticos como podemos observar en el Programa 3.11.

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

Tabla 3.12 Operadores lgicos en Java

Significado Operador
y lgico &&
o lgico ||
no lgico !

System.out.println(letra es igual a:+ letra++ + letra--);


} //Fin del main
}//Fin de la clase

La salida del programa es:

letra es igual a: a
letra es igual a: b

Tabla 3.13. Tabla de verdad del y

X Y X&&Y
V V V
V F F
F V F
F F F

letra es igual a: bc

Si se aade la sentencia System.out.println (letra es: +letra); al final del progra-


Introduccin a la programacin 73

ma, la salida que se habra obtenido sera:

letra es igual a: a
letra es igual a: b
letra es igual a: bc
letra es: b

3.5.3. OPERADORES RELACIONALES


Como hemos visto al hablar de la condicin (if), se puede evaluar una condicin produciendo un
resultado lgico (true o false). Muy frecuentemente la condicin expresa una relacin entre dos
entidades, tal como su igualdad. Para expresar las diferentes relaciones existen una serie de operado-
res denominados relacionales. Dichos operadores, y su sintaxis en Java, se recogen en la Tabla 3.11.

Tabla 3.14. Tabla de verdad del o lgi-

X Y XY
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.

3.5.4. OPERADORES LGICOS


En relacin con las operaciones lgicas hemos presentado en el apartado anterior los operadores rela-
cionales (igual, mayor, menor, diferente, mayor e igual y menor e igual). Existen algunos operadores
ms, relacionados con las expresiones lgicas. Son los denominados operadores lgicos y permiten
aplicar el lgebra de Boole.

Tabla 3.15. Tabla de verdad del no

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

El resultado tambin sera:

La condicion se cumple

c) Por ltimo, encontramos la tabla de verdad del no lgico en la Tabla 3.15.


En este caso se consigue la negacin lgica.
Debe tenerse en cuenta que las comparaciones no tienen en absoluto por qu involucrar valores
numricos, aunque es tremendamente frecuente. Se puede trabajar directamente con valores lgicos,
como en el siguiente ejemplo ilustrativo del no lgico,
boolean etiqueta=true;

if (!etiqueta) {
System.out.println (La condicion se cumple);
}
else {
System.out.println (La condicion no se cumple);
Introduccin a la programacin 75

Tabla 3.16. Operadores de asignacin en

Operador Ejemplo Equivalencia


15 a15b a5a1b
25 a25b a5a2b
*5 a*5b a5a*b
/5 a/5b a5a/b
%5 a%5b a5a%b

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:

! > && > ||

Un ejemplo tpico de uso incorrecto de operadores lgicos es el siguiente:

if (a==3 || 4) {
---- bloque de acciones ----
}

En Java en particular, esto producira un error de compilacin, pues 4 es un literal entero y no se


puede convertir a tipo lgico para establecer la relacin con el resultado de a 55 3 (que s es un resul-
tado lgico). La forma correcta sera,

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,

if (x!=0 && x!=4) {


---- bloque de acciones ----
}

si x es igual a cero el && ya no puede cumplirse y no se evala la segunda condicin.

3.5.5. OPERADORES DE ASIGNACIN

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

esa misma variable. En Java, los operadores de asignacin ms frecuentes se recogen en la

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.

Ejercicio 7.* Cul es el resultado del siguiente programa?

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

donde pKa es el menor logaritmo decimal de Ka, log representa el


logaritmo decimal y [HA] es la concentracin molar (moles/litro) del
cido.
Escriba un programa en Java que acepte la constante de acidez de
un cido dbil y luego pregunte por la concentracin de la disolu-
cin, evaluando el pH. El programa debe solicitar un valor de con-
centracin tras cada clculo, hasta que el usuario indique que no
desea calcular el pH de ninguna nueva disolucin.

Ejercicio 9.* Indique cul es la salida del siguiente programa:

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

Ejercicio 10.* Escriba un programa que calcule la frecuencia, v (s-1), de oscilacin


de un pndulo dada su masa m y longitud l. La expresin corres-
pondiente es
1
v5 } !g}
Ejercicio 10.* donde g es la aceleracin normal de la gravedad en el campo gravi-
tatorio terrestre (9.8 m/s2). Aunque la frecuencia es independiente
de la masa, consideremos que si sta es mayor de 1 kg, el hilo del
que cuelga la misma se romper. El programa debe distinguir esta
situacin y calcular la frecuencia slo si m<1 kg.

Ejercicio 11.* Indique cul es la salida del siguiente programa:

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 4.5. Tcnicas de representacin


4.2. El salto incondicional 4.5.1. Diagramas de flujo de control
4.3. El teorema de estructura 4.5.2. Pseudocdigo
4.4. Estructuras de control elementales 4.5.3. Diagramas de accin
4.4.1. Secuencia
4.4.2. Seleccin
4.4.3. Iteracin
80 Introduccin a la programacin con orientacin a objetos

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

Mejorar la fiabilidad de los programas.


Mejorar la legibilidad de los programas.
Minimizar la complejidad de los programas.
Simplificar el mantenimiento de los programas.
Incrementar la productividad de los programadores.
Proveer de una metodologa de programacin disciplinada.

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.

4.2. EL SALTO INCONDICIONAL


En la mayora de los lenguajes existe una sentencia de salto incondicional que normalmente se deno-
mina sentencia goto y cuya sintaxis genrica es:
goto etiqueta

donde etiqueta es un identificador numrico o alfanumrico que corresponde a una sentencia en


concreto. El efecto del goto es producir un salto desde donde l se encuentra hasta la sentencia mar-
Programacin estructurada 81

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

El operador .NE. es un operador relacional que significa NonEqual (distinto). En el ejemplo, si


A es distinto de 1 se produce un salto a la sentencia marcada con la etiqueta 10. Si no es as, se hace
A=A+1 y luego saltamos a la sentencia con la etiqueta 20 donde con el Write se imprime la variable A.
El salto a la sentencia con etiqueta 20 evita pasar por la sentencia marcada con la etiqueta 10. Como
podemos ver, el efecto del fragmento de cdigo anterior es equivalente a un if-else. Es tambin fcil
entender que un programa escrito a base de saltos incondicionales tiene una lgica complicada. Tanto
es as que en la jerga de programacin a los programas escritos de esta forma se los denomina spa-
guetti-like (de tipo espagueti).
A pesar de todo lo que se ha escrito en contra del goto todos los lenguajes incorporan esta sen-
tencia, o algn tipo de salto incondicional ms o menos controlado. Java no soporta la instruccin
goto. Sin embargo, sta es una palabra reservada, de forma que el compilador puede detectar cual-
quier uso errneo de este identificador. Esto no quiere decir que Java no incorpore otras formas de rea-
lizar el salto incondicional. Existen dos sentencias que realizan un salto incondicional ms o menos
controlado, las sentencias break y continue. Son sentencias para alterar el flujo de control produ-
ciendo un salto incondicional. Se podra decir que son gotos disfrazados.
La sentencia break se usa en los switch (sentencia de seleccin mltiple que ms adelante vere-
mos). El break produce un salto al final del switch y el flujo de control salta a la sentencia que se
encuentra despus de la llave que cierra el switch. Tambin puede usarse en bucles. Su efecto es el
de finalizar la ejecucin del bucle, ejecutndose la sentencia que hay despus del mismo. En un bucle
produce un salto incondicional al final del mismo. De esa forma salimos del bucle y continuamos en
la sentencia siguiente. Como ejemplo consideremos el fragmento de cdigo ilustrado en el Progra-
ma 4.1. Cuando dato sea igual a 12 el flujo de control salta fuera del bucle while y se ejecuta la sen-
tencia siguiente al bucle que es System.out.println (Fuera del bucle);.

Programa 4.1. Ilustracin de salto incondicional

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

Programa 4.1. Ilustracin de salto incondicional (continuacin)


System.out.println(\ndato: +dato);
num++;
} // Fin del bucle
System.out.println(\nFuera del bucle);
} //Fin del main
}//Fin de la clase

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.

Programa 4.2. Ejemplo de reestructuracin de cdigo

import java.io.*;
class Ejercicio {
public static void main(String [] args) throws IOException {
int num=1;
int dato=0;

BufferedReader leer = new BufferedReader


(new InputStreamReader(System.in));

while (num !=20 && dato !=12) { // Producto lgico de condiciones


System.out.println(\nIntroduzca un numero: );
dato=Integer.parseInt(leer.readLine());
System.out.println(\ndato: +dato);
num++;
} // Fin del bucle

System.out.println(\nFuera del bucle);

} //Fin del main


}//Fin de la clase

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

Programa 4.3. Ilustracin del funcionamiento de la sentencia continue

import java.io.*;
class Ejercicio {
public static void main(String [] args) throws IOException {
int num=1;
int dato=0;

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) {
continue;
} // Fin del if
System.out.println(\ndato: +dato);
num++;
} // Fin del while
System.out.println(\nFuera del bucle);

} //fin del main


}//fin de la clase

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.

Programa 4.4. Reestructuracin del programa 4.3

import java.io.*;
class Ejercicio {
public static void main(String [] args) throws IOException {
int num=1;
int dato=0;

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) {
System.out.println(\ndato: +dato);
num++;
} // Fin del if
} // Fin del while

System.out.println(\nFuera del bucle);

} //Fin del main


}//Fin de la clase
84 Introduccin a la programacin con orientacin a objetos

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

4.3. EL TEOREMA DE ESTRUCTURA


En la introduccin de este tema se ha hecho referencia al trabajo de Bhm y Jacopini (Bhm y Jaco-
pini, 1966) sobre la posibilidad de construir todos los programas que satisfagan ciertas condiciones sin
usar el salto incondicional. Esto constituye el teorema de estructura que podramos enunciar en len-
guaje actual de la siguiente forma (Joyanes, 1996):

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.

4.4. ESTRUCTURAS DE CONTROL ELEMENTALES


Para abordar una programacin estructurada en un lenguaje determinado se necesita conocer los cons-
tructores que permiten realizar las tres acciones elementales de secuencia, seleccin y bucle. Ya hemos

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

Figura 4.1. Ejemplo de programa no estructurado

Figura 4.2. Versin estructurada del programa de la Figura 4.1

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

Figura 4.3. Anidamiento y concatenacin de estructuras

La secuencia de acciones indicada en las sentencias anteriores se corresponde con el orden en el


que encontramos dichas sentencias en el cdigo.

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:

Por anidamiento: con una estructura dentro de otra.


Por concatenacin: con una estructura tras otra.

Las dos situaciones se ilustran grficamente en la Figura 4.3.


Vamos a mostrar cada caso por medio de condiciones (sentencia if).

a) Concatenacin (incluyendo la clusula else):

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

Figura 4.4. Ejemplo de concatenacin de sentencias if

if (precio > 10.0) {


precio=precio*0.9; // Descuento del 10%
}

if (cantidad >5) {
cantidad++; // Regalo de un artculo ms
}

Obsrvese cmo en el ejemplo la concatenacin de condiciones permite que comprobemos la


segunda independientemente del resultado de la primera.

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

Figura 4.5. Ejemplo de anidamiento de sentencias if

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

} // Fin mtodo main


} // Fin clase

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:

c:\datos> java Ejemplo 23.4 33.9 54.8

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

Al no haber break se continuara en el siguiente caso y se escribira:

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

Si se suprimiera adems el segundo break, la salida sera:

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:

condicin ? expresin1 : expresin2;

El operador realiza un if-else abreviado. Si la condicin es verdadera, se ejecuta la expre-


sin1 y si es falsa la expresin2. El ejemplo anterior es equivalente a:

if (condicin){
expresin1;
}
else {
expresion2;
}

Como es un operador compacto se puede colocar en un System.out.println, por ejemplo, para


que se imprima un resultado u otro en funcin de la condicin. En una asignacin este operador se
podra usar de la forma siguiente:

mayor= (n1 <n2) ? n2 : n1;

si n2 es el mayor se salva en la variable mayor y si es n1 es este valor quien se almacena en mayor.


92 Introduccin a la programacin con orientacin a objetos

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.

Programa 4.6. Clculo de factorial con un bucle do-while

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

System.out.println(Introduzca un numero para calcular


+ el factorial:);
n=Integer.parseInt(leer.readLine());
Programacin estructurada 93

Programa 4.6. Clculo de factorial con un bucle do-while (continuacin)


System.out.println(Numero introducido: +n); // Eco de
// la entrada
i=0;
factorial=1.0;
do {
i=i+1;
factorial=factorial*i;
} while (i<n);
System.out.println(Factorial: +factorial);
} // Fin mtodo main
} // Fin clase Factorial

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

En la inicializacin lo que se inicializa es la variable contadora. La condicin indica qu se tiene


que cumplir para que el bucle contine (recordemos que un for es un tipo de bucle while), y qu
incremento recibe la variable contadora. El for se ejecuta mientras la condicin es verdadera. Semn-
ticamente, un for es equivalente a un while con la estructura:
inicializacin;
while (condicin) {
-- bloque de sentencias --
incremento;
}
94 Introduccin a la programacin con orientacin a objetos

Por ejemplo, implementemos el caso del sumatorio desde 1 a N con un for:

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

Si ahora pretendemos usar la variable i, en un mtodo println, por ejemplo, el compilador


dara un error de variable no declarada. Al igual que un bucle while, la condicin de la sentencia
for se comprueba antes de ejecutarse el cuerpo del bucle, por lo que el bucle for se ejecuta cero o
ms veces.
Sintcticamente, no es necesario colocar los tres componentes (inicializacin, condicin e incre-
mento) de la cabecera del for. Es decir, las tres expresiones de la cabecera de un bucle for son
opcionales. Si no se incluye alguna de ellas se produce un comportamiento por defecto. Dicho com-
portamiento es el siguiente:

a) Si no se incluye la expresin de inicializacin, la variable no se inicializa en ese punto y debe


inicializarse (antes) en otro punto del programa.
b) Si no se incluye la condicin, se considera siempre verdadera, y por tanto estamos en un bucle
infinito.
c) Si no se incluye el incremento, no se incrementa la variable.

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++;
}

Tambin, en Java se puede incluir ms de una sentencia en las secciones de inicializacin y de


incremento de un for. A tal efecto se utiliza el operador coma (,). Por ejemplo:

for (i=0, j=0; i<100; i++, j+=2) {


- bloque de sentencias -
}
Programacin estructurada 95

for (a=1, b=4; a<b ; a++, b--) {


-- bloque de sentencias --
}

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;

while (contador <= LIMITE) {


System.out.println(contador);
contador = contador - 1;
}

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.

4.5. TCNICAS DE REPRESENTACIN


El teorema de estructura nos indica que cualquier programa puede construirse con tres estructuras ele-
mentales o privilegiadas. Para trabajar a mayor nivel de abstraccin que el cdigo, podemos usar tc-
nicas simblicas que nos permitan representar algoritmos de forma independiente del lenguaje. En esta
seccin se presentan tres tcnicas tpicas: los diagramas de flujo, el pseudocdigo y los diagramas de
accin. De stas, las ms extendidas son las dos primeras: los diagramas de flujo (de control) y el pseu-
docdigo.

4.5.1. DIAGRAMAS DE FLUJO DE CONTROL


Los diagramas de flujo de control (tambin denominados ordinogramas) son una herramienta grfica
que sirve para representar las acciones generales en un programa, y entre ellas las tres estructuras ele-
mentales. Es una de las herramientas de representacin basadas en diagramas ms antiguas y, aunque
en la actualidad no se recomienda su uso, se siguen utilizando en muchas ocasiones. En los diagramas
de flujo de control se usan una serie de smbolos que representan el tipo de operacin a realizar. Los
smbolos estn unidos por flechas que representan el flujo de control. Los smbolos ms comunes nor-
malizados por la ANSI (American National Standards Institute) son los mostrados en la Figura 4.6.
El bucle se representa normalmente jugando con el smbolo de condicin. Los conectores se usan
para encadenar diagramas que estn en la misma o distinta pgina. Cada conector se identifica por un
nmero colocado en su correspondiente smbolo. El mismo nmero aparece en los conectores del final
del diagrama y principio del diagrama con el que conecta el anterior.
Veamos un ejemplo de utilizacin de los diagramas de flujo. Representemos un algoritmo para la
suma de los diez primeros nmeros enteros distintos de cero. El diagrama resultante se ilustra en la
96 Introduccin a la programacin con orientacin a objetos

Figura 4.6. Smbolos ms comunes usados en los diagramas de flujo de control

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

Figura 4.8. Diagrama de flujo de control del bucle while


98 Introduccin a la programacin con orientacin a objetos

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:

Segn (variable) Hacer:


valor 1: -- bloque de sentencias 1 --
valor 2: -- bloque de sentencias 2 --
...
valor n: -- bloque de sentencias n --
Fin_segn

c) Bucle
Los bucles while y do-while se representan como Mientras y Haz-Mientras. El for lo pode-
mos representar como:

Para (valor inicial) mientras (condicin) incremento (valor del


incremento)

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

nmeros enteros distintos de cero,


Inicio
sumai0
numeroi1
Haz
sumaisuma+numero
numeroinumero+1
Mientras (numero 10)
Escribe suma
Fin

Obsrvese que el pseudocdigo generado se puede traducir con facilidad a un lenguaje de progra-
macin.

4.5.3. DIAGRAMAS DE ACCIN


Aunque de uso menos frecuente y ms profesional, los diagramas de accin proporcionan una tcnica
de diagramas capaz de representar de la misma manera la estructura de alto nivel de un programa que
la visin detallada de su lgica interna (Martin y McClure, 1988; Martin y McClure, 1989) . Los dia-
gramas de accin emplean los conceptos de la programacin estructurada y pueden representar las
estructuras de control elementales. La simbologa usada es la siguiente:

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

Figura 4.9. Representacin de la secuencia de acciones en los diagramas de accin

b) Seleccin
La seleccin simple se representa como la secuencia, vase la Figura 4.10.

if (condici n) {
---bloque de sentencias---
}

Figura 4.10. Diagrama de accin para la seleccin simple


100 Introduccin a la programacin con orientacin a objetos

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

Figura 4.11. Diagrama de accin para el if-else

Finalmente, la representacin de la seleccin mltiple se encuentra en la Figura 4.12.

switch (valor) {
case 1:
--- bloque de sentencias 1 ---
break;
case 2:
--- bloque de sentencias 2 ---
break;
case 3:
--- bloque de sentencias 3 ---
}

Figura 4.12. Diagrama de accin para la seleccin mltiple

Como puede observarse, cada rama horizontal corresponde a un caso de la seleccin.

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

Figura 4.13. Diagrama de accin para bucles


Programacin estructurada 101

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

Ejercicio 1.* Disee y codifique un programa estructurado en Java que calcule el


factorial de un nmero entero positivo cualquiera (incluido cero). El
programa debe solicitar un entero para calcular el factorial, identifi-
car el caso de un entero negativo y, en ese caso, solicitar un nue-
vo valor hasta que se introduzca un entero no negativo. Tras
calcular el factorial el programa debe preguntar si se desea introdu-
cir un nuevo entero. Si es as, el programa debe solicitar un valor
entero en las mismas condiciones que anteriormente. El proceso se
repetir hasta que el usuario indique que no desea seguir calculan-
do factoriales.

Ejercicio 2.* Dada la siguiente sentencia switch transfrmela en una secuencia


equivalente de sentencias if.

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:

*****
***
*

Ejercicio 4.* El siguiente fragmento de programa pretende sumar los enteros de


1 a n (ambos inclusive) almacenando el resultado en la variable sum.
Es correcto el programa? Si no lo es, indique por qu y qu habra
que hacer para solucionarlo.

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:

*****
****
***
**
*

Se debe imprimir un nico carcter cada vez.

Ejercicio 6.* Reestructure el siguiente fragmento de cdigo para evitar el uso de


saltos incondicionales.

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:

*******
******
*****
****
***
**
*

Se debe imprimir un nico carcter cada vez.

Ejercicio 8.* Qu imprimira el siguiente programa?

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

Ejercicio 9.* Cul es el resultado del siguiente programa?

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.

meses: while (m<=11) {


m++;
d=1;
while (d<=30) {
if (m==2 && d==29) continue meses;
System.out.println(m+ +d);
d++;

}
}

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

realizar el sumatorio de valores al calcular la media.

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 5.3. Tipos abstractos de datos. Clases y objetos


5.2. Programacin Modular 5.3.1. Conceptos generales
5.2.1. Modularizacin funcional 5.3.2. Clase cadena (String)
5.2.2. Paso de parmetros 5.3.3. Clase matriz
5.2.3. Sobrecarga de mtodos 5.3.4. Clases contenedoras
106 Introduccin a la programacin con orientacin a objetos

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.

5.2. PROGRAMACIN MODULAR


El comienzo de la programacin modular se remonta a los primeros tiempos de la programacin como
disciplina. De hecho, exista programacin modular antes de existir la programacin estructurada. La
programacin modular aplica el principio de divide-y-vencers para poder tratar con la complejidad.
Segn este principio, cuando abordemos un problema complejo dividmoslo en subproblemas ms
pequeos, hasta que estos subproblemas sean fciles de tratar por separado. En una primera aproxi-
macin, la solucin de los subproblemas nos proporciona la solucin del problema completo 1.
Aplicado a un programa, este principio nos lleva a dividir el programa en subprogramas que rea-
lizan cada una de las tareas necesarias. La aproximacin contraria, es decir, construir un nico pro-
grama principal monoltico, no es en absoluto eficiente para un desarrollo medianamente complejo. En
la Figura 5.1 se ilustran grficamente las dos aproximaciones.
Para poder llevar a la prctica la modularizacin es necesario un mecanismo que permita implan-
tarla en los lenguajes de programacin. Este mecanismo existe en todos los lenguajes, y consiste en la
posibilidad de definir tareas especficas como mdulos de cdigo independientes del programa prin-
cipal. Lgicamente, estos mdulos de cdigo deben poder invocarse desde el principal para que empie-
cen a trabajar, y deben acabar devolviendo el control al principal cuando terminen de ejecutarse.
Vamos a considerar estos bloques de cdigo independiente.

5.2.1. MODULARIZACIN FUNCIONAL


Los bloques de cdigo independientes del programa principal que hemos definido tienen siempre
una connotacin funcional. Esto quiere decir que estos bloques realizan alguna (en el caso ideal slo
una) tarea especfica. De hecho, cuando definimos estos subprogramas lo que hacemos es identifi-
car tareas especficas y escribir el subprograma para resolverlas.

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

Subprograma 1 Subprograma 2 Subprograma 3

a) Programa b) Programa modular


monoltico

Figura 5.1. Esquema estructural de un programa monoltico frente a un programa modular

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:

Un grupo de sentencias de programa identificadas con un nombre.

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:

modificador tipo_a_devolver identificador_mtodo(


tipo parmetro_1, ..., tipo parmetro_n){
---- bloque de sentencias ----
}

Los modificadores se considerarn con detalle en el Captulo 7, cuando se exponga la creacin de


clases. De momento baste con saber que para el mtodo main y cualquier otro definido en su misma
clase debemos usar el modificador static, que implica que el mtodo se puede usar aunque no haya-
mos creado un objeto de su clase (el mtodo se usa directamente a travs de la clase). En la lista de
parmetros hay que indicar el tipo de cada uno (tipo primitivo o clase, si el parmetro en cuestin es
un objeto). Para invocar un mtodo, en el caso general debemos crear un objeto y luego usar el ope-
rador . (punto):

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

Figura 5.2. Flujo de control en una llamada a un mtodo


Abstraccin procedimental y de datos 109

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,

int factorial(int n){


---- bloque de sentencias ----
}

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:

void factorial (int n) {


---- bloque de sentencias ----
}

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

// Determinando permetro y superficie


perimetro=calcular_ perimetro(radio);
superficie= calcular_superficie(radio);

// 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 mtodo main

public static double calcular_ perimetro(double dato_radio) {


double valor;
valor=2.0*Math.PI*dato_radio;
return valor;

} // Fin mtodo calcular_ perimetro

public static double calcular_superficie(double dato_radio) {


return Math.PI*dato_radio*dato_radio;

} // Fin mtodo calcular_superficie

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

Figura 5.3. Diagrama de estructura del programa 5.1

los componentes de un mdulo (sentencias) deben ir dirigidos a resolver un y solo un problema.


A esta propiedad se la denomina cohesin y se dice que los mdulos deben ser cohesivos. El obje-
tivo en el diseo modular es conseguir mdulos tan cohesivos como sea posible. Muchas veces
esto no es posible al cien por cien, pero siempre debe pretenderse desarrollar mdulos de alta
cohesin.
Cuando se consideran varios mdulos de software, y no un solo programa principal, surge el pro-
blema del alcance de las variables. Por tal se entiende la zona del programa donde una variable es acce-
sible. Desde este punto de vista, en un lenguaje de programacin las variables pueden ser de dos tipos;
locales o globales. Consideremos cada una de ellas.

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

localizar. Por esa razn, se debe evitar el uso de variables globales.


En Java no hay variables globales como tales. Algo parecido a una variable global se puede con-
seguir declarando variables dentro de una clase pero fuera de todos los mtodos de la misma 5. La
situacin se puede ilustrar con el Programa 5.2.

Programa 5.2. Demostracin de variable global en Java

class Global {
static int global=1; // Variable global y static porque no se
// crea ningn objeto

public static void main(String[] args) {


metodo_1();
System.out.println(En main global vale: +global);
}

public static void metodo_1() {


System.out.println(En metodo_1 global vale: +global);
}
} // Fin clase

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.2.2. PASO DE PARMETROS


Un mtodo puede aceptar informacin para usarla en su cuerpo de sentencias. Esta informacin se
suministra en forma de literales, variables u objetos pasados al mtodo a travs de su cabecera. Cada
uno de estos elementos de informacin se denomina parmetro. De manera ms formal se puede defi-
nir un parmetro como un valor que se pasa al mtodo cuando ste se invoca. La lista de parmetros
en la cabecera de un mtodo especifica los tipos de los valores que se pasan y los nombres por los cua-
les el mtodo se referir a los parmetros en la definicin del mtodo. En la definicin del mtodo, los
nombres de los parmetros aceptados se denominan parmetros formales. En las invocaciones al mto-
do desde algn punto del programa, los valores que se pasan al mtodo se denominan parmetros
actuales 6. Los parmetros actuales y formales no necesitan tener los mismos identificadores, ya que
se corresponden por tipo y posicin, vase la Figura 5.4.
Cuando se invoca un mtodo, los parmetros se pasan entre parntesis. Si no se necesitan par-
metros se usan parntesis vacos. Los parmetros formales son identificadores que actan como varia-
bles locales al mtodo y cuyo valor inicial se toma de los parmetros actuales. Los parmetros actuales

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

Figura 5.4. Correspondencia de parmetros actuales y formales

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.

a) Paso por valor


En este caso, el mtodo recibe una copia del valor que se le pasa. La variable original (el parmetro actual)
no cambia de valor, independientemente de que en el mtodo se cambie el contenido del parmetro for-
mal. En Java los datos de tipo primitivo se pasan por valor, es decir, que se asigna al parmetro formal una
copia del valor contenido en el parmetro actual. Por ejemplo, si en la clase Circulo (Programa 5.1)
radio se inicializa en el mtodo main con el valor 5, al invocar al mtodo calcular_ perimetro el
parmetro dato_radio recibe el valor 5. Si tambin dentro del mtodo calcular_ perimetro se
modificara el valor de dato_radio el valor de radio no cambiara.

b) Paso por referencia


En el paso por referencia no se pasa una copia del valor, sino la identificacin de la zona de memoria

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.

5.2.3. SOBRECARGA DE MTODOS


Cuando se compila un programa, cada invocacin de un mtodo se enlaza con el cdigo (instruccio-
nes) que lo define. Algunos lenguajes, entre ellos Java, permiten usar el mismo identificador de mto-
do para mltiples mtodos. Esta capacidad se denomina sobrecarga de mtodos. La distincin entre los
distintos mtodos se realiza a travs de la lista de parmetros, usando distinto nmero de ellos o dis-
tintos tipos de datos. Un ejemplo de sobrecarga en Java lo encontramos en el mtodo println que
acepta distintos tipos de parmetros para escribir (enteros, reales, cadenas, boolean...).
El nombre del mtodo junto con el tipo, orden y nmero de sus parmetros es la firma del mto-
do. El tipo de retorno del mtodo no es parte de la firma. Para que haya sobrecarga se debe usar el mis-
mo identificador para el mtodo y el mismo tipo de retorno, pero distinta firma. Veamos un ejemplo.
Sean los siguientes mtodos:

int metodo_1(int variable_1)


int metodo_1(double variable_1)
int metodo_1(int variable_1, double variable_2)
int metodo_1(double variable_2, int variable_1)

tenemos cuatro mtodos con el mismo identificador (nombre) pero distinta firma, hay por lo tanto
sobrecarga. Sin embargo, en los dos mtodos siguientes,

int metodo_1(int variable_1)


double metodo_1(int variable_1)

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.

5.3. TIPOS ABSTRACTOS DE DATOS. CLASES Y OBJETOS


En el apartado anterior hemos considerado la abstraccin procedimental, mostrando que es una tcni-
ca til para el desarrollo de software. En este apartado se presenta la abstraccin de datos como otra
tcnica tambin de utilidad que es necesario conocer. En particular, se presenta la relacin entre el con-

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

cepto de tipo abstracto de datos y las clases y objetos.

5.3.1. CONCEPTOS GENERALES


Un concepto fundamental en el campo de la programacin es el de tipo abstracto de dato. Hasta ahora
hemos encontrado nicamente datos primitivos, tales como el tipo entero o el real. Una generalizacin del
concepto de dato primitivo es el de estructura de datos. Una estructura de datos es un conjunto de datos
primitivos agrupados juntos en una forma determinada que se pueden manejar como una unidad (Smith,
1987). A su vez podemos definir un concepto ms general que es el de tipo abstracto de datos (TAD). Un
tipo abstracto de datos queda definido por una estructura de datos y la serie completa de operaciones que
se pueden realizar sobre un ejemplar de ese TAD. Una nomenclatura usada con respecto a estos concep-
tos (tipos primitivos, estructuras de datos o TAD) es la de ejemplar, para indicar un miembro del tipo (gru-
po) considerado 8. Por ejemplo, las variables de un tipo primitivo determinado seran ejemplares de ese
tipo. En orden creciente de abstraccin, la relacin entre los datos primitivos, las estructuras de datos y los
tipos abstractos de datos se podra representar grficamente como muestra la Figura 5.6.
Cuando se trabaja con TAD se pretende que la manera en la que est definido quede oculta para el
usuario del mismo. El usuario creara ejemplares del TAD y los usara llamando a los distintos proce-
dimientos o tareas que ese TAD permite. El usuario no necesitara conocer cul es la implementacin
del TAD (el usuario podra trabajar a mayor nivel de abstraccin), slo necesitara conocer cmo se usa.
Este mecanismo asegura que no se pueden realizar operaciones incorrectas sobre los ejemplares del
TAD (siempre que est correctamente definido). Un programa escrito en trminos de TAD se puede
transportar a cualquier mquina, en tanto y en cuanto la nueva mquina tenga definido el mismo TAD.
Pues bien, el concepto de la programacin orientada a objetos (POO) se fundamenta en el concepto
de TAD. Los objetos son los ejemplares de un determinado TAD. En POO el TAD se define por medio
de lo que se denomina clase. Por lo tanto, la clase se define como un conjunto de datos junto con las ope-

Tipo primitivo Tipo primitivo

Estructura de datos Operaciones

Tipo abstracto de datos

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

Clase A Objeto 1 (de la clase A)

Datos (Propiedades)

Objeto 2 (de la clase A)

Procedimientos (Mtodos)
Objeto 3 (de la clase A)

Figura 5.7. Relacin entre datos, procedimientos, clase y objetos

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

Aunque en el Captulo 7 el concepto de clase se tratar en detalle, a modo de ejemplo se presenta


a continuacin una posible implementacin de la clase Automovil.

class Automovil{

//Caractersticas
Abstraccin procedimental y de datos 117

int cilindrada, potencia;


String modelo, tipo_de_motor;
double precio;
boolean disponibilidad;

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

Nombre_clase Nombre_objeto= new Mtodo_constructor(parmetros);

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(tipo parmetro_1,..., tipo parmetro_n){


bloque de sentencias
}

Otro constructor de la clase Automovil podra no aceptar parmetros, quedando de la siguiente


forma:

Automovil(){
bloque de sentencias
}

Para crear un objeto de la clase Automovil tendramos que escribir el siguiente cdigo:

Automovil coche1= new Automovil();

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:

Pieza_ajedrez caballo=new Pieza_ajedrez();

Ahora existira el objeto caballo y podramos usarlo en el programa.


Para invocar un mtodo se usa el operador . (punto). Por ejemplo, si queremos invocar al mto-
118 Introduccin a la programacin con orientacin a objetos

do actualizar_ precio de la clase Automovil, usaramos el objeto coche1 y haramos:

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.

5.3.2. CLASE CADENA (String)

En programacin es habitual que se necesite manejar cadenas completas de caracteres alfanumricos.


Por eso, los distintos lenguajes de programacin proporcionan algn tipo de dato para el manejo de
cadenas. En algunos lenguajes de programacin las cadenas son tipos primitivos de datos. En otros,
las cadenas de caracteres no se representan por tipos primitivos. En Java, por ejemplo, se representan
como objetos de una clase, la clase String, definida en el paquete java.lang. Para crear una cade-
na podemos usar la sintaxis habitual de creacin de un objeto, pasando como argumento al mtodo
constructor la cadena deseada, por ejemplo:

String cadena=new String(Esto es un ejemplo);

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:

String cadena=Esto es un ejemplo;

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:

Esto es un ejemplo <....... Cadena


Abstraccin procedimental y de datos 119

Tabla 5.1. Algunos mtodos de la clase String de Java

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

012345678901234567 <....... Numeracin

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.

Programa 5.3. Uso de algunos mtodos de la clase String

class Cadenas {
public static void main(String[] args) {
String C1=Cadena 1 ;
String C2=Cadena 1 ; //Tiene un blanco al final
int N1, N2;

// Determinando la longitud de las cadenas

N1=C1.length();
N2=C2.length();

System.out.println(Longitud cadena 1: +N1);


System.out.println(Longitud cadena 2: +N2);

// Obteniendo el cuarto carcter

System.out.println(Caracter en posicion 3 (cuarto):


+C1.charAt(3));

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

// Conversin de un nmero a cadena


120 Introduccin a la programacin con orientacin a objetos

Programa 5.3. Uso de algunos mtodos de la clase String (continuacin)

System.out.println(Conversion: +C2.valueOf(420.37));

// Impresin de C2 viendo que no se ha alterado

System.out.println(C2: +C2);

// Conversin de un nmero a cadena usando una variable

N1=342;
System.out.println(Conversion: +C2.valueOf(N1));

}
}

El resultado del Programa 5.3 sera:

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.

5.3.3. CLASE MATRIZ 9


En programacin es muy frecuente encontrar una situacin en la que hay que manejar muchos ele-
mentos del mismo tipo. Los lenguajes de programacin suelen proporcionar una forma de tratar a
todos los elementos como un conjunto, con un identificador para el conjunto que permite mane-
jarlo como un todo. A su vez, es posible tratar los elementos individuales distinguindolos por un
ndice. La forma de conseguir esto es por medio de una estructura de datos denominada matriz 10.
En programacin, una matriz representa una coleccin de elementos del mismo tipo organizados
de acuerdo a un criterio. Este punto es importante, en una matriz tenemos una organizacin que se

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,

a0, a1, a2, .... <---- caso monodimensional

aij, i=0,N <---- caso bidimensional


j=0,M

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:

tipo [] nombre_matriz = new tipo [dimensin_matriz];

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:

int [] resultados = new int [20];

Esta declaracin tambin se puede dividir en dos pasos:

tipo[] nombre_matriz;
nombre_matriz =new tipo [dimensin_matriz];
122 Introduccin a la programacin con orientacin a objetos

En la primera sentencia se declara el identificador de la matriz, pero no se le asigna an su tamao


(no se reserva memoria para ella). En la segunda sentencia se asigna el tamao de la matriz.
En la declaracin de matrices los corchetes se pueden colocar detrs del nombre de la matriz de la
forma siguiente:

tipo nombre_matriz [] = new tipo [dimensin_matriz];

Sin embargo, es ms legible el primer formato por comparacin con otras declaraciones. Por ejem-
plo, con la sentencia,
int n1,n2,n3;

declaramos tres variables de tipo entero. A su vez, con la sentencia,


int [] matriz1,matriz2,matriz3;

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

Programa 5.4. Ejemplo del uso de una matriz

class Matrices_1 {
public static void main(String [] args) {
final int LIMITE=5;

int [] lista = new int [LIMITE];

// Inicializando la matriz

for (int i=0; i<LIMITE; i++) {


lista[i]=10*i;
}

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

for (int i=0; i<LIMITE; i++) {


System.out.print(lista[i]+ );
}

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

System.out.println(Introduzca el tamagno de la matriz: );


limite = Integer.parseInt(leer.readLine());

int [] lista = new int [limite];


// Inicializando y escribiendo la matriz
124 Introduccin a la programacin con orientacin a objetos

for (int i=0; i<limite; i++) {


lista[i]=10*i;
System.out.print(lista[i]+ );
}

} // 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:

tipo [] nombre_matriz = {Lista};

donde lista es una serie de valores separados por comas. Por ejemplo:

int [] valores = {22, 56, 1, 39, 88};

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,

public static void main(String [] args) {


-- - bloque de sentencias - - -
}

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.

Programa 5.6. Ejemplo de la matriz de argumentos de la lnea de rdenes

class Matrices {
public static void main(String [] args) {
int N1, N2;

//Leyendo de la lnea de rdenes


Abstraccin procedimental y de datos 125

N1=Integer.parseInt(args[0]);
N2=Integer.parseInt(args[1]);

System.out.println(El primer argumento es: +N1);


System.out.println(El segundo argumento es: +N2);

} // Fin mtodo
} // Fin clase

Si al ejecutar el Programa 5.6 hacemos:

C:> java Matrices 2 3

El resultado sera:

El primer argumento es: 2


El segundo argumento es: 3

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:

int [][] dosD=new int [4][5];

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

int [][] tabla ={{1},{2,3},{4,5,6},{7,8,9,10}};

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.

Programa 5.7. Ejemplo de matriz bidimensional

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

for (int i=0;i<tabla.length;i++) { // Recorriendo


// filas
for (int j=0;j< tabla[i].length;j++) { // Recorriendo
// columnas
System.out.print(tabla[i][j] + );
}
System.out.println();
}

// Sumando filas e imprimiendo el resultado

for (int i=0; i<tabla.length;i++) {


suma=0;
for (int j=0; j< tabla[i].length; j++) {
suma+=tabla[i][j];
}
System.out.println(Fila: +(i+1)+ Suma: +suma);
}

} // 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).

Programa 5.8. Ejemplo de paso de parmetros

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

public static void cambiar(int a, double b, double [] c) {


a=5;
b=7.2;
c[0]=9.1;
System.out.println(Valores dentro del metodo);
System.out.println(a+ + b+ + c[0]);
} // Fin mtodo cambiar

} // Fin clase

La salida del Programa 5.8 sera:

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

Tabla 5.2. Clases contenedoras en Java

Tipo primitivo Clase contenedora


byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
chart Character

del valor contenido, no una referencia a la zona de la memoria donde se almacena el valor).

5.3.4. CLASES CONTENEDORAS


Ya hemos comentado que en Java existen unas clases que corresponden a los tipos primitivos (de
hecho, en un lenguaje puramente orientado a objetos lo que existira seran estas clases y no los tipos
primitivos). Hay una de estas clases para cada tipo primitivo de datos, se denominan clases contene-
doras 12 y contienen mtodos relacionados con cada uno de los tipos. Las clases contenedoras se deno-
tan con el nombre completo del tipo considerado comenzando con mayscula. La sintaxis es la
indicada en la Tabla 5.2.
Las clases contenedoras son tiles cuando se necesita un objeto en lugar de un tipo primitivo. Hay
clases que slo tratan con objetos, por lo que si se desea almacenar un tipo primitivo en una de ellas
ser necesario crear un objeto a partir de su clase contenedora. Los objetos creados a partir de clases
contenedoras se comportan como los objetos tradicionales. Por esa razn, todo el procesamiento sobre
dicho objeto se realiza a travs de mtodos. Un ejemplo de mtodo til contenido en estas clases es el
ya visto parseNombre_de_tipo para convertir una cadena (clase String) que contiene un nmero
a su valor entero o real. Todas estas clases estn en el paquete java.lang.
Existen dos constantes que se usan en las clases contenedoras numricas y que dan el mayor y
menor valor que se puede representar con ese tipo. Estas constantes son MAX_VALUE y
MIN_VALUE. El Programa 5.9 muestra el resultado de utilizar dichas constantes con la clase Float.

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

La salida del Programa 5.9 sera:

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 3.* Escriba un programa que devuelva el mximo y el mnimo de tres


nmeros introducidos como argumentos por la lnea de rdenes.

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.

Ejercicio 5.* Escriba un programa que imprima el tringulo de Pascal con el


nmero de filas introducido como argumento por la lnea de rde-
nes. Para ello, utilice la funcin combinatoria c (n, k) usando la defi-
nicin:

n
1 2
c (n,k) 5 n!/(k! (n-k)! ) 5

con k # n

Ejercicio 6. Como complemento del ejercicio anterior escriba un algoritmo que


imprima por pantalla el tringulo de Pascal con su formato correcto.
Sugerencia: Tenga en cuenta en primer lugar el nmero de filas que
se van a escribir.

Ejercicio 7.* Cul es el resultado del siguiente programa?

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

public static int [] metodo1(int [] a, int [] b) {


int [] c={3,4,5};
for (int i=0; i< a.length; i++)
c[i]=a[i]+c[i];
return c;
}//fin metodo 1
}//fin clase

Ejercicio 8.* Cul es el resultado del siguiente programa?


130 Introduccin a la programacin con orientacin a objetos

class Ejercicio {

public static void main(String [] args) {

int [] Valores1 ={9, 48, 5, 3, 29, 62};

int [] Valores2 ={45, 1, 33};

metodo1(Valores1 [4]);

metodo1(Valores2 [2]);

metodo2(Valores2);

Valores1=metodo3(Valores2);

System.out.print(Valores1[0]);

for (int i=1; i<Valores2.length;i++)

System.out.print( +Valores1[i]);

}
Abstraccin procedimental y de datos 131

public static void metodo1(int numero) {


numero = 0;
numero=numero+10;
}

public static void metodo2(int [] lista) {


lista[1]=lista[2];
}

public static int [] metodo3(int [] lista) {


for (int i=0; i<lista.length;i++)
lista[i]=lista[i]+i;
return lista;
}
}

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.

Ejercicio 10.* Escriba un programa que calcule numricamente el valor de la inte-


gral:

E 0
p

sen (u) d u

aplicando los siguientes mtodos:

a) Regla del trapecio.


b) Montecarlo.

sese un mtodo (mdulo) para cada tipo de clculo. Imprima el


resultado de ambos mtodos.

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:

a) Tmese el resto del cociente m/n.


b) Si el resto es cero, entonces n es el mximo comn divisor.
c) Si el resto es distinto de cero se hace m=n y n=resto.
d) Se vuelve al punto a).

Escriba un programa que acepte dos nmeros enteros ledos por


teclado y determine su mximo comn divisor aplicando el algorit-
132 Introduccin a la programacin con orientacin a objetos

mo de Euclides. Escriba un mtodo que aplique el algoritmo.

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 6.4. Recursividad frente a iteracin


6.2. Principio de induccin 6.5. Aplicaciones
6.3. Recursividad elemental
6.3.1. Casos base e inductivo
6.3.2. Funcionamiento de la recursividad
6.3.3. Correccin de la recursividad
6.3.4. Recursividad indirecta
134 Introduccin a la programacin con orientacin a objetos

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.

6.2. 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:

a) Comprobamos que si el teorema se cumple para N (hiptesis inductiva) se cumple tambin


para N 1 1.
b) Comprobamos que el teorema se cumple para el primer valor posible. Si esto es as, por el
apartado a) ser tambin vlido para el segundo valor considerado y al serlo para ste tambin
lo ser para el tercero, etc., lo que completa la demostracin.

Veamos un ejemplo. Sea el siguiente teorema:

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

Para todo entero N $ 1, la suma de los N primeros enteros dada por:


N

6 i 5 1 1 2 1 ... 1 N
i 5 1
N(N 1 1)
es igual a }}.
2
Prueba por induccin:

a) Hiptesis inductiva: Consideramos que el teorema es vlido para N, es decir, que:

N
N(N 1 1)
6 i 5 }}2
i 5 1

veamos si se cumple para N 1 1. Para ello tenemos que

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

N(N 1 1) N(N 1 1) 1 2(N 1 1)


}} 1 (N 1 1) 5 }}} ;
2 2

y sacando factor comn (N 1 1)

N(N 1 1) 1 2(N 1 1) (N 1 1)(N 1 2)


}}} 5 }} ;
2 2

con esto,

N 1 1
(N 1 1)(N 1
6 i 5 }2
i 5 1

lo que completa la demostracin (considrese N 1 1 5 M para ver la expresin ms clara).


136 Introduccin a la programacin con orientacin a objetos

b) Vamos a ver si el teorema se cumple para el primer entero considerado, es decir 1.

6 i 5 1;
i 5 1

con la expresin cerrada para N=1 tendramos:

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.

6.3. RECURSIVIDAD ELEMENTAL

En este apartado vamos a presentar la racionalizacin formal de la recursividad basndonos en el prin-


cipio de induccin.

6.3.1. CASOS BASE E INDUCTIVO


La recursividad est relacionada con el principio de induccin. Existe un caso base, en el que no se
realiza ninguna llamada inductiva. Por otro lado, existe un caso o clusula inductiva (tambin llama-
do caso recursivo o caso general), en el que se van realizando llamadas a versiones del propio mto-
do. A estas versiones se les van pasando parmetros ms simples que los que se usan en la versin del
mtodo actual y que van llevando hacia el caso base. Por tanto, en un mtodo recursivo:

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:

Caso base: s(1)=1;


Caso inductivo: s(n) 5 s(n 2 1) 1 n;
Recursividad 137

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.

Programa 6.1. Suma recursiva de los n primeros enteros

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

public static int s(int n) {


int valor;
if (n==1) // Caso base
valor = 1;
else
valor = s(n-1)+n; // Caso inductivo
return valor;
} // Fin mtodo s
} // Fin clase

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:

public static int s(int n) {


int valor;
if (n>0) {
if (n==1){ // Caso base
valor = 1;
}
else {
valor = s(n-1)+n; // Caso inductivo
}
}
else {
System.out.println(El numero no puede ser menor+
que 1);
valor = -1;
}
return valor;
} // Fin mtodo s
138 Introduccin a la programacin con orientacin a objetos

En el ejemplo, el primer if se comprobara en cada llamada al mtodo usando el valor -1 como


valor centinela, para indicar que la entrada de datos es errnea. Sin embargo, si es el propio mtodo
recursivo el que comprueba con una condicin (un if) si n 0, la condicin se va a probar en todas las
llamadas del mtodo, aunque basta con probar dicha condicin la primera vez que se invoca el mto-
do para saber si se cumple o no. Esta situacin es muy normal con los mtodos recursivos y la solu-
cin consiste en usar un driver 2 donde se comprueba la condicin y desde donde luego se invoca al
mtodo recursivo. Dependiendo del caso, el driver ser un mtodo nuevo o un fragmento de cdigo
nuevo inserto en el mdulo que haya realizado la llamada al mtodo recursivo. En el ejemplo anterior,
si usamos un mtodo nuevo como driver tendramos el Programa 6.2.

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

public static int s(int n) {


int valor;
if (n==1) // Caso base
valor = 1;
else
valor = s(n-1)+n; // Caso inductivo
return valor;

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

6.3.2. FUNCIONAMIENTO DE LA RECURSIVIDAD


Vamos a utilizar el problema del factorial para explicar el funcionamiento e implementacin del pro-
ceso recursivo. Para calcular recursivamente el factorial de un nmero necesitamos indicar cul es el
caso base y cul es el caso inductivo. El caso base debe ser el caso ms simple de calcular. El facto-
rial de 0 es 1, ste es el caso ms simple que se nos puede dar, por lo que:

Caso base: fact (0) 5 1

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:

Caso inductivo: fact(n) 5 fact (n 2 1) ? n

En el Programa 6.3 se muestra dicho clculo.

Programa 6.3. Clculo recursivo del factorial de n.

class Factorial_recursivo {
public static void main(String [] args) {
int n;

System.out.println(Calculo recursivo del factorial\n);


n=Integer.parseInt(args[0]);

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

public static long factorial(int n) {


long nFact;
if (n==0){
nFact = 1;
}
else{
nFact = factorial(n-1)*n;
}
return nFact;

} // Fin mtodo factorial


} // Fin de la clase

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

Figura 6.1. Ilustracin del funcionamiento interno de las llamadas recursivas

Tabla 6.1. Anlisis de la pila de llama-

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.

6.3.3. CORRECCIN DE LA RECURSIVIDAD


Una consecuencia evidente de la exposicin del apartado anterior es que no podemos determinar si un
algoritmo recursivo es correcto o no visualizando in mente el conjunto de llamadas recursivas reali-
zadas. Imaginemos, por ejemplo, la complejidad de la visualizacin de lo que ocurre en el mtodo del
factorial para un valor n, arbitrario. Sin embargo, es necesario poder determinar si un algoritmo recur-
sivo es o no correcto, es decir, si funciona de forma adecuada. Podemos alcanzar este objetivo con ayu-
da del principio de induccin (Weiss, 1998) de la siguiente forma. Supongamos un algoritmo recursivo
genrico con un caso base y un caso inductivo,

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.

6.3.4. RECURSIVIDAD INDIRECTA


La recursividad directa se da cuando un mtodo se llama a s mismo, tal como el factorial que llama al
propio factorial. La recursividad indirecta ocurre cuando un mtodo llama a otro mtodo, que invoca a
otro, etc., terminando finalmente en que el mtodo original se invoca otra vez. Por ejemplo, si el mto-
do m1 llama al mtodo m2, el cual llama al mtodo m3, el cual llama a m1 nuevamente, decimos que m1
es recursivo indirectamente. La indireccin puede tener varios niveles de profundidad, por ejemplo
m1 invoca a m2, el cual invoca a m3, el cual invoca a m4, que invoca a m1. En la Figura 6.2 se presen-
ta un ejemplo de recursividad indirecta. Las invocaciones a los mtodos se marcan con lneas continuas,
y las devoluciones con lneas discontinuas.

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

Figura 6.2. Esquema de llamadas recursivas en un caso de recursividad indirecta.


Las lneas continuas representan las llamadas hasta llegar al caso base. Las lneas
discontinuas representan el camino de vuelta desde el caso base
Al igual que en la recursividad directa, se sigue el camino completo de invocaciones hasta el caso
base. En ese momento, se van deshaciendo a la inversa la serie de llamadas recursivas. La recursi-
vidad indirecta requiere que se ponga la misma atencin a los casos base e inductivo que la directa.
Sin embargo, es ms complicada de seguir (trazar) y de depurar. El Programa 6.4 muestra un ejemplo
de recursividad indirecta.

Programa 6.4. Programa ilustracin de recursividad indirecta

class Indirecta {
public static void main(String [] args) {
int n=6;
metodo1(n);
}
public static void metodo1(int n) {
Recursividad 143

Programa 6.4. Programa ilustracin de recursividad indirecta (continuacin)

// El caso base es n=0


if (n == 0) {
System.out.println(En metodo1 con N: +n);
}
else { // Caso inductivo
metodo2(n);
}
}
public static void metodo2(int n) {
System.out.println(En metodo2 con N: +n);
metodo3(n-1);
}
public static void metodo3(int n) {
System.out.println(En metodo3 con N: +n);
metodo1(n-1);
}
}

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.

6.4. RECURSIVIDAD FRENTE A ITERACIN


En este apartado se presenta una comparacin entre la iteracin y la recursividad. Se indican las carac-
tersticas de ambas y las ventajas y desventajas de cada una de ellas.

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.

Programa 6.5. Ilustracin de la evaluacin iterativa del factorial

class Factorial_iterativo {
public static void main(String[] args) {
int n=0;
System.out.println( Calculo iterativo del Factorial ) ;
Recursividad 145

Programa 6.5. Ilustracin de la evaluacin iterativa del factorial (continuacin)


n=Integer.parseInt(args[0]);
if (n>=0)
System.out.println( n + ! = +factorial(n) ) ;
else
System.out.println(No se puede evaluar el factorial
+ si n<0);

} // mtodo main

public static long factorial(int numero) {


long nFact = 1;
for (int i = numero ; i > 1 ; i-- ) {
nFact = nFact * i ;
}
return nFact ;

} // Fin mtodo factorial

} // Fin clase Factorial_iterativo

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:

Hemos alcanzado la salida.


Intentamos movernos fuera de los lmites del laberinto.
Ya hemos pasado por esa posicin.

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

public static void imprime(char [][] labe){


int N=labe.length;
int M=labe[0].length;

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

public static boolean valido(char [][] labe, int x, int y)


int N=labe.length;
int M=labe[0].length;
boolean ok=false;

if (x<N && x>=0 && y<M && y>=0 &&


(labe[x][y]== || labe[x][y] == s)) {
ok=true; // Movimiento vlido
}
return ok;
} // Fin mtodo vlido

public static boolean resolver(char [][] labe, int x, int y) {


boolean fin=false;
if (valido(labe,x,y)) {
if (labe[x][y] == s) {
fin =true;
148 Introduccin a la programacin con orientacin a objetos

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

La salida del Programa 6.6 es:

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

Un tipo de problema relacionado con el backtracking y donde la recursividad encuentra aplicacin


es el asociado a los juegos de estrategia, donde se aplica una aproximacin prospectiva (lookahead). En
los juegos de estrategia el problema es parecido al de resolver un laberinto. Aqu, tenemos movimien-
tos que nos pueden conducir a la victoria o a la derrota y cada movimiento supone una eleccin que nos
lleva por un camino u otro. Un programa que juegue un juego de estrategia poda pensarse como uno
que pudiera moverse sobre ese laberinto de posibilidades buscando la salida, en este caso un movi-
miento que corresponda a un camino ganador. Este matiz es importante, intentamos elegir un movi-
miento que, desde el punto actual del juego, est en un camino que lleve a la victoria. Esto no quiere
decir que haciendo ese movimiento se gane seguro. Por ejemplo, si tengo que elegir entre dos posibles
movimientos uno que est en un camino ganador y otro que no est en ningn camino ganador, el pro-
grama elegira el primero. Podemos ilustrar la aplicacin de esta aproximacin con un ejemplo tpico,
el del juego del Nim. En este juego tenemos una serie de fichas colocadas en filas. Hay tantas filas como
se deseen, con una ficha en la primera, dos en la segunda, tres en la tercera, etc., vase la Figura 6.3.

Fila 1

Fila 2

Fila 3

Fila 4

Figura 6.3. Tablero del Nim con cuatro filas

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

Para ji1 mientras j<tablero(i) y seguir incremento jij+1


tablero(i)itablero(i)-j
Si contrario_ pierde entonces
sigueifalse
filaii
fichasij
Fin_Si
tablero(i)itablero(i)+j
Fin_Para
Fin_Para

Si sigue entonces
filai-1
Fin_Si

Devolver fila y fichas


Fin

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

pinta Tablero movimiento_chachi

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.

Programa 6.7. Implementacin del juego del Nim

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;

BufferedReader leer =new BufferedReader


(new InputStreamReader(System.in));

// Definiendo el tablero
System.out.println(Bienvenido al juego del Nim);
System.out.println(Introduzca el numero de filas
+que desea: );

// Colocando las fichas en el tablero


tamagno=Integer.parseInt(leer.readLine());
tablero=new int[tamagno];
for (int i=0; i<tamagno; i++) {
tablero[i]=i+1;
}

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

Programa 6.7. Implementacin del juego del Nim (continuacin)

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

public static void pintaTablero(int [] tablero) {

/* Se pinta el tablero indicando la fila y el nmero


de fichas en cada fila. Cada ficha se representa con
un asterisco */

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

public static boolean fin(int [] tablero) {


// Aqu se determina si no quedan fichas

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

Programa 6.7. Implementacin del juego del Nim (continuacin)

// Este mtodo indica si una jugada es mala

boolean kaput=false;

if (fin(tablero)) {
kaput=true; // Caso base
}
else {
movimiento_chachi(tablero);
if (fila_m ==-1) {
kaput =true;
}
}
return kaput;

} // Fin mtodo chungo

public static void movimiento_chachi(int [] tablero) {


// Este mtodo escoge una jugada buena
boolean sigue=true;

for (int i=0; i<tablero.length && sigue; i++) {


for (int j=1; j<=tablero [i] && sigue; j++) {
tablero [i]-=j;
if (chungo(tablero)) { // Saliendo de los for
sigue=false;
fila_m=i;
fichas_m=j;
}
tablero [i]+=j;
} // Fin for del j
} // Fin for del i
if (sigue) {
fila_m=-1; // Valor centinela si no hay jugada ganadora
}
} // Fin mtodo movimiento_chachi

} // Fin de la clase Nim

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 2.* Cul es la salida del siguiente programa?


class Ejercicio {
public static void main(String[]args) {
metodoA(3);
}

public static void metodoA(int n) {


if (n<1){
System.out.println(B);
}
else {
metodoA(n-1);
System.out.println(R);
}
}
}

Ejercicio 3.* La funcin de Ackermann A(m, n) se define para enteros no negati-


vos como:
n+1 si m=0,
A(m,n)= A(m-1,1) si m>0, n=0,
A(m-1,A(m,n-1)) si m>0, n>0.

Escriba un programa que contenga un mtodo recursivo que calcu-


le esta funcin. Qu devolver el mtodo si m51 y n51? En este
caso, cuntas llamadas se realizarn al mtodo desde el propio
mtodo?

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

la primera varilla, colocados de abajo arriba en orden de tamao


decreciente; sta es la torre de Brahma. Los sacerdotes estn tratan-
do de pasar la pila de la primera varilla a la segunda, sometidos a las
leyes de Brahma que indican que slo se puede mover un disco a la
vez, y que en ningn momento se puede colocar un disco ms gran-
de sobre uno ms pequeo. Se cuenta con la tercera varilla para colo-
car los discos temporalmente. Cuando todos los discos hayan sido
transferidos, la torre, los sacerdotes, el templo, y todo el mundo
desaparecer con un estruendo (Enunciado original del hoy conocido
como problema de las torres de Hanoi).
Desarrolle un programa con un mtodo recursivo que solucione el
problema de las torres de Hanoi para un nmero arbitrario de dis-
cos. El programa debe imprimir la secuencia precisa de transferen-
cia de los n discos de una varilla a otra.

Ejercicio 6.* Escriba un mtodo recursivo que calcule la potencia n de un nme-


ro x.

Ejercicio 7.* Cul es el error del siguiente mtodo que pretende evaluar un
sumatorio? Cmo corregira el error?

int suma(int numero){


if (numero ==0)
return 0;
else
numero+suma(numero-1);

}//Fin del mtodo suma

Ejercicio 8.* Implemente un mtodo recursivo que imprima los elementos de una
matriz monodimensional.

Ejercicio 9.* Cul es la salida del siguiente programa?

class Indirecta{
public static void main(String [ ] args) {
metodoA(3);
} // Fin del main

public static void metodoA(int n){


if (n==1)
return;
else{
System.out.println(A antes);
metodoB(n);
System.out.println(A despues);
}

} // Fin del metodoA

public static void metodoB(int n){


System.out.println(B antes);
metodoA(n-1);
Recursividad 157

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 7.5. Desarrollo de software orientado a objetos


7.2. Concepto de objeto y clase 7.6. Definicin de clases y creacin de objetos
7.2.1. Objetos 7.7. Referencias
7.2.2. Clases 7.7.1. Conceptos generales
7.3. Concepto de encapsulacin y abstraccin 7.7.2. Estructuras dinmicas: listas enlaza-
7.3.1. Encapsulacin das
7.3.2. Abstraccin 7.8. Modificadores
7.4. Relaciones entre clases 7.8.1. Modificadores de visibilidad
7.4.1. Relacin de generalizacin 7.8.2. Modificador static
7.4.2. Relacin de asociacin
7.4.3. Relacin de dependencia
160 Introduccin a la programacin con orientacin a objetos

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,

Programacin con un lenguaje orientado a objetos.


Programacin orientada a objetos.

La programacin con un lenguaje orientado a objetos implica simplemente la utilizacin de clases


y objetos como entidades tiles, pero no el aprovechamiento de las caractersticas implcitas en el para-
digma de orientacin a objetos, tales como la herencia y el polimorfismo. La programacin, o ms
correctamente el desarrollo de software orientado a objetos implica un cambio de mentalidad desde el
tradicional punto de vista funcional. As, debemos considerar desde un punto de vista general, casi
como un diseo, la formulacin de la resolucin de nuestro problema en trminos de objetos y sus rela-
ciones.
Por estas razones, los primeros apartados de este captulo se centran en la exposicin de los con-
ceptos semnticos bsicos en orientacin a objetos. Slo tras esta exposicin se aborda la correspon-
diente sintaxis en Java con los correspondientes ejemplos. Comencemos considerando los conceptos
centrales de objeto y clase.

7.2. CONCEPTO DE OBJETO Y CLASE


Los conceptos centrales del paradigma orientado a objetos son el de objeto y el de clase, por lo que es
importante entender el papel que desempea cada uno. Ambos conceptos estn estrechamente rela-
cionados, pues uno deriva de otro. Algunos textos prefieren empezar con la definicin de clase para
despus describir lo que es un objeto. Aqu, adoptaremos el punto de vista contrario, partiendo del
objeto como concepto particular y generalizando luego al de clase.

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

Figura 7.1. Relacin entre las caractersticas externas de un objeto


y su modelizacin interna, en software

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)

Figura 7.2. Relacin entre clase y objetos de esa clase

Un elemento de una clase (instancia, objeto) se representa en la memoria del ordenador, y es lo


que realmente se manipula en el sistema. En cambio, para una clase no se reserva espacio de datos en
la memoria.
Es importante indicar llegados a este punto la relacin entre clases y objetos con las estructuras de
datos tpicas en los lenguajes no orientados a objetos 3. Las estructuras de datos se pueden definir como
agregados de tipos de datos primitivos. Dichos tipos no tienen por qu ser todos iguales. La utilizacin
de tipos de datos heterogneos impide que se pueda usar un tipo matriz (array, tabla). Estos tipos de
datos se denominan normalmente registros o estructuras y sus componentes se denominan campos.
Veamos como ejemplo un hipottico registro para almacenar informacin sobre los estudiantes de un
centro:

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

Campo 5: Sexo (Tipo carcter)


Una vez definido el registro como estructura de datos se puede usar como un tipo primitivo y
declarar variables o matrices de dicho tipo. El mismo resultado se puede conseguir en programacin
orientada a objetos sin utilizar ningn tipo de registro o estructura. El equivalente a estos registros
se obtiene definiendo una clase donde las propiedades (variables) de la misma sean los campos
necesarios y donde los procedimientos sean los que se usen para manipular los datos (por ejemplo,
actualizar_nombre o escribir_edad).

7.3. ENCAPSULACIN Y ABSTRACCIN


En este apartado vamos a definir y analizar estos dos conceptos relacionados, que se asocian de forma
indisoluble con el de clase. Tanto es as, que la encapsulacin se considera como una de las carac-
tersticas definitorias de la 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)

Figura 7.3. Representacin grfica de los distintos tipos de procedimientos (mtodos)


existentes en una clase

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.

7.4. RELACIONES ENTRE CLASES


Una relacin es una conexin entre elementos. A la hora de buscar una solucin orientada a objetos
para un problema, primero se identifican los objetos y, por tanto, las clases, necesarios. Una vez iden-
tificado un conjunto de clases bsico se debe reflexionar sobre si existe alguna relacin entre dichas
clases. Por lo tanto, cuando hacemos un modelo orientado a objetos no slo debemos conocer los obje-
tos (clases) que lo conforman, sino tambin sus relaciones. Las relaciones entre clases son muy impor-
tantes y deben identificarse con claridad cuando se est realizando un desarrollo orientado a objetos.
Las relaciones entre clases pueden deberse a la existencia de un estado (variables) y/o comporta-
miento comn o, tambin, a que una clase necesite usar otra clase. Aunque algunas clases pueden exis-
tir aisladas, la mayora no pueden y deben cooperar unas con otras. En el ejemplo de la casa, sta est
formada por otras entidades como paredes, suelo o techos. A su vez, la casa contiene elementos de
uso como puertas o ventanas. Podemos ver que en este ejemplo existen diferentes entidades que con-
forman estructuralmente, o que se usan en, la casa. Entre clases tambin pueden existir diferentes tipos
de relaciones. El estudio formal de las relaciones entre clases indica que dichas relaciones pueden ser
de tres tipos (Booch et al., 2000; Rumbaugh et al., 2000):

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.

7.4.1. RELACIN DE GENERALIZACIN


La relacin (es un) se da entre un elemento general (clase padre o superclase) y un caso especfico
de ese elemento (clase hija o subclase). Una relacin de generalizacin se presenta cuando una clase
es un subtipo de otra clase (a veces esta relacin se denomina a-kind-of, un tipo de). Por ejemplo, un
oso es un mamfero. La clase mamfero sera la clase padre o superclase, y la clase oso la clase hija o
subclase. Otros ejemplos de relacin es un seran el caso de una rosa, que es una flor, o de un
empleado, que es una persona. Esta relacin se conoce como relacin de herencia y es importante
recalcar que es una relacin entre clases. La clase hija hereda los atributos (datos) y procedimientos
(mtodos) del padre, pudiendo aadir los suyos propios. La herencia es una caracterstica important-
sima en programacin orientada a objetos. De hecho es una de las caractersticas definitorias de una
Clases y Objetos 167

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

Figura 7.4. Ejemplo de jerarqua de clases generada por herencia

En el ejemplo de la Figura 7.4 tenemos relaciones de herencia, pues cada subclase es un tipo
especial de la clase padre.

7.4.2. RELACIN DE ASOCIACIN


Cuando una clase est estructuralmente compuesta de otras clases se dice que hay una relacin de aso-
ciacin. Esto se consigue usando algn objeto de una de las clases como atributo (dato) de la clase
compuesta. En otras palabras, una clase puede estar formada por objetos de otra u otras clases. Por
ejemplo una hipottica clase Coche podra tener como atributos objetos de la clase Puerta, Rue-
da, etc.
Una cuestin de inters es el nmero de ejemplares involucrados en la relacin de asociacin. Si
se relacionan dos elementos, la relacin se denomina binaria. Si se conectan ms de dos elementos,
por ejemplo, n, la asociacin se denomina n-aria. En UML una asociacin se representa por una lnea
continua que conecta los elementos (en realidad las clases) relacionados. Es posible indicar cuntos
168 Introduccin a la programacin con orientacin a objetos

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

Si el nmero de empleados fuera indefinido tendramos:

1 *
Empresa Empleado

Finalmente, si fueran como mnimo 2 pero sin lmite superior tendramos:

1 2..*
Empresa Empleado

Veamos un ejemplo ms completo. Consideremos un coche que posee un motor y un chasis, as


como ruedas y un sistema de transmisin. A su vez, las ruedas constan de neumtico y tapacubos. El
diagrama correspondiente con las relaciones estructurales de asociacin sera el representado en la
Figura 7.5.
A veces, en una relacin de asociacin queremos indicar explcitamente que tenemos un todo com-
puesto por partes. En este caso, se habla de agregacin y en el diagrama de clases se usa un rombo
Clases y Objetos 169

Coche
1 1
1 1

1 1 4 1

Motor Chasis Ruedas Transmisin

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

La relacin de generalizacin (herencia, es un), vista en el apartado anterior, es nicamente una


relacin entre clases, relacin que define una jerarqua de clases-subclases. Por otro lado, la relacin
de asociacin describe elementos que se deben mantener dentro de una clase. La relacin de asocia-
cin se caracteriza porque una clase tiene como miembros objetos de otra clase.

7.4.3. RELACIN DE DEPENDENCIA


La relacin de dependencia es una relacin de utilizacin, donde un cambio en el estado de un
objeto (el independiente) afecta al estado de otro (el dependiente), pero no a la inversa. Esta rela-
cin aparece en la prctica cuando una clase se relaciona con otra a travs de los mensajes que
le enva (mtodos que invoca). Es decir, que se pasa un ejemplar de la clase independiente como
uno de los parmetros del mtodo invocado (el de la clase dependiente). Por ejemplo si la clase
Monitor tiene un mtodo llamado dibujar y se quiere dibujar un objeto de la clase Circulo, se
debera hacer mi_monitor.dibujar(mi_circulo) siendo mi_monitor y mi_circulo obje-
tos de las clases correspondientes.
En UML la relacin de dependencia se representa por una flecha discontinua dirigida hacia el ele-
mento del cual se depende con la punta de la flecha apuntando hacia la clase independiente. Las depen-
dencias se usarn cuando se quiera indicar que un elemento usa o utiliza a otro. Un ejemplo sera el
elemento ducha como dependiente del elemento caera:

Ducha Caera

Si se modifica el comportamiento de las caeras se afecta el comportamiento de la ducha. No es


una relacin estructural, la ducha no est hecha de caeras sino que las utiliza. Otro ejemplo podra
ser un elemento estudiante que utiliza uno o varios elementos asignatura, el diagrama sera:

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

7.5. DESARROLLO DE SOFTWARE ORIENTADO A OBJETOS


La utilizacin de la aproximacin orientada a objetos para el desarrollo de software presenta una serie
de ventajas (Pressman, 2002) que explican el peso que dicha aproximacin va adquiriendo en la actua-
lidad. El punto central es que la abstraccin de datos (atributos) est escondida detrs de una barrera
de abstracciones procedimentales (los procedimientos de servicio) que son los nicos que pueden
manipular dichos datos, vase la Figura 7.6.

Clase
Mtodos

Mtodos
Mtodos

Datos

Mtodos

Figura 7.6. Representacin esquemtica de la relacin entre datos (atributos)


y mtodos (procedimientos)

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.

7.6. DEFINICIN DE CLASES Y CREACIN DE OBJETOS


Hasta este punto hemos presentado consideraciones conceptuales relativas a clases y objetos. Aborde-
mos ahora las consideraciones sintcticas, en el lenguaje que estamos utilizando, para la creacin de
Clases y Objetos 173

las clases y los objetos.

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

Cuenta (cuenta:int, inicial:double)


depositar (cantidad:double):void
retirar (cantidad:double):void
devuelveSaldo ( ):double

Figura 7.7. Diagrama de la clase Cuenta

El cdigo correspondiente a la clase Cuenta en Java sera el siguiente:

class Cuenta {
int numero_cuenta;
double saldo;

public Cuenta(int cuenta, double inicial) {


numero_cuenta=cuenta;
saldo = inicial;
} // Fin constructor cuenta

public void depositar(double cantidad) {


saldo = saldo + cantidad;
} // Fin mtodo deposito

public void retirar(double cantidad) {


saldo = saldo - cantidad;
} // Fin mtodo retirada

public double saldo(){


return saldo;
} // Fin mtodo saldo

} // Fin clase Cuenta

5
Pronombre demostrativo que significa ste, sta, esto.
Clases y Objetos 175

En este ejemplo, declaramos dos variables de ejemplar, numero_cuenta y saldo. Es importan-


te darse cuenta de que las variables de ejemplar estn definidas en el bloque de cdigo ms externo de
la clase. Por otro lado, los mtodos tambin estn definidos en ese mismo bloque. Esto implica que las
variables de ejemplar son visibles dentro de los mtodos. Por ejemplo, el mtodo depositar y el
mtodo retirar usan la variable de ejemplar saldo y alteran su valor previo.
En la clase anterior tambin se define un mtodo constructor. El mtodo constructor es el que tiene
el mismo nombre que la clase. Es importante indicar que los mtodos constructores no tienen tipo de
retorno (no tienen, ni siquiera void) ya que su misin es la creacin de un objeto. El mtodo construc-
tor se ejecuta siempre para crear un objeto de la clase considerada. Un constructor puede aceptar par-
metros. El uso tpico de los mtodos constructores es la inicializacin de las variables de ejemplar del
objeto creado. Por ejemplo, en la clase anterior, en el constructor se declaran dos parmetros formales
en la cabecera, cuenta e inicial y se asigna su contenido a numero_cuenta y saldo, respectiva-
mente. De esta forma al crear una cuenta nueva (un objeto) se le asignar un nmero y un saldo inicial,
como establecen los requisitos. Una clase puede tener varios constructores, que tendrn el mismo nom-
bre (sobrecarga), sabindose a cul se llama por el tipo y el nmero de parmetros que se pasan. Si no
se definen constructores para una clase, el compilador crea un constructor por defecto que no recibe
argumentos y no hace nada (simplemente inicializar las variables de tipos de datos numricos a 0, las
boolean a false y las referencias a null), pero asegura que cada clase siempre tenga un constructor.
El mtodo saldo() es un ejemplo de mtodo de consulta. En orientacin a objetos sta es la for-
ma de devolver la informacin contenida en un objeto, nunca se debe acceder al contenido del objeto
(sus datos) directamente. En el ejemplo hemos usado la convencin de llamar al mtodo de consulta
con el nombre de la variable de ejemplar cuyo valor devuelve.
Dentro de una clase es posible declarar parmetros de un mtodo con el mismo identificador que
alguna de las variables de ejemplar. Dentro del correspondiente mtodo esto da lugar a ambigedad,
pues en l existira una variable de ejemplar y una variable local con el mismo nombre. Para evitar esa
ambigedad se usa la palabra reservada this 5 que siempre hace referencia a los miembros de la cla-
se. El uso de la clusula this se muestra en el siguiente ejemplo. Sea la siguiente clase,

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

Figura 7.8. Clase cuenta y dos objetos de esa clase

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:

Cuenta cuenta_1=new Cuenta(123456, 250532);


Cuenta cuenta_2=new Cuenta(456784, 1523879);

Vamos a analizar la sentencia de creacin de un objeto de clase Cuenta como:

Cuenta cuenta_1 = new Cuenta(123456,250532);

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,

declaracin: Cuenta cuenta_1;


inicializacin: cuenta_1 = new Cuenta(123456,250532);

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)

Figura 7.9. Relacin entre la referencia al objeto y el objeto en s

caso del mtodo println() en System.out.println(). El operador se pone a continuacin del


nombre del objeto seguido por el mtodo al que invocamos. No tiene sentido invocar directamente a
los datos o variables (ocultamiento de informacin).
Existiendo ya la clase Cuenta podramos usarla en un programa, vase el Programa 7.1 donde
creamos una cuenta bancaria y se realizan varias operaciones con ella.

Programa 7.1. Programa que utiliza la clase Cuenta

class Banco {
public static void main(String [] args) {
double total_cuenta;

//Se crea la cuenta


Cuenta cuenta_1 = new Cuenta(123456, 250532);

// Se consulta el saldo
total_cuenta=cuenta_1.saldo();
System.out.println(Total actual en la cuenta:
+total_cuenta + Euros);

// Se hace un ingreso en la cuenta


cuenta_1.depositar(10000);

// Se consulta el saldo otra vez


total_cuenta=cuenta_1.saldo();
System.out.println(Total actual en la cuenta:
+total_cuenta + Euros);
}
}

La salida del Programa 7.1 sera:

Total actual en la cuenta: 250532.0 Euros


Total actual en la cuenta: 260532.0 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.

7.7.1. CONCEPTOS GENERALES


El concepto de alias como nombre equivalente, sinnimo, es muy general en informtica. Vamos a
considerarlo aplicado a referencias y objetos. En primer lugar, debemos saber que la asignacin de
objetos no corresponde a una asignacin de contenidos. En realidad, una referencia almacena la direc-
cin de memoria donde se encuentra el objeto, no el objeto en s, vase la Figura 7.9.
Toda la interaccin con el objeto transcurre a travs de la referencia, lo que implica una relacin
por variable y no por valor. Esto es as porque aunque manipulemos la referencia, por ejemplo, pasn-
dola a un mtodo, el objeto referido siempre es el mismo (como ya vimos en el Captulo 5). Esta rela-
cin por variable se nota en acciones tales como la asignacin. Consideremos esta cuestin con ms
detalle. Con variables de tipos primitivos de datos como en,

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

Figura 7.10. Efecto de una asignacin sobre un tipo primitivo de datos

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

Figura 7.11. Representacin esquemtica de la referencia a un objeto

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.

Identificador: cuenta_1 Identificador: cuenta_1

Direccin de memoria Direccin de memoria

Objeto cuenta_1

Declaracin Inicializacin

Figura 7.12. Funcionamiento de la declaracin de referencias y creacin de objetos

A partir de la exposicin anterior se explica el distinto funcionamiento de la asignacin por refe-


rencia y por valor. En la asignacin por referencia, si se cambia el contenido de un objeto usando una
referencia, se cambia el contenido del objeto al que se accede usando la otra, porque en realidad esta-
mos trabajando con el mismo objeto. Veamos un ejemplo paso a paso. Si hacemos,
180 Introduccin a la programacin con orientacin a objetos

Cuenta cuenta_1= new Cuenta(1001,10000.0);


Cuenta cuenta_2= new Cuenta(1002,0.0);

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

Objeto cuenta_1 Objeto cuenta_2


Antes de
la asignacin

cuenta_1 cuenta_2

Objeto cuenta_1 Objeto cuenta_2


Despus de
la asignacin

Figura 7.13. Efecto de la asignacin de referencias

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.

Programa 7.2. Ilustracin del manejo de referencias

class Clase1 {
private int valor=10;

public void modificar(Clase2 objt){ // Acta sobre un objeto


objt.cambiar(valor); // de Clase2
}
} // Fin Clase1

class Clase2 {
private int indicador=0;

public void cambiar(int x) { // Modifica el valor de indicador


indicador=x;
}

public int indicador() {


return indicador; // Mtodo de consulta
}
} // Fin Clase2

class Referencias {
public static void main(String [] args) {
int i;
Clase1 obj1 = new Clase1();

Clase2 obj2 = new Clase2();


i=obj2.indicador(); // Valor inicial
System.out.println(i antes de aplicar el metodo modificar: +i);

//Se pasa la referencia a obj2 como parmetro

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

texto texto texto etc.

siguiente siguiente siguiente

Objeto 1 Objeto 2 Objeto 3

Figura 7.14. Enlazamiento de objetos a travs de una referencia

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:

i antes de aplicar el metodo modificar: 0


i tras aplicar el metodo modificar: 10

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.

7.7.2. ESTRUCTURAS DINMICAS: LISTAS ENLAZADAS


El hecho de que tengamos referencias a objetos nos permite construir estructuras dinmicas. Es ste
un tema que no vamos a abordar en detalle en este libro, pero s vamos a presentar un ejemplo senci-
llo de esta tcnica.
Una matriz es una estructura esttica en el sentido de que, una vez dimensionada, su tamao no se
puede cambiar en tiempo de ejecucin. En muchas ocasiones, sin embargo, se necesitan estructuras de
datos dinmicas, donde el tamao de la misma pueda aumentar o disminuir, segn se necesite, en tiem-
po de ejecucin. Un ejemplo sera un programa que deba manipular una serie de artculos que vayan
llegando o consumindose continuamente. Otro ejemplo tpico lo encontramos en la gestin de proce-
sos de un sistema operativo. El nmero de procesos aumenta o disminuye arbitrariamente segn el tra-
bajo que se realice con el sistema. La estructura de datos unas veces tendr que ir creciendo para
acomodar los nuevos elementos y cuando stos se vayan eliminando habr que ir reduciendo el tamao
de la estructura. Estos problemas dinmicos se resuelven con el uso de referencias (o punteros en otros
lenguajes) para enlazar los objetos. La clave est en definir una clase que contenga una referencia (no
un objeto) a un objeto de la misma clase, como en el ejemplo siguiente,
Clases y Objetos 183

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

Programa 7.3. Ejemplo de lista enlazada (continuacin)

Figura 7.15. Relacin de asociacin entre Lista y Nodo

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.

Programa 7.3. Ejemplo de lista enlazada

class Ejemplo_lista {
public static void main(String [] args) {

Lista lista_titulos =new Lista();


lista_titulos.incluir(Don Quijote de la Mancha);
lista_titulos.incluir(Hamlet);
lista_titulos.incluir(El Principito);
lista_titulos.imprimir();

} // Fin metodo main


} // Fin clase principal

class Nodo {
private String titulo;
private Nodo siguiente; /*Se refiere al siguiente elemento
de la lista */

public Nodo(String cadena) {


titulo=cadena;
siguiente=null;
} // Fin constructor

public void poner(Nodo siguiente_nodo) {


siguiente=siguiente_nodo;
}

public Nodo coger() {


return siguiente;
}

public String titulo() {


return titulo;
}
} // Fin clase Nodo

class Lista {
private Nodo primero;

public Lista() {
primero=null; // Almacena el primer elemento de la lista
Clases y Objetos 185

} // Fin constructor

public void incluir(String cadena) {

Nodo elemento = new Nodo(cadena);


if (primero==null) {
primero=elemento; /*la asignacin implica el enlazar la
referencia con el objeto */
}
else {

Nodo aux;

// Se pone en el ltimo para aadir al final


for (aux=primero;aux.coger()!= null;aux=aux.coger());

aux.poner(elemento);
}
} // Fin metodo incluir

public void imprimir() {

for( Nodo aux=primero; aux != null; aux=aux.coger()) {


System.out.println(aux.titulo());
}
}
} // Fin clase Lista

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

public void imprimir() {

for( Nodo aux=primero; aux != null; aux=aux.coger()){


System.out.println(aux.titulo());
}
}

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:

for( Nodo aux=primero; aux.coger() != null; aux = aux.coger()){


System.out.println(aux.titulo());
186 Introduccin a la programacin con orientacin a objetos

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 elemento = new Nodo(cadena);


if (primero==null) {
primero=elemento;
}
else {

Nodo aux=primero;
for (aux=primero;aux.coger()!= null;aux = aux.coger());

aux.poner(elemento);
}
} // Fin metodo incluir

Fijmonos que cuando primero==null se hace primero=elemento y no


primero.poner(elemento). Esto es as porque cuando primero==null no refiere a un objeto de
clase Nodo. De hecho no refiere a nada, as que no existe un campo siguiente en primero sobre el
que pueda actuar el mtodo poner. Lo que hay que hacer es la asignacin, primero=elemento.
Ahora la referencia primero s apunta a un objeto de clase Nodo, en concreto al objeto elemento. A
partir de este momento lo que haremos para aadir un nodo nuevo ser partir del primer nodo (identi-
ficado por primero) y movernos hasta el final de la lista con el bucle for,

for (aux=primero;aux.coger()!= null;aux = aux.coger());

Tabla 7.1. Modificadores de visibilidad en Java

Modificador Clases e Interfaces Mtodos y variables


default (no Visible en su paquete Accesibles desde cualquier clase en el mismo
modificador) paquete que su clase, pero no sus subclases
que no estn en el mismo paquete

public Visible en cualquier lugar Accesibles desde cualquier lugar

protected No se aplica Accesibles desde cualquier clase en elmismo


paquete que su clase o desde cualquier
subclase an en paquetes distintos

private No se aplica Accesible slo desde la propia clase

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.

7.8.1. MODIFICADORES DE VISIBILIDAD


Los modificadores de visibilidad se usan para especificar dnde se puede usar la entidad declarada.
Este tipo de modificadores especifica las caractersticas de acceso de los miembros (datos y mtodos,
incluyendo constructores) de una clase. Estos modificadores se denominan de visibilidad porque con-
trolan en qu medida un miembro de la clase puede ser accedido y referido. As, estos modificadores
nos permiten definir las caractersticas de encapsulacin de un objeto. En Java (y semnticamente en
general en orientacin a objetos) tenemos tres modificadores de visibilidad:
a) public
b) private
c) protected

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,

Mtodos de servicio: public.


Mtodos de soporte: private.
Atributos (datos): private.
188 Introduccin a la programacin con orientacin a objetos

Programa 7.4. Ejemplo del uso de variables estticas (continuacin)

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

7.8.2. MODIFICADOR STATIC


Este modificador asocia una variable o mtodo con la clase y no con objetos de la clase. Veamos el
efecto sobre variables y mtodos.

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)

ticas, las constantes estticas no se pueden declarar dentro de un mtodo.


Las variables estticas son muy tiles para controlar el nmero de objetos que se van creando de
una clase concreta. El Programa 7.4 muestra un ejemplo que incrementa una variable esttica cada vez
que se crea un objeto de la clase Alumno.

Programa 7.4. Ejemplo del uso de variables estticas

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

La salida del Programa 7.4 sera:


112

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:

double pow(double a, double b) devuelve ab


double cos(double a) devuelve cos (a)
190 Introduccin a la programacin con orientacin a objetos

double sqrt(doubla a) devuelve -a


double abs(int a) devuelve el valor absoluto de a en doble precisin

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.

Programa 7.5. Ejemplo del uso de una variable y un mtodo estticos

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

}
}

La salida del Programa 7.5 sera:

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

ID cuenta tarjeta Nmr


coste

pago ( ) pago ( )

Escriba una versin en Java de la clase Producto. Incluya mtodos


que permitan acceder a la informacin del producto.

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

los datos completos de una persona y devolver el nombre o el DNI


independientemente.

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.

Ejercicio 7.* Cul es el resultado del siguiente programa?

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 8.3. Mecanismos adicionales de abstraccin


8.2. Herencia basados en herencia
8.2.1. Concepto de herencia 8.3.1. Clases y mtodos abstractos
8.2.2. Enmascaramiento de variables y 8.3.2. Interfaces
sobrescritura de mtodos
8.2.3. Jerarquas de clases
8.2.4. Organizacin de jerarquas de clases
(paquetes)
8.2.5. Polimorfismo
194 Introduccin a la programacin con orientacin a objetos

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.

8.2.1. CONCEPTO DE HERENCIA


Como ya hemos indicado, la herencia en programacin orientada a objetos permite crear una clase
nueva a partir de otra ya existente. La nueva clase contendr automticamente algunos o todos los atri-
butos (variables) y procedimientos (mtodos) de la clase original. El diseador de software puede aa-
dir nuevas variables y mtodos a la clase nueva, o bien modificar las variables y mtodos heredados
para definir de manera apropiada la nueva clase. Las nuevas clases que se crean usando herencia se
construyen ms rpidamente al aprovechar el cdigo heredado, y son ms fciles de manejar al ser
menos complejas que si tuvieran que incluir todo el cdigo. Por todo esto consumen menos esfuerzo
de desarrollo. En el corazn de la herencia subyace la idea de reutilizabilidad del software. Usando
componentes software ya existentes para crear otros nuevos, sacaremos partido de todo el esfuerzo que
se hizo en el diseo, implementacin y pruebas del software existente.
Consideremos en ms detalle la herencia de clases. La palabra clase proviene de la idea de clasi-
ficar grupos de objetos con caractersticas similares. Los esquemas de clasificacin usan normalmen-
te niveles jerrquicos que se relacionan. Por ejemplo, todos los mamferos comparten ciertas
caractersticas: sangre caliente, pelo, etc. Un subconjunto seran los caballos. Todos los caballos son
Herencia 195

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:

class clase_hija extends clase_ padre {


--- contenido de la clase ---
}

Animal

Invertebrado Vertebrado

Reptil Ave Mamfero

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,

class Coche extends Vehiculo {


--- contenido de la clase ---
}

La sintaxis especificada representa la definicin de la clase clase_hija. La clase hija automti-


camente hereda los mtodos y variables de la clase clase_ padre. Podemos imaginar que hemos
copiado el cdigo de la clase padre y lo hemos pegado en la clase hija, pero sin necesidad de hacerlo
explcitamente. Las variables y mtodos heredados pueden usarse en la clase clase_hija como si
hubieran sido declarados localmente en dicha clase. Adems, las variables y mtodos heredados retie-
nen sus caractersticas de visibilidad originales en la subclase. Es importante indicar que la herencia
acta en una direccin: de la clase padre a la clase hija. Esto implica que las variables y los mtodos
nuevos declarados en la clase hija no pueden usarse en la clase padre.
Desde un punto de vista general la herencia puede ser mltiple o simple. Existe herencia mltiple
cuando podemos heredar de varias clases y herencia simple cuando slo se puede heredar de una cla-
se, tal y como ilustra la Figura 8.2.
Dependiendo del lenguaje es posible disponer de herencia mltiple o simple. En Java, slo es posi-
ble la herencia simple.
Las variables y los mtodos que se heredan vienen controlados por los modificadores de visibilidad.
Los miembros con visibilidad public se heredan y los que tienen visibilidad private no se heredan.
Si stas fueran las nicas posibilidades, la herencia servira de poco, pues todos los miembros (datos,
mtodos) heredables tendran que ser pblicos. En estas condiciones, al tener variables pblicas per-
deramos las ventajas de la encapsulacin. Para conseguir que el comportamiento para clases forneas
sea privado pero para las clases derivadas sea pblico usamos el modificador protected. El modifi-
cador protected establece un nivel intermedio de proteccin entre un acceso public y uno priva-
te. Los miembros (mtodos y variables) de una superclase o clase padre etiquetados como protected
son accesibles para las subclases (clases hijas) y las otras clases del mismo paquete 1. Se recomienda al
lector revisar la tabla de modificadores de visibilidad que se present en el Apartado 7.8 del captulo
anterior.
Vamos a ver un ejemplo de herencia. Consideremos un programa que maneja publicaciones y que
tiene que trabajar con tesis doctorales. Puesto que las tesis (todas) son un tipo particular de publica-
cin tenemos una relacin de herencia. Consideremos que las publicaciones, entre otros atributos,
estn caracterizadas por el ttulo, los autores y el ao de publicacin. A su vez, las tesis tendrn tam-

Clase Padre_1 Clase Padre_2 Clase Padre

Clase Hija Clase Hija

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

public String titulo() {


return titulo;
}
public String autores() {
return autores;
}
public int [] fecha() {
return fecha_edicion;
}
} // Fin clase publicacin

class Tesis extends Publicacion {


protected String departamento;

public Tesis(String titulo, String autores,


String departamento,int dia,
int mes, int agno) {
this.titulo=titulo;
this.autores=autores;
this.departamento=departamento;
198 Introduccin a la programacin con orientacin a objetos

fecha_edicion [0]=dia;
fecha_edicion [1]=mes;
fecha_edicion [2]=agno;
}

public String departamento() {


return departamento;
}
}

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

int [] fecha = una_tesis.fecha();


System.out.println(Fecha: +fecha[0]+/+fecha[1]
+/+fecha[2]);
}
}

El resultado del Programa 8.1 sera,

Titulo: Simulacion de sistemas biologicos


Autor/es: Francisco Perez
Departamento: Informatica
1/3/2002

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

class Tesis extends Publicacion {


protected String departamento;

public Tesis(String titulo, String autores,


String departamento,
int dia, int mes, int agno) {
super(titulo,autores,dia,mes,agno); // Uso de super
this.departamento=departamento;
}

public String departamento() {


return departamento;
200 Introduccin a la programacin con orientacin a objetos

}
}

En la clase Tesis se ha definido un nuevo constructor usando super. En el mtodo constructor


Tesis hemos inicializado las variables heredadas del padre usando la referencia super. No se ha
creado ningn objeto de la clase padre, pero s hemos podido inicializar usando el cdigo del cons-
tructor Publicacion. En realidad super ejecuta el constructor de la clase padre, pero sin crear un
ejemplar de dicha clase.
Otra cuestin a tener en cuenta es que los miembros (datos y mtodos) declarados como privados
en la clase padre, y que por lo tanto no se heredan, existen y se usan normalmente si el hijo invoca a
un mtodo heredado del padre que a su vez usa esa/s variable/s o mtodos. Un ejemplo tpico seran
los mtodos privados de soporte que usa un mtodo del padre que se hereda. Cuando el mtodo here-
dado se invoque desde el hijo dichos mtodos funcionarn normalmente. No es necesario declarar
como protected los mtodos de soporte.

8.2.2. ENMASCARAMIENTO DE VARIABLES Y SOBRESCRITURA


DE MTODOS

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.

Programa 8.2. Ilustracin del enmascaramiento de variables

class Padre{
protected int dato=10;

} // Fin clase Padre

class Hija extends Padre {


private int dato;

public Hija(int dato) {


this.dato =dato;
}
Herencia 201

Programa 8.2. Ilustracin del enmascaramiento de variables (continuacin)

public void imprime_hija() {


System.out.println(Valor de dato en Hija: +dato);
}
public void imprime_ padre() {
System.out.println(Valor de dato en Padre:
+super.dato);
}
}
class Herencia {
public static void main(String [] args) {
Hija ejemplo = new Hija(5);
ejemplo.imprime_hija();
ejemplo.imprime_ padre();
}
}

El resultado del Programa 8.2 sera,

Valor de dato en Hija: 5


Valor de dato en Padre: 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:

Sobrecarga: El mismo identificador pero distinta firma (diferentes parmetros).


Sobrescritura: La misma firma que un mtodo heredado.

Como vemos, la sobrescritura est asociada a la herencia. Si pretendemos sobrescribir un mtodo


heredado pero usamos una firma distinta (por ejemplo, aadiendo un parmetro ms) no tenemos
sobrescritura sino sobrecarga y los dos mtodos sobrecargados son accesibles desde los ejemplares
(objetos) de la clase hija. La finalidad de la sobrescritura es que podamos usar slo un identificador y
un nico conjunto de parmetros para diferentes clases relacionadas por herencia. El cdigo de cada
mtodo sobrescrito es diferente en la clase hija que en la clase padre. As, se consigue particularizar el
202 Introduccin a la programacin con orientacin a objetos

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.

Programa 8.3. Ilustracin de la sobrescritura de mtodos

class ClaseX {
protected int n=25;

public void imprimir(){


System.out.println(En ClaseX, n= +n);
}
}

class ClaseY extends ClaseX {

protected int m=10;

public void imprimir(){


System.out.println(En ClaseY, m= +m);
}
}

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

La gran ventaja de la sobrescritura es que podemos especificar versiones personalizadas de un


mtodo para las clases hijas. El nombre y el uso del mtodo es el mismo para todas las clases, pero el
comportamiento est totalmente adaptado a cada necesidad. As, no es necesario crear un nuevo mto-
do con nombre distinto al del padre para realizar el mismo tipo de tarea, como en el Programa 8.3 para

2
Una vez ms usamos mtodo por semejanza con la notacin de Java.
Herencia 203

imprimir una informacin.


Aunque como hemos indicado el proceso es transparente al usuario, es interesante exponer cmo
puede el sistema saber qu versin del mtodo sobrescrito tiene que activar en cada momento. En los
lenguajes donde no existe sobrescritura de mtodos 2 cuando se realiza una llamada a un mtodo no hay
ambigedad posible. En el mismo momento de la compilacin se puede establecer el enlace entre el
cdigo del mtodo invocado y el punto de invocacin. En este caso se dice que tenemos enlazamiento
esttico. El enlazamiento esttico es el soportado por los lenguajes no orientados a objetos y esto sig-
nifica que el compilador genera una llamada a un nombre especfico de mtodo y el enlazador (linker)
resuelve la llamada a la direccin absoluta del cdigo que se ha de ejecutar. Ahora, consideremos el pro-
blema cuando hay sobrescritura de mtodos. En este caso, el compilador se asegura, por un lado, de que
el mtodo existe y, por otro, realiza la verificacin de tipos de los argumentos y del valor de retorno.
Sin embargo, el compilador no conoce cul es el mtodo a ejecutar. Por ejemplo, pensemos en una cla-
se padre donde se define un mtodo para que lo herede una clase hija. Supongamos que ese mtodo
(mtodo A) usa una invocacin a otro mtodo distinto (mtodo B) que se sobrescribe en la clase hija.
Qu pasa al crear un objeto de la clase hija? Que tendremos el cdigo heredado del primer mtodo
(mtodo A) el cual usa una invocacin al cdigo sobrescrito del mtodo B en el objeto. Sin embargo,
la clase padre se puede compilar independientemente de la clase hija, as que el mtodo A no puede
saber en tiempo de compilacin qu versin del mtodo B se va a usar, vase la Figura 8.4.
En memoria slo hay una copia de cada mtodo. Si el enlazamiento es esttico, con qu Mto-
do_B enlazaramos al Mtodo_A? En funcin de lo que ocurra en la ejecucin del programa podemos
necesitar una versin u otra, y esto no es posible determinarlo de antemano. La sobrescritura implica
determinar cul es la versin a usar del mtodo sobrescrito en tiempo de ejecucin. Por lo tanto, el
enlazamiento entre el cdigo del mtodo a usar y el punto de llamada no puede ser esttico. Lo que
hace el sistema (de forma totalmente transparente para el usuario) es incluir algo ms de cdigo para
que en tiempo de ejecucin se pregunte a qu clase pertenece el mtodo invocado y as poder activar
la versin correcta de dicho mtodo. Esta forma de enlazamiento se denomina enlazamiento dinmi-
co (dynamic binding) (Bishop y Bishop, 2000; Bishop, 1999; Savitch, 1999). Como ilustracin del
problema, imaginemos un programa que lee un entero y si ste es 1 crea un objeto de Clase_Padre
y si no crea un objeto de Clase_Hija. Tendramos,
if (i==1){
Clase_Padre objeto = new Clase_Padre();

Mtodo_B
(Clase padre)

Mtodo_A ?

Mtodo_B
(Clase hija)

Figura 8.4. Si el Mtodo_B est sobrescrito es necesario determinar en tiempo de ejecucin


qu versin se activar
204 Introduccin a la programacin con orientacin a objetos

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

8.2.3. JERARQUAS DE CLASES


Hasta ahora hemos visto derivaciones por herencia simple. Esto es, hemos usado una clase para deri-
var otra nueva. Sin embargo, con la herencia se puede ir ms all, estableciendo autnticas jerarquas
de clases. Para establecer una jerarqua basta con hacer que algn descendiente de una clase padre ten-
ga a su vez clases descendientes, vase la Figura 8.6.
Una clase hija puede ser a su vez ser padre de una o ms clases, creando la jerarqua. No hay lmi-
te en el nmero de hijos que una clase puede tener, o en el nmero de niveles que constituyen la jerar-
qua. Lgicamente, un buen diseo de la jerarqua de clases mantiene las propiedades y
procedimientos comunes tan alto en la jerarqua como sea posible.
El mecanismo de herencia es transitivo, esto es, los atributos o procedimientos pasan de la clase
padre a las clases hijas y de stas a su vez pasan a sus descendientes (incluyendo en este caso los nue-
vos atributos y procedimientos declarados en la clase hija). Dicho de otra forma, el miembro (atribu-
to o procedimiento) heredado puede provenir del padre inmediato o de algn nivel anterior. En
cualquier caso, siempre debe existir en la jerarqua de clases una relacin de tipo es-un en las clases

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

Padre Primer nivel


jerrquico

Hijo 1 Hijo 2 Hijo 3 Segundo nivel


jerrquico

Nieto 1 Nieto 2 Nieto 3 Tercer nivel


jerrquico

Figura 8.6. Diagrama que muestra la organizacin jerrquica de clases

descendientes con respecto a sus antecesoras.


Respecto a la relacin de herencia podemos establecer la denominada herencia del invariante
(Meyer, 1999). Una clase descendiente tiene algo que la distingue de la antecesora. Las caractersticas
(propiedades) que distinguen una clase descendiente de una ascendiente se denominan invariantes de
la clase. Si de verdad hay una relacin de herencia, una subclase debe admitir como caracterstica suya
la unin por un y lgico de lo que la distingue a ella y a todas sus antecesoras. Es decir, a una sub-
clase le corresponden todos los invariantes de su lnea jerrquica unidos por un y lgico. Por ejem-
plo, consideremos la relacin jerrquica mostrada en la Figura 8.7, donde los invariantes de clase son
evidentes a partir del nombre de las clases.

Figura_cerrada

Polgono

Tringulo

Tringulo_equiltero

Figura 8.7. Relacin jerrquica entre clases para figuras geomtricas


206 Introduccin a la programacin con orientacin a objetos

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

Cuenta_bancaria Libreta_de_ahorro extends


Cuenta_bancaria
# numero:int
# saldo:double # interes:double

+ Cuenta_bancaria (numero int :,


saldo_inicial:double) + Libreta_de_ahorro (numero:int,
+ depositar (cantidad:double):void saldo_inicial:double,
+ retirar (cantidad:double):void interes:double)
+ saldo ( ):double + interes ( ):void

Libreta_2000 extends Cuenta_corriente extends


Libreta_de_ahorro Cuenta_bancaria
- penalizacion:double - cuenta_asociada:Libreta_de_ahorro

+ Libreta_2000 (numero:int, + Cuenta_corriente (numero:int,


saldo_inicial:double, saldo_inicial:double,
interes:double, libreta:Libreta_de_ahorro)
penalizacion:double) + retirar (cantidad:double):void
+ retirar (cantidad:double):void

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;

public Cuenta_bancaria(int numero, double saldo_inicial) {


this.numero=numero;
saldo=saldo_inicial;
}

public void depositar(double cantidad) {


System.out.println();
System.out.println(Deposito en la cuenta: +numero);
System.out.println(Cantidad depositada: +cantidad
+ Euros);
saldo+=cantidad;
System.out.println(Saldo actual: +saldo+ Euros);
}

public void retirar(double cantidad) {


System.out.println();
System.out.println(Retirada de fondos de la cuenta:
+numero);
System.out.println(Cantidad solicitada: +cantidad
+ Euros);
if (saldo < cantidad)
System.out.println(No hay fondos suficientes);
else {
saldo-=cantidad;
System.out.println(Saldo actual: +saldo+ Euros);
}
}

public double saldo() {


return saldo;
}
} // Fin Cuenta_bancaria

class Libreta_de_ahorro extends Cuenta_bancaria {


208 Introduccin a la programacin con orientacin a objetos

protected double interes;

public Libreta_de_ahorro(int numero, double saldo_inicial,


double interes) {
super(numero, saldo_inicial);
this.interes=interes;
}

public void intereses() {


System.out.println();
System.out.println(Actualizando intereses en la cuenta:
+numero);
saldo+=saldo*interes/100.0; // Esto es:
// saldo=saldo+saldo*interes/100.0
System.out.println(Saldo tras la actualizacion: +saldo
+ Euros);
}
} // Fin Libreta_de_ahorro

class Libreta_2000 extends Libreta_de_ahorro {

private double penalizacion; // Penalizacion (%) por retirada

public Libreta_2000(int numero, double saldo_inicial,


double interes, double penalizacion) {
super(numero, saldo_inicial, interes);
this.penalizacion=penalizacion;
}

public void retirar(double cantidad) {


System.out.println();
System.out.println(Retirando fondos con penalizacion del
+penalizacion+% de la cuenta: +numero);
System.out.println(Cantidad solicitada: +cantidad
+ Euros);
if (saldo < cantidad)
System.out.println(No hay fondos suficientes);
else {
saldo-=cantidad*(1.0-penalizacion/100.0);
System.out.println(Saldo actual: +saldo + Euros);
}
}
} // Fin Libreta_2000

class Cuenta_corriente extends Cuenta_bancaria {


private Libreta_de_ahorro cuenta_asociada;

public Cuenta_corriente(int numero, double saldo_inicial,


Libreta_de_ahorro libreta) {

super(numero, saldo_inicial);
cuenta_asociada=libreta;
}

public void retirar(double cantidad) {


System.out.println();
Herencia 209

System.out.println(Retirada de fondos de la cuenta:


+numero);
System.out.println(Cantidad solicitada: +cantidad
+ Euros);

if (saldo < cantidad)


if (cuenta_asociada.saldo() < (cantidad-saldo) )
System.out.println(No hay fondos suficientes);
else {
System.out.println(Retirando fondos adicionales+
de la cuenta asociada);
cuenta_asociada.retirar(cantidad-saldo);
saldo=0.0;
System.out.println(Saldo actual cuenta corriente:
+0.0 Euros);
}
else {
saldo-=cantidad;
System.out.println(Saldo actual: +saldo+ Euros);
}
}
} // Fin Cuenta_corriente

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

Libreta_de_ahorro libreta = new Libreta_de_ahorro(12345,


2000,4);
Libreta_2000 libreton = new Libreta_2000(23456,
2000,8,2);
Cuenta_corriente cuenta = new Cuenta_corriente(34567,
2000, libreta);
libreta.retirar(100);
cuenta.retirar(300);
libreton.retirar(200);
libreton.intereses();
}
}

El resultado sera,

Retirada de fondos de la cuenta: 12345


Cantidad solicitada: 100.0 Euros
Saldo actual: 1900.0 Euros

Retirada de fondos de la cuenta: 34567


Cantidad solicitada: 300.0 Euros
210 Introduccin a la programacin con orientacin a objetos

Saldo actual: 1700.0 Euros

Retirando fondos con penalizacion del 2.0% de la cuenta: 23456


Cantidad solicitada: 200.0 Euros
Saldo actual: 1804.0 Euros

Actualizando intereses en la cuenta: 23456


Saldo tras la actualizacion: 1948.32 Euros

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

class nombre_clase extends Object {


}

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

Programa 8.5. Uso del mtodo toString()

class Alumno{
public String nombre;
public int matricula;

// Constructor
Alumno(String nombreAlumno, int matriculaAlumno){
nombre = nombreAlumno;
matricula = matriculaAlumno;
}

// Sobrescritura del mtodo toString


public String toString( ) {
return Los datos del alumno son:+ nombre+ + matricula;
}

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

La salida del Programa 8.5 sera,

Los datos del alumno son: Juan 123


Los datos del alumno son: Maria 124

Obsrvese en el Programa 8.5 que en los mtodos System.out.println(alumno1) y


System.out.print(alumno1) se usan directamente los objetos, sin necesidad de invocar el mtodo
toString(). Esto puede considerarse una sintaxis abreviada de la invocacin a toString(), y se reali-
zara como System.out.println(alumno1.toString()) y System.out.print(alumno2.toS-
tring()). Si no hubisemos sobrescrito el mtodo toString() y se hubiera utilizado el mtodo tal y
como se hereda de la clase Object la salida del Programa 8.5 hubiese sido:

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.

8.2.4. ORGANIZACIN DE JERARQUAS DE CLASES (PAQUETES)


Usando jerarquas de clases podemos agrupar clases relacionadas pero, hay alguna forma de agrupar
varias jerarquas de clases? Por ejemplo, supongamos una coleccin de clases que permiten simular un
coche. Tenemos una jerarqua de clases para tipos de carroceras, otra para ruedas, otra para motores, etc.
Es conveniente agrupar juntas esas jerarquas distintas bajo un nico nombre que muestre que estn con-
ceptualmente relacionadas. En Java tenemos un mecanismo para agrupar diferentes jerarquas de clases y
referirlas con un nico identificador, se trata de los paquetes 3. Un paquete es una coleccin de clases agru-
padas juntas bajo un solo nombre. Cuando se agrupan varias jerarquas en un paquete es porque de algu-
na manera se considera que dichas jerarquas tienen algo en comn. Las clases en un paquete no tienen
por qu estar relacionadas por herencia.
Para poder trabajar con paquetes tenemos que hacer dos cosas:

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

package nombre_ paquete;

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.*;

public class Lectora {

static BufferedReader leer = new BufferedReader


(new InputStreamReader(System.in));

public static int read_int() throws IOException {


return Integer.parseInt(leer.readLine());
} // Fin mtodo read_int

public static String read_line() throws IOException {


return leer.readLine();
} // Fin mtodo read_line

} // Fin clase Lectora

La clase Lectora ahora pertenece al paquete LecturaComoda. Despus de compilar la clase, el


compilador crear el fichero Lectora.class. La clase Lectora se usara igual que hemos hecho
con la clase predefinida Math. Es decir, haramos Lectora.read_int() para leer un entero por
teclado. Los ficheros compilados se deben copiar en un subdirectorio 4 con el mismo nombre que el
paquete. En nuestro ejemplo deberamos copiar Lectora.class al subdirectorio .../LecturaCo-
moda/.

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

El emplazamiento del directorio donde se encuentran los paquetes se identifica normalmente en la


variable de entorno CLASSPATH. Es conveniente consultar la documentacin del compilador y el sis-
tema operativo que estemos usando para ms informacin sobre variables de entorno y subdirectorios.
Para ms informacin sobre la variable CLASSPATH, vase Eckel, 2002.
Los nombres de los paquetes pueden contener un punto (.). Se usa normalmente para indicar algu-
na relacin entre las partes de dos o ms paquetes. Por ejemplo, java.lang y java.io son dos
paquetes diferentes pero estn relacionados. Los ficheros .java y .class del paquete lang se alma-
cenan en un directorio llamado java/lang y las del paquete io en un subdirectorio llamado
java/io. Para localizar el subdirectorio, el punto se transforma en la barra normal, /, o la barra inver-
tida, \, en funcin del sistema operativo utilizado.
Hasta ahora hemos visto cmo se define un paquete, vamos a ver cmo se usa. Para usar una cla-
se de un paquete debemos incluir la sentencia:
import nombre_ paquete.nombre_clase;

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

} // Fin mtodo main


} // Fin clase

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

} // Fin mtodo main


} // Fin clase

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.

Programa 8.8. Ilustracin del uso de referencias polimrficas

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;

polimorfico=padre; // hace referencia a un objeto padre


polimorfico.dondeEstoy();

polimorfico=hijaPrimera; //hace referencia a un objeto


//hijaPrimera
polimorfico.dondeEstoy();

polimorfico=hijaSegunda; //hace referencia a un objeto


//hijasegunda
polimorfico.dondeEstoy();
}
}

La salida del Programa 8.8 es:


216 Introduccin a la programacin con orientacin a objetos

Estoy en el metodo del Padre


Estoy en el metodo llamada de la HijaPrimera
Estoy en el metodo llamada de la HijaSegunda

En el Programa 8.8 las asignaciones polimorfico=padre, polimorfico=hijaPrimera y


polimorfico=hijaSegunda son asignaciones polimrficas. Una entidad tal como la variable
polimorfico que aparece en alguna asignacin polimorfa es una entidad polimorfa.
Como muestra la salida del Programa 8.8, la versin del mtodo ejecutada se determina en funcin
del tipo de objeto que est siendo referido en el momento de realizar la llamada. Si hubiese sido deter-
minado por la clase de la referencia polimorfico, se hubiesen realizado tres llamadas al mtodo
dondeEstoy() de la clase Padre. Cuando vamos reasignando la referencia polimorfico, los obje-
tos liberados no se vuelven a usar, as que son candidatos a la recogida de basura. Una consideracin
interesante es que al derivar en Java, en ltima instancia, todos las clases de la clase Object, cual-
quier objeto se puede asignar a una referencia Object.
Para entender la utilidad del polimorfismo imaginemos un mtodo que acepta como parmetro una
referencia polimrfica. Dentro del mtodo se usar la referencia de forma normal, invocando los mto-
dos sobrescritos sin mayor problema (de hecho ni siquiera necesitaramos saber qu son mtodos
sobrescritos). Fijmonos en que el mtodo slo acepta la referencia, pero l no realiza la asignacin de
la referencia a un objeto de una clase o de otra. Por lo tanto, el mtodo es el mismo, independiente-
mente de la clase a la que pertenezca el objeto referido por la referencia. sta es la potencia del poli-
morfismo. Yo podra cambiar entera la estructura jerrquica de las clases de las que uso la referencia
polimrfica, y el mtodo considerado no habra que cambiarlo nunca. En el ejemplo de las vacaciones
podramos tener un mtodo en otra clase que imprimiera los das de vacaciones, invocando al mtodo
duracion() anteriormente mencionado. Este mtodo podra ser:

public void dias(Vacaciones mis_vacaciones) {


mis_vacaciones.duracion();
}

y el mtodo sera el mismo independientemente de a qu clase de objeto refiera mis_vacaciones.


Otro ejemplo tpico es el de una estructura de datos de referencias polimrficas. Por ejemplo, ima-
ginemos que necesitamos una lista, implementada como una matriz monodimensional de objetos de
clase Navidades y otra de objetos de clase Verano. Sin polimorfismo no hay ms solucin que usar
dos matrices. Sin embargo, con el polimorfismo podramos usar una sola matriz de referencias de cla-
se Vacaciones. El polimorfismo nos permite ir vinculando cada elemento de esta matriz a objetos de
clase Navidades o Verano, segn convenga. Es decir, slo hace falta una estructura de datos.

8.3. MECANISMOS ADICIONALES DE ABSTRACCIN BASADOS EN HERENCIA


En este punto consideraremos dos mecanismos de abstraccin a usar en conjuncin con la herencia.
En primer lugar consideraremos las clases abstractas y su relacin con las subclases. En segundo lugar
introduciremos las interfaces, que nos permiten definir la especificacin de un conjunto de mtodos.
Comencemos con las clases abstractas.

8.3.1. CLASES Y MTODOS ABSTRACTOS


En programacin orientada a objetos no es necesario que todos los mtodos de una clase estn imple-
mentados. Puede ser conveniente disponer de clases en las que todos los procedimientos estn recogidos
Herencia 217

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

Figura 8.11. Notacin UML de una clase abstracta


218 Introduccin a la programacin con orientacin a objetos

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}

Deportivo Turismo Familiar

Figura 8.12. Relacin de herencia en el ejemplo de los automviles

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

Programa 8.9. Uso de clases y mtodos abstractos (continuacin)

Programa 8.9. Uso de clases y mtodos abstractos

abstract class Automovil {


abstract public void eslogan();
}

class Deportivo extends Automovil {


public void eslogan() {
System.out.println(Veloz como el rayo);
}
}

class Turismo extends Automovil {


public void eslogan() {
System.out.println(Para el uso diario);
}
}

class Familiar extends Automovil {


public void eslogan() {
System.out.println(Cabe hasta el gato);
}
}

// Clase que contiene el mtodo main

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

public static void imprime_eslogan(Automovil [] auto) {


for (int i=0; i<=2; i++)
auto[i].eslogan();

}
}

El resultado del programa sera:

Veloz como el rayo


Para el uso diario
Cabe hasta el gato

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

pendientemente de las modificaciones que hiciramos en la estructura jerrquica, en tanto y en cuan-


to siga existiendo el mtodo eslogan(). Este uso de una clase abstracta es una tcnica comn para
escribir clases reutilizables. Podemos reemplazar toda la jerarqua de clases que emergen de la clase
Automovil por una jerarqua completamente diferente, sin afectar para nada al mtodo
imprime_eslogan(). El polimorfismo es muy til tambin a la hora de definir estructuras de datos,
pues todos los elementos de la estructura se pueden declarar de un solo tipo usando referencias
polimrficas de la clase padre.

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
}

En la declaracin de los mtodos se debe incluir el tipo de retorno y la lista de parmetros.


Adems, slo est permitido el uso de los modificadores public y abstract. Sin embargo, no son
necesarios, porque por defecto los mtodos de una interfaz son pblicos y abstractos. Las constantes
en una interfaz son siempre public y final.
Las interfaces no se usan por s mismas sino que se implementan (una especie de herencia) en
una clase. La clase en cuestin proporciona las implementaciones para cada uno de los mtodos defi-
nidos en la declaracin de la interfaz. En Java se usa la palabra clave implements para indicar que
una clase implementa una interfaz. La sintaxis es:

class nombre_clase implements nombre_interfaz {


implementacin de los mtodos de la interfaz
}

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

class nombre_clase implements interfaz_1,..., interfaz_n {


--- cuerpo de la clase ---
}

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

donde p es la presin, V el volumen, n el nmero de moles, Na la constante de Avogadro, k la cons-


tante de Boltzmann y T la temperatura, vase el Programa 8.10.

Programa 8.10. Ilustracin del uso de una interfaz

interface Constantes_fisicas {
double Na=6.023e23; // Constante de Avogadro
// en (partculas/mol)

double k=1.38066e-23; // Constante de Boltzmann en J/K


}

class Herencia implements Constantes_fisicas {


public static void main(String[] args) {
double p, T, v;
p = Double.parseDouble(args[0]); // p en pascales (SI)
// 1Pa= 1 Newton/(m*m)
// 1atm = 101325 Pa
T = Double.parseDouble(args[1]);
System.out.println(Presion: +p + Pa);
System.out.println(T: +T+ K);

v=k*Na*T/p; // Volumen molar (metros cbicos)


v=v*1000.0; // litros (1 metro cubico = 1000 litros)

System.out.println(Volumen molar: +v+ litros);


}
222 Introduccin a la programacin con orientacin a objetos

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 Clase2 extends Clase1 {


public void imprimir(int i){
prop1=prop1+i;
prop2=prop2+i;
System.out.print(prop1+ +prop2);
}
}
Herencia 223

Ejercicio 2.* Cul es el resultado del siguiente programa?

class Uno {
int i=2;
public void frase() {
System.out.println(Estoy en un objeto de clase
Uno);
}
}

class Dos extends Uno {


public void frase() {
i=3;
System.out.println(Estoy en un objeto de clase Dos
con i:+i);
}
}
class Tres extends Dos {
public void frase() {
System.out.println(Estoy en un objeto de clase Tres
con i:+i);
}
}

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();
}
}
}

Ejercicio 3.* Cul es el resultado del siguiente programa?

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

Ejercicio 4.* Cul es el resultado del siguiente programa?

class Uno {
public void imprime(double x) {
System.out.println(Valor de la variable pasada:
+x);
}
}

class Dos extends Uno {


public void imprime(int j) {
System.out.println(Valor de la variable: +j);
}
}

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 7.* Implemente el cdigo de una clase abstracta denominada ObjetoGra-


fico que represente un objeto que se puede dibujar. Esta clase debe
contener dos atributos que indiquen las coordenadas de la figura. La
clase debe tener tambin dos mtodos, uno debe ser abstracto y otro
no. Los nombres de los mtodos son: mueveObjeto, que desplaza las
coordenadas x e y de la figura diez posiciones y el mtodo dibujar
que dibuja la figura. Implemente dos clases Rectangulo y Circunfe-
rencia que hereden de la clase ObjetoGrafico.

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.

9.2. DEFINICIN Y USO


Desde un ms bien alto nivel de abstraccin un fichero es una coleccin de datos estructurados que se
manejan como un todo (Prieto et al., 1995; Ruiz et al., 1998). Los ficheros estn organizados en uni-
dades elementales, todas con la misma estructura, que se denominan registros y que a su vez constan
de unidades denominadas campos, vease la Figura 9.1. Desde el presente punto de vista, un fichero es
un conjunto de registros que a su vez son un conjunto de campos.

Fichero

Registros

Campos

Figura 9.1. Organizacin de un fichero

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:

Nmero de identificacin; Nombre; Direccin; Telfono; Categora; Salario

El Nmero de identificacin podra ser un entero, el Nombre y Direccin cadenas, el Telfono un


entero o una cadena, la Categora podra estar codificada usando un carcter y el Salario podra ser un
real. Como se puede observar, cada elemento de informacin (campo) puede ser de tipo diferente. El
conjunto de los campos define el registro en s, es decir, un registro es un conjunto de campos rela-
cionados entre s. El registro contiene informacin relativa a una entidad particular, en el ejemplo ante-
rior un empleado. El registro se usa de manera unificada en un programa, y es la unidad para el uso de
un fichero. Un registro tal como lo hemos considerado es un registro lgico (abstracto). Cuando se
desea transferir informacin del almacenamiento secundario a la memoria central se puede transferir
un registro lgico, menos de un registro lgico o incluso ms de un registro lgico. Esta unidad de
transferencia se denomina registro fsico o bloque. El nmero de registros lgicos por registro fsico
se denomina factor de bloqueo (o ms correctamente factor de bloque).
Un conjunto de registros relacionados entre s define un fichero. Por lo tanto, un fichero es un con-
junto de registros que tienen algo en comn. En el ejemplo anterior, un fichero de empleados con-
tendra la informacin de los empleados de una empresa con un registro para cada empleado y una
serie de campos para los elementos de informacin de cada empleado.
Para facilitar la recuperacin de registros especficos de un fichero, se escoge por lo menos un
campo de cada registro como clave de registro. Una clave de registro identifica un registro como per-
teneciente a una entidad en particular y como distinto de todos los dems registros del fichero. En el
ejemplo anterior el nmero de identificacin del empleado se podra escoger como clave del registro.
Para buscar y recuperar un empleado (registro) determinado dentro de un fichero lo que haramos sera
localizar el registro cuya clave es la que buscamos.

9.3. CLASIFICACIN DE FICHEROS


Los ficheros pueden clasificarse desde distintos puntos de vista. Sin embargo, como los ficheros se
implementan sobre memoria secundaria, el tipo de fichero est relacionado con el soporte fsico sobre
el que se implementan. El soporte es el medio fsico donde se almacenan, donde se graban, los datos.
Los tipos de soporte utilizados en la gestin de ficheros pueden ser (Joyanes, 1997):

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.

9.3.1. CLASIFICACIN DE ACUERDO AL TIPO DE ACCESO


Retomando ahora los ficheros como entidad, la primera distincin que podemos hacer es relativa al
tipo de acceso. El tipo de acceso a un fichero se refiere a la forma en la que se puede acceder a un
registro concreto, es decir, la manera en la que se localiza un registro. Segn las caractersticas del
soporte empleado y el modo en que se han organizado los registros, se consideran dos tipos de acce-
so a los registros de un fichero:

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.

9.3.2. CLASIFICACIN DE ACUERDO AL TIPO DE ORGANIZACIN


La organizacin de un fichero define la forma en que los registros se disponen sobre el soporte de
almacenamiento o la forma en la que se estructuran los datos en un fichero. La forma de organizacin
se decide cuando se disea el fichero y, lgicamente, desde un punto de vista fsico slo se podr
implementar un tipo determinado de organizacin sobre un soporte fsico que la admita. En general,
se consideran tres organizaciones:

a) Organizacin secuencial.
b) Organizacin directa o aleatoria.
c) Organizacin secuencial indexada.

En la organizacin secuencial se disea el fichero como una sucesin de registros almacenados


consecutivamente sobre el soporte externo, de tal forma que para acceder a un registro n hay que pasar
por los n-1 anteriores. Esta organizacin implica un tipo de acceso secuencial. Los registros se graban
consecutivamente cuando se crea el fichero y se debe acceder consecutivamente cuando se leen dichos
registros. El orden fsico en que fueron grabados (escritos) los registros es el orden de lectura de los
mismos. Existe un registro especial, que es el ltimo, e indica el fin del fichero. Este registro lo inclu-
ye el sistema automticamente (no lo tiene que incluir el programador) y se suele denominar EOF (End
Of File), o marca de fin de fichero. Todos los dispositivos de memoria auxiliar soportan la organiza-
cin secuencial.
Ficheros 229

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

Figura 9.2. Ejemplo de organizacin secuencial indexada

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.

9.3.3. CLASIFICACIN DE ACUERDO AL FORMATO

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.

9.4. OPERACIONES SOBRE LOS FICHEROS

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

9.5. FICHEROS EN JAVA


En un lenguaje determinado necesitaremos saber cmo crear ficheros de los distintos tipos que hemos
mencionado. Tambin es necesario conocer cmo se realizan cada una de las operaciones citadas ante-
riormente. Consideremos este problema desde el punto de vista del lenguaje Java.
Como ya comentamos anteriormente, en Java todos los tipos de entradas y salidas se realizan por
stream (corriente) y se habla de corrientes de entrada o de salida. Un stream es un flujo de datos.
No hay sentencias de entrada y salida en Java. La entrada y salida se realiza usando bibliotecas de cla-
ses predefinidas. La mayora de las operaciones de entrada-salida estn definidas en el paquete
java.io (io es un acrnimo de input output) de la API de Java. Por las razones anteriores, Java con-
sidera los ficheros simplemente como flujos secuenciales de bytes. Cada fichero termina con un mar-
cador de fichero o bien en un nmero de byte especfico registrado en una estructura de datos
administrativa mantenida por el sistema. Cuando se abre un fichero se crea un objeto y se asocia un
flujo (stream) a dicho objeto. A partir de ese momento, ese objeto es el fichero para nuestro pro-
grama.
En el manejo de ficheros es especialmente til capturar las excepciones que se pueden producir.
Como ya introdujimos en un captulo anterior, una excepcin se puede entender como un problema
recuperable. Esto es, nuestro programa puede detectar que se ha producido el problema y actuar en
consecuencia para resolverlo. Si en el lenguaje que manejamos existe un mecanismo de gestin de
excepciones, es necesario conocerlo. Por esta razn vamos a presentar el mecanismo de captura y ges-
tin de excepciones particularizando la sintaxis a Java.

9.5.1. MANEJO DE EXCEPCIONES


Un programa puede llegar a encontrar una situacin problemtica que implique la finalizacin del mis-
mo. Si la causa de este problema se puede identificar, puede ser posible evitar que el programa finali-
ce a causa del mismo. Este tipo de problemas recuperables son las excepciones. Existe una clara
diferencia entre excepcin y error. En ambos casos tenemos una situacin problemtica, pero la excep-
cin se puede capturar y hacer que el programa siga funcionando y el error es irrecuperable y hace que
el programa termine. Para manipular las excepciones debemos disponer de algn mecanismo de ges-
tin de excepciones. Una excepcin se puede producir en distintos casos, por ejemplo, cuando:

El ndice de una matriz (array) est fuera de los lmites permitidos.


Se intenta dividir entre cero.
Se intenta leer de un fichero que no existe.

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

-- bloque de sentencias para el caso normal --


}
catch(excepcin_1 identificador_1) {
-- bloque de sentencias para el caso de producirse la excepcin_1 --
}
...
catch(excepcin_n identificador_n) {
-- bloque de sentencias para el caso de producirse la excepcin_n --
}

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:

public class NombreException extends Exception {


public NombreException( ){ //Constructor
-- cdigo del mtodo --
}
}

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:

throw new nombreExcepcion();

Por ejemplo:

if (denominador == 0)
throw new DivideporCeroExcepcion();

Si en un mtodo se pueden producir (lanzar) excepciones pero no se capturan se debe indicar en


la cabecera del mtodo que no queremos considerar esas excepciones. Dicho de otra forma, que vamos
234 Introduccin a la programacin con orientacin a objetos

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

En el ejemplo anterior, se puede producir la excepcin DivideporCeroExcepcion. Como no


hay una clusula catch para ella es necesario indicar el throws en la cabecera del mtodo. Otro ejem-
plo tpico sera throws IOException que hemos ido escribiendo en los programas de captulos ante-
riores porque no se capturaba dicha excepcin.
Los catch se van considerando en el orden de escritura, de forma que se deben colocar las
excepciones ms especficas primero y las ms generales despus. Por ejemplo, consideremos la
relacin de herencia existente en Java entre las clases IndexOutOfBoundsException,
ArrayIndexOutOfBoundsException, y ArrayIndexOutOfBoundsException, ilustrada en la
Figura 9.3.

IndexOutOfBoundsException

StringIndexOutOfboundsException ArrayIndexOutOfBoundsException

Figura 9.3. Relacin de herencia entre las clases consideradas

Para manejar las excepciones IndexOutOfBoundsException y ArrayIndexOutOfBoundsEx-


ception un programa tendra que tener la forma indicada en el Programa 9.1.

Programa 9.1. Ejemplo de excepciones

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

ArrayIndexOutOfBoundsException es una excepcin que hereda de IndexOutOfBoundsEx-


ception y, por lo tanto, la primera excepcin es ms especfica. El programa anterior funcionara nor-
malmente, pero si ponemos IndexOutOfBoundsException como primera excepcin obtendramos un
Ficheros 235

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
}

En el Programa 9.2 se muestra un ejemplo de excepciones definidas por el usuario.

Programa 9.2. Ejemplo de excepciones definidas por el usuario

class Aexception extends Exception {


public Aexception() {
}
}

class Bexception extends Exception {


public Bexception() {
}
}

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

La salida para una entrada con valor 3 es:

Error tipo A
Ahora me toca a mi
Imprimeme
236 Introduccin a la programacin con orientacin a objetos

Para una entrada 5 la salida sera:

Error tipo B
Ahora me toca a mi
Imprimeme

Para una entrada distinta de 3 y 5 la salida sera:

llega hasta aqui


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.

9.5.2. FICHEROS DE ACCESO SECUENCIAL


Java no obliga a los ficheros a tener una estructura, por tanto, el concepto de registro no existe. Esto
implica que el programador debe estructurar los ficheros desde un punto de vista lgico para satisfa-
cer las necesidades de las aplicaciones. El programador debe imponer una estructura de registros al
fichero. Como ya hemos indicado, un registro se compone de varios campos que en Java sern varia-
bles.
Recordemos que en Java la entrada/salida se realiza por medio de corrientes (streams) de datos.
De dnde venga o a dnde vaya dicha corriente no afecta al hecho de tomar o poner datos en ella.
Recordemos tambin que en Java los Streams son corrientes de 8 bits y que para una adecuada inter-
nacionalizacin con Unicode se introdujeron los Readers y Writers que son corrientes de entrada o
salida de 16 bits.
Con estas ideas generales, comencemos con la exposicin de los ficheros secuenciales que pueden
ser de tipo binario o de tipo texto.

Ficheros secuenciales binarios

En Java podemos trabajar de dos formas con los ficheros secuenciales binarios:

a) Directamente byte a byte.


b) Como datos pasados a byte.

Consideremos las dos posibilidades.

a) Trabajo byte a byte

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:

FileOutputStream(String nombre_fichero, boolean aadir)

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.

Programa 9.3. Escritura de informacin en un fichero binario de acceso secuencial usando la


clase FileOutputStream

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

/* Si no ha habido error de lectura se escribe un fichero


de enteros */
for (int i=0;i<99;i++) {
salida.write(i);
}
238 Introduccin a la programacin con orientacin a objetos

Programa 9.3. Escritura de informacin en un fichero binario de acceso secuencial usando la


clase FileOutputStream (continuacin)
salida.close();

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

} // Fin mtodo main

} // Fin clase Ficheros

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.

Programa 9.4. Ejemplo de la clase FileInputStream

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

/* Si no ha habido error de lectura se escribe un fichero


de enteros */
do {
i=entrada.read();
sigue=(i!=-1);
if (sigue) {
salida.write(i);
System.out.println(i); // Visualizando contenido
}
} while (sigue);

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

Programa 9.4. Ejemplo de la clase FileInputStream (continuacin)


catch(IOException e) {
System.out.println(Excepcion de entrada/salida:
+e.toString());
}

} // Fin metodo main

} // Fin clase Ficheros

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) Datos pasados a bytes


Escribir o leer datos como bytes puros y duros es rpido pero burdo, por lo que se usan las clases
DataOutputStream y DataInputStream que permiten trabajar con tipos primitivos. En cualquier
caso, el fichero al final est en formato binario. De forma anloga al caso de los ficheros binarios de
bytes organicemos la exposicin en escritura y lectura.

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:

DataOutputStream salida=new DataOutputStream


Ficheros 241

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

writeInt(variable_tipo_entero): escribe un entero de tipo int.

writeUTF(objeto_tipo_cadena): escribe una cadena en formato UTF (formato independiente de


la plataforma) que usa 8 bits para los caracteres alfabticos normales.

writeDouble(variable_tipo_doble): escribe un dato de tipo double.

writeFloat(variable_tipo_float): escribe un dato de tipo float.

writeChar(variable_tipo_carcter): escribe un dato de tipo char.

De forma anloga tenemos mtodos para el resto de tipos primitivos como: writeBoolean, wri-
teByte, writeLong, writeShort, etc.

Por ejemplo, para escribir un entero en un fichero llamado salida haramos:

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.

Programa 9.5. Ilustracin del uso de la clase DataOutputStream

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

salida=new DataOutputStream(new BufferedOutputStream


(new FileOutputStream(teclado.readLine())));

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

Programa 9.5. Ilustracin del uso de la clase DataOutputStream (continuacin)

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());
}

} // Fin metodo main

} // Fin clase Ficheros

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,

readChar(): lee y devuelve un carcter


Ficheros 243

readDouble(): lee y devuelve un valor de tipo double

readInt(): lee y devuelve un entero de tipo int


244 Introduccin a la programacin con orientacin a objetos

Programa 9.6. Ilustracin del uso de la clase DataInputStream (continuacin)

readFloat(): lee y devuelve un valor de tipo float

readLine(): lee una lnea devolviendo una cadena (clase String)

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.

Programa 9.6. Ilustracin del uso de la clase DataInputStream

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

System.out.println(Nombre de fichero incorrecto);


seguir=false; // Para no entrar en el if de abajo
} // Fin del segundo try-catch
// Si no se ha producido la FileNotFoundException se sigue
if (seguir) {
while (seguir) {
try { // try interno al while
valor=entrada.readDouble();
System.out.println(valor);
}
catch(EOFException e) {
System.out.println(Alcanzado el fin del fichero);
seguir=false; // Diciendo al while que acabe
} // Fin del try-catch interno al while

} // Fin del while

entrada.close(); // Cerrando el fichero

} // Fin del if

} // Se cierra el try ms externo

catch(IOException e) {
System.out.println(Excepcion de entrada/salida+
e.toString());

} // Este catch corresponde al try ms externo

} // Fin mtodo main


} // Fin clase Ficheros

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.

Ficheros secuenciales de texto


Para trabajar con ficheros secuenciales de texto se pueden usar las clases BufferedReader y
PrintWriter para escritura y lectura, respectivamente. Veamos cmo.

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

System.out.println() o System.out.print() para la pantalla. Para abrir un fichero para escri-


tura debemos crear un stream de la clase PrintWriter, conectndolo al fichero de texto con el
siguiente constructor,

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.

Programa 9.7. Ilustracin del uso de la clase PrintWriter

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

System.out.println(Introduzca tres lineas de texto:);


for (int cont=1;cont<4; cont++) {
linea=entrada.readLine();
salida.println(cont+ +linea);
}
entrada.close();
salida.close();
System.out.println(Estas lineas se han escrito en
+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

Programa 9.8. Ilustracin de lectura-escritura en ficheros secuenciales de texto (continuacin)

} //Fin clase Ficheros

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:

BufferedReader entrada=new BufferedReader(


new FileReader(fichero.txt);
char car;
car=(char)(entrada.read());

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.

Programa 9.8. Ilustracin de lectura-escritura en ficheros secuenciales de texto

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();
}

System.out.println(cont + lineas escritas en


+fichero2);

} // Fin del try interno

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());
}

} //Fin metodo main


} //Fin clase Ficheros

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.

9.5.3. FICHEROS DE ACCESO DIRECTO


Hasta ahora hemos visto cmo crear y leer ficheros de acceso secuencial. Estos ficheros no son los
apropiados para aplicaciones donde debemos acceder de inmediato a un determinado registro de infor-
macin. Como ejemplos de aplicaciones donde es necesario acceso directo podramos citar sistemas
Ficheros 249

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.

Creacin de un fichero de acceso directo


Cuando de un fichero de acceso directo se trata, utilizamos la clase RandomAccessFile (fichero de
acceso aleatorio). No tenemos clases distintas para lectura y escritura. Los objetos RandomAccessFi-
le (fichero de acceso aleatorio) tienen todas las capacidades de los objetos de clase DataInputS-
t r e a m
y DataOutputStream que hemos expuesto en el anteriormente. Cuando se asocia un flujo
RandomAccessFile a un fichero, los datos se leen o escriben a partir de la posicin en que nos
encontremos en el fichero. Todos los datos se leen o escriben como tipos de datos primitivos. Por
ejemplo, al escribir un valor int, se envan 4 bytes al fichero. Al leer un valor double, se recuperan
8 bytes del fichero. El tamao de los diversos tipos de datos est garantizado porque Java tiene
tamaos fijos para todos los tipos de datos primitivos, sea cual sea el sistema (ordenador, sistema ope-
rativo) de trabajo.
La clase RandomAccessFile tiene dos constructores:

RandomAccessFile(File objeto_fichero, String modo)

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

Programa 9.9. Ilustracin del uso de la clase RandomAccessFile (continuacin)

Figura 9.4. Estructura de un fichero de acceso directo en Java

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

Escritura en un fichero de acceso directo


Para crear y escribir en un fichero de este tipo debemos crear un objeto de clase RandomAccessFi-
le. Al constructor se le pasan dos argumentos, el primero de clase String o de clase File indican-
do el nombre del fichero, y el segundo con el modo de apertura del fichero. En este caso el modo ser
lectura-escritura, rw:

RandomAccessFile salida;
salida=new RandomAccessFile(nombre,rw);

Al igual que en los ficheros de acceso secuencial se debe capturar la IOException.


Una vez abierto el fichero, o creado si no existe, escribiremos en l. Existen varios mtodos an-
logos a los de la clase DataOutputStream. Algunos de ellos son:

writeInt(entero): Escribe un entero de tipo int.

writeDouble(doble): Escribe un dato de tipo double.

writeBytes(cadena): Escribe una cadena como una secuencia de bytes.

writeUTF(String): Escribe una cadena usando el formato UTF (formato independiente de la


plataforma) que usa 8 bits para los caracteres normales. Este formato aade 2 bytes al principio, que
indican la cantidad de bytes que conforman la cadena. Por eso, si escribimos n caracteres en UTF, en
el fichero se escribirn n * 1 byte 1 2 bytes.

Como siempre usaremos el mtodo close para cerrar el fichero.


Como ejemplo, construyamos un programa que aade a un fichero una serie de registros formados
por un nmero de orden (de tipo int) y valores reales introducidos por teclado, vase el Programa 9.9.
Ficheros 251

Programa 9.9. Ilustracin del uso de la clase RandomAccessFile

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

salida=new RandomAccessFile(teclado.readLine(), rw);

salida.seek(salida.length()); /* Movindose al final


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());
}

} // Fin mtodo main

} // Fin clase Ficheros

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.

Lectura en un fichero de acceso aleatorio


252 Introduccin a la programacin con orientacin a objetos

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

donde, como habitualmente, nombre es un objeto de clase String o File.


Tambin aqu tenemos mtodos similares a los de la clase DataInputStream. Algunos de tales
mtodos son:

readInt(): lee y devuelve un entero de tipo int.

readDouble(): lee y devuelve un dato de tipo double.

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:

int (4 bytes) 1 nombre (30 1 2 bytes) 1 double (8 bytes) 5 44 bytes

Podemos asignar a una variable el tamao del registro:

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

Figura 9.5. Relacin registros-bytes en un fichero de clase RandomAccessFile

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.

Programa 9.10. Ejemplo de acceso directo a registros en un fichero

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;

l_registro=12; // 4 bytes (int)+ 8 bytes (double)

try {
teclado=new BufferedReader(new InputStreamReader(
System.in));
System.out.println(Introduzca el nombre del fichero:);

entrada=new RandomAccessFile(teclado.readLine(), r);

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

} // Fin mtodo main

} // Fin clase Ficheros

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.

9.5.4. LA CLASE FILE


Esta clase no trabaja sobre flujos como la mayora de las clases definidas en java.io. Trabaja direc-
tamente con los ficheros y el sistema de ficheros, es decir, los objetos de la clase File no abren real-
mente un fichero ni ofrecen funciones de procesamiento de ficheros, slo se usan para describir las
propiedades de un fichero. Un objeto File se utiliza para obtener o modificar informacin asociada
con un fichero de disco, como los permisos, hora, fecha y subdirectorio, o para navegar por la jerar-
qua de subdirectorios. Esta clase es de gran utilidad para recuperar del disco informacin acerca de
un fichero o de un subdirectorio concreto. Una utilidad importante de esta clase es la verificacin de
si un fichero existe. Ya comentamos que si abrimos directamente con FileOutputStream un fiche-
ro que ya existe, se borra toda la informacin. Con la clase File podramos ver si el fichero existe y
si es as abrir el fichero en modo aadir, o por lo menos advertir al usuario que se borrar toda la infor-
macin contenida en el fichero.
Un objeto de clase File se inicializa con uno de los tres constructores siguientes:

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.

File(String directorio, String nombre)

donde directorio incluye la trayectoria hasta llegar al directorio o fichero indicado por nombre.

File(File directorio, String nombre)


Ficheros 255

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.

Un ejemplo de creacin de un objeto de clase File asociado a un fichero sera:

File fichero=new File(datos.dat);

Algunos mtodos de la clase File son:

exists(): Devuelve true si el nombre especificado como argumento en el constructor de la cla-


se File es un fichero o directorio que est en el camino especificado. Devuelve false en caso con-
trario.

getName(): Devuelve un String con el nombre del fichero o directorio.

length(): Devuelve un long que nos da la longitud del fichero en bytes.

lastModified(): Devuelve un long que es una representacin, dependiente del sistema, de la


fecha y hora en que se modific por ltima vez el fichero o directorio. El valor devuelto slo sirve para
hacer comparaciones con otros valores devueltos por este mtodo.
list(): Devuelve una matriz de objetos String que representa el contenido del directorio.

delete(): Borra el fichero o directorio. Devuelve true si se ha borrado, y false si no se puede


borrar.

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.

9.5.5. FICHEROS Y OBJETOS


Respecto al uso de ficheros y objetos existe una posibilidad no relacionada con lo visto hasta ahora en el
captulo. Se trata de la posibilidad de salvar objetos como tales en un fichero. Es importante distinguir este
caso de lo considerado hasta el momento. La exposicin realizada en el captulo nos ha mostrado cmo
usar los ficheros como una estructura de datos externa. Es posible por lo tanto mantener ms informacin
en el fichero que la que cabra en la memoria central. Lo que se plantea en este apartado es guardar en un
fichero un objeto existente en la memoria. No se trata, por lo tanto, de otra forma de trabajar con fiche-
ros, entendidos como estructuras de datos permanentes, sino de una manera de salvar en un fichero la
informacin contenida en un objeto en un momento concreto y poder recuperarla posteriormente.
En Java para poder escribir y leer un objeto de una clase en un fichero debe indicarse que la clase
implementa la interfaz Serializable de la forma siguiente,

class Ejemplo implements Serializable {

- Cdigo para la clase Ejemplo -

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,

a) Creacin de un stream de objetos para salida:

ObjectOutputStream salida;
salida=new ObjectOutputStream(FileOutputStream(nombre));

b) Creacin de un stream de objetos para entrada:

ObjectInputStream entrada;
entrada=new ObjectInputStream(FileInputStream(nombre));

donde nombre, de tipo String, identifica el fichero a usar.


Tras crear las corrientes se usan los mtodos,

writeObject(Objeto): Escribe el Objeto en la corriente de salida.

readObject(): Lee y devuelve el Objeto de la corriente de entrada.

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

Para informacin ms detallada sobre la serializacin de objetos en Java consltense Arnold et


al., 2000; Lambert y Osborne, 1999. Se puede encontrar informacin especfica sobre el paquete
java.io y sus clases en la documentacin del Java 2 SDK de Sun (Java, 2002) y en el libro (Harold,
1999).

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

donde cada lnea representa un registro, qu valores se leeran al


hacer fichero.readInt() seguido de fichero.readDouble()?

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.

Ejercicio 6.* A fin de ocultar los detalles de implementacin, disee y programe


una clase para escritura en un fichero secuencial byte a byte.

Ejercicio 7.* A fin de ocultar los detalles de implementacin, disee y programe


una clase para escritura de datos en un fichero secuencial.

Ejercicio 8.* A fin de ocultar los detalles de implementacin, disee y programe


una clase para trabajar con registros en un fichero de acceso alea-
torio. Como ejemplo considere un registro de clientes formado por
un nmero de cliente, un nombre o identificacin para el mismo y
un saldo.

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 10.3. Bsqueda


10.2. Ordenacin 10.3.1. Bsqueda lineal
10.2.1. Ordenacin por intercambio 10.3.2. Bsqueda binaria
10.2.2. Ordenacin por seleccin 10.3.3. Comparacin de mtodos
10.2.3. Ordenacin por insercin
10.2.4. Comparacin de mtodos
258 Introduccin a la programacin con orientacin a objetos

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

Especficamente en el campo de la computacin se entiende por ordenacin la distribucin de una


serie de elementos en orden creciente o decreciente, de acuerdo a un cierto criterio. Puesto que la
ordenacin es una actividad tan comn, a lo largo del tiempo se han desarrollado muchos algorit-
mos con variada eficiencia. La clasificacin taxonmica clsica es la debida a Knuth (Knuth,
1998).
Dependiendo normalmente de la cantidad de elementos a ordenar, se puede trabajar en memoria
principal, hablndose de ordenacin interna, o en memoria secundaria, hablndose entonces de orde-
nacin externa. En este captulo vamos a considerar slo la ordenacin interna. Respecto a la ordena-
cin interna disponemos de distintas tcnicas de variada complejidad. As, las cinco categoras clsicas
de los mtodos de ordenacin son: insercin, intercambio, seleccin, fusin y distribucin (Knuth,
1998). De acuerdo a Smith (Smith, 1987), una organizacin posible de estas cinco categoras para los
algoritmos de ordenacin interna sera,

a) Mtodos basados en comparacin:

a.1. Ordenacin por intercambio.


a.2. Ordenacin por seleccin.
a.3. Ordenacin por insercin.
a.4. Ordenacin por fusin o mezcla (Merge sorting).

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

10.2.1. ORDENACIN POR INTERCAMBIO


Este algoritmo de clasificacin se conoce tradicionalmente como mtodo de la burbuja. El algoritmo
se basa en el principio de comparar pares de elementos adyacentes e intercambiarlos entre s hasta que
estn todos ordenados. Veamos un ejemplo de ordenacin en orden creciente. Consideremos la
siguiente lista de valores:

50 15 56 14 35 1

que queremos ordenar en orden ascendente. Los pasos a seguir seran:

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

2. A continuacin, se comparan los elementos 2. y 3., 50 y 56 en este caso. De nuevo se inter-


cambian si es necesario. En este caso no lo sera.
3. El proceso contina hasta que cada elemento de la lista ha sido comparado con sus elementos
adyacentes y se han realizado los intercambios necesarios. Al acabar de realizar esta opera-
cin sobre la serie tendramos un valor colocado en su posicin final. En este caso, el valor 56
quedara en ltimo lugar.
4. Ahora se repetira el proceso sobre la lista pero sin incluir el ltimo elemento pues ya est
ordenado. Dados n valores iniciales, si se efecta (n 2 1) veces la operacin sobre la lista de
valores se consigue ordenar la misma.

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,

// Ordenacin por intercambio. Orden creciente

public void burbuja(int [] lista) {


int aux, longitud;
longitud=lista.length;
for (int i=0; i<longitud-1; i++) {
for (int j=0; j< (longitud-1-i); j++) {
if (lista [j]>lista[j+1]) {
aux=lista[j];
lista[j]=lista[j+1];
lista[j+1]=aux;
}
} // Fin for j
} // Fin for i
} // Fin mtodo

Obsrvese que al pasar la lista por referencia (puesto que es un objeto de clase matriz) no es nece-
sario devolverla con un return.

10.2.2. ORDENACIN POR SELECCIN

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

En la ordenacin por seleccin actuaramos de la siguiente forma:

1. Seleccionaramos el elemento menor y lo colocaramos en la primera posicin, intercambian-


dolo con el valor existente all. En el ejemplo seleccionaramos el valor 1 y el resultado sera,

1. 1 15 56 14 35 50

1. El primer elemento ya est ordenado, ya est en su posicin final.


2. Seleccionamos como nueva lista la sublista obtenida eliminando el primer elemento.
3. Volvemos al paso 1 para ordenar la sublista.

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:

// Ordenacin por seleccin. Orden creciente

public void seleccion(int [] lista) {


int aux, indice_min, n;

n=lista.length;

for (int j=0; j<n; j++) {


indice_min=j;
for (int i=j+1; i<n; i++) {
if (lista[i]<lista[indice_min]) {
indice_min=i;
}
}
aux=lista[j];
lista[j]=lista[indice_min];
lista[indice_min]=aux;
}
}//Fin mtodo seleccin

Una vez ms, el paso por referencia de la lista hace innecesaria la devolucin de la matriz orde-
nada con un return.

10.2.3. ORDENACIN POR INSERCIN

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

2. Tomamos el siguiente elemento y lo colocamos (insertamos) en la posicin que le correspon-


da entre los anteriores.
3. Repetimos el paso 2 hasta que no queden ms elementos.

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

Cada elemento de la lista se va seleccionando consecutivamente. En cada seleccin se coloca el


elemento en la posicin que le corresponda entre los anteriores (los ya ordenados). El efecto es el de
insertar cada elemento en su posicin, moviendo hacia el final los elementos que quedan desde el
insertado hacia el ltimo.
Para una lista de n elementos a ordenar en orden creciente, el pseudocdigo del algoritmo de inser-
cin, con 0-origen para la lista, sera,

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:

// Ordenacin por Insercin. Orden creciente


public void insercion(int [] lista) {
int valor, posicion, n;

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.

10.2.4. COMPARACIN DE MTODOS

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,

n21 n(n 2n(n


1) 2 1)
6(n 2 i) 5 (n 2 1)n 2 }}2 5 }}2
i51

y el algoritmo es de orden n 2.
264 Introduccin a la programacin con orientacin a objetos

El algoritmo de seleccin y el de insercin tambin implican un mximo de n 2 i comparaciones


en la pasada i, con un total de n 2 1 pasadas. El resultado es el mismo que para el caso anterior, com-
plejidad de orden n 2.

b) Nmero de intercambios de variables


El mtodo de la burbuja realiza n(n 2 1)/2 comparaciones donde cada comparacin implica tres inter-
cambios de variables. La complejidad es entonces 3n(n 2 1)/2. Por otro lado, el algoritmo de seleccin
realiza n(n 2 1)/2 intercambios. Por contra, el algoritmo de insercin presenta la colocacin del ele-
mento a ordenar fuera de uno de los bucles, con lo que la complejidad para esta tarea es de orden n.
Con los resultados anteriores los mtodos de seleccin e insercin aparecen como ms eficientes
que el de la burbuja, tradicionalmente considerado como el ms ineficiente de los mtodos de ordena-
cin. Por otro lado, el mtodo de seleccin realiza sus O(n 2) comparaciones e intercambios siempre,
incluso si la lista ya est ordenada. Por esta razn si la lista est parcialmente ordenada se suele pre-
ferir el mtodo de insercin. Para un anlisis ms detallado de la complejidad de los algoritmos de
ordenacin vase Rawlins, 1992.
Para finalizar, indiquemos que hay varios algoritmos ms eficientes que los examinados, obedecien-
do a complejidad de orden n log2(n). El lector interesado puede consultar Knuth, 1998; Smith, 1987.

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.

10.3.1. BSQUEDA LINEAL

ste es el algoritmo ms sencillo, aunque no el ms eficiente. Es la aproximacin ms directa, que


implica que comenzamos a recorrer secuencialmente nuestra lista hasta que localizamos el valor cla-
ve deseado o hasta que alcanzamos el final de la lista (lo que indica que la clave no est en la lista).
Este algoritmo no necesita de una ordenacin previa. Los pasos a seguir son:

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

Mientras (i< n) y (lista(posicion) clave)


posicioniposicion+1
Fin_Mientras

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:

// Bsqueda secuencial o lineal

public int secuencial(int [] lista, int clave){


int n, posicion=0;
n=lista.length;

while (posicion<n && clave != lista[posicion]){


posicion++;
}
if(posicion==n) {
posicion=-1;
}
return posicion;

} //Fin mtodo secuencial

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.

10.3.2. BSQUEDA BINARIA

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

1. Seleccionar el elemento central (aproximadamente) de la lista y comparar con la clave. Si son


iguales o no hay ms elementos acabamos el proceso.
2. Si la clave y el elemento central no coinciden, determinar si la clave es mayor o menor que
dicho elemento para descartar la mitad de la lista donde no puede encontrarse la clave
(depender de si la lista est ordenada en orden creciente o decreciente) volviendo al
paso 1.

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

busquemos el nmero 1983:

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:

1. 1983 2005 2446 2685 3200

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

Mientras (izquierda <= derecha) y (clave lista(posicion))


posicion i Parte entera de(izquierda + derecha)/2
Si (clave > lista(posicion)) entonces
izquierda i posicion+1
Si_no
derecha i posicion-1
Fin_Si
Fin_Mientras

Si (lista(posicion) clave) entonces


Ordenacin y Bsqueda 267

posicion= -1
Fin_Si

Devuelve posicion
Fin

Veamos un mtodo en Java que implementa el algoritmo:

// Bsqueda binaria

public int binaria (int[] lista, int clave) {

int posicion=0, izquierda = 0, derecha = lista.length - 1;

while (izquierda <= derecha && clave != lista[posicion]) {


posicion = (izquierda + derecha)/2; // Cociente entero,

if (clave > lista[posicion]) {


izquierda=posicion+1; // clave esta en la segunda mitad
}
else {
derecha = posicion-1; // clave esta en la primera mitad
}
} // Fin del while

if (clave != lista[posicion]) {
posicion=-1;
}
return posicion;

} //Fin mtodo binaria

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.

10.3.3. COMPARACIN DE MTODOS

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 en cada particin se hace una comparacin, m es justamente el nmero de comparaciones.


Por lo tanto, la complejidad es de orden log2 n, lo que representa una gran mejora frente a la bs-
queda lineal 1. El nico requisito de la bsqueda binaria es que necesitamos una ordenacin previa
de la lista.
La mayor eficiencia de la bsqueda binaria frente a la secuencial se puede constatar, de manera
prctica, en la Tabla 10.1 que recoge el nmero de comparaciones necesarias cuando se busca un ele-
mento en una lista de n en el caso ms desfavorable (el elemento no est en la lista).

Tabla 10.1. Comparacin entre las bsquedas secuencial y binaria

Nmero de elementos Bsqueda Bsqueda


(n) secuencial binaria
10 10 3
100 100 7
1000 1000 10
1.000.000 1.000.000 20

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.

Ejercicio 4. Escriba una variante de los mtodos de ordenacin por intercambio,


seleccin e insercin, presentados en el captulo 10, donde la orde-
nacin sea de forma decreciente.

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.

CAPTULO 1. SISTEMAS BASADOS EN COMPUTADOR

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

CAPTULO 2. ELEMENTOS DE PROGRAMACIN Y LENGUAJES

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

CAPTULO 3. INTRODUCCIN A LA PROGRAMACIN

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

Con un resultado final de 4

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:

if (Math.abs (r1-r2) < p) {


11System.out.println(Se consideran iguales);
}
else {
11System.out.println(Se consideran distintos);
}

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

1111// Lectura de datos


1111System.out.println(Introduzca el numero n);
1111n=Integer.parseInt(leer.readLine());
1111System.out.println(Calculando la suma de cuadrados
111111111111111111111111+desde 1 hasta +n);

111// Suma de los cuadrados de 1 a n usando un bucle while


1111i=1; // Variable usada como contador
1111n_2=0;
1111while (i<=n) {
111111n_2=n_2+i*i;
111111i++;111// Incremento del contador
1111}

111// Salida de resultados


1111System.out.println(Suma de los cuadrados: +n_2);

11}//Fin del main


}//Fin clase

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

1111boolean sigue=true;1111111// Variable de control del bucle


1111int opcion=0;

1111BufferedReader leer =new BufferedReader


11111111111111111(new InputStreamReader(System.in));

111// Lectura de datos


1111System.out.println(Introduzca la constante de acidez:);
1111Ka=Double.parseDouble(leer.readLine());

1111/* logaritmo base 10 de x =0.43429448*logaritmo natural de x


1111111Se usa esta forma porque Java no incorpora el logaritmo
1111111base 10 */
1111pKa=-0.43429448* Math.log(Ka);

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

1111BufferedReader leer =new BufferedReader


1111111111111111(new InputStreamReader(System.in));

// Lectura inicial de datos


1111System.out.println(Introduzca la masa del pendulo (kg):);
1111m=Double.parseDouble(leer.readLine());

1111if (m < 1.0 ) {


111111System.out.println(Introduzca la longitud del pendulo (m):);
111111l=Double.parseDouble(leer.readLine());

111111frecuencia = Math.sqrt(g/l);
111111frecuencia = frecuencia /(2.0*Math.PI);

111111System.out.println(Frecuencia (1/s): +frecuencia);


1111}
1111else {
111111System.out.println(La masa debe ser menor de 1 kg);
1111}

11}111// Fin mtodo main


} // Fin clase Pndulo

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.

CAPTULO 4. PROGRAMACIN ESTRUCTURADA

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.

En este proceso informal de anlisis no vamos ms allde la recoleccin de requisitos. En la apro-


ximacin estructurada al anlisis, los requisitos se transforman en un diagrama que recoge las tareas y
el flujo de datos entre ellas. Esta labor supone la creacin de un modelo lgico del sistema.

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

Distinguir entero negativo


Inicio
11Haz
1111repite ifalso
1111leer n
1111Si (n<0) entonces
111111repite iverdadero
111111Imprimir que n no puede ser negativo
1111Fin_Si
11Mientras (repite)
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

11Si (n!=0) entonces


1111Calcular factorial
11Fin_Si
Fin

Integrndolo con el clculo de N! obtendramos,

Inicio
11factoriali1
11Si (n!=0) entonces
1111Para i i1 mientras i n incremento iiI+1
111111factorial ifactorial*i
1111Fin_Para
11Fin_Si
Fin

Preguntar si continuar o no (hasta introducir una opcin vlida)


Inicio
11otra_opcion iverdadero
11Haz
1111Preguntar si desea continuar
1111Si (continuar si y continuar no) entonces
111111Decir que no es una opcin vlida
1111Si_no
111111otra_opcion ifalsa
1111Fin_Si
11Mientras (otra_opcion)
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

Con esta serie de algoritmos tenemos resuelto el problema.

IMPLEMENTACIN

import java.io.*;
class Factorion {
Soluciones a los ejercicios propuestos 281

11public static void main(String[] args) throws IOException {


1111int n, opcion;
1111boolean repite, pide_numero, otra_opcion;
1111double factorial;
1111BufferedReader leer =new BufferedReader
111111111111111111111111(new InputStreamReader(System.in));

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

111111if (n!=0) { // Si n=0 se queda con valor de factorial=1


11111111for (int i=1; i<=n; i++){
1111111111factorial=factorial*i;
11111111}
111111}

111111System.out.println(El factorial de +n+ es: +factorial);

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

11Si (km > 200) entonces


111billete_billete +0.03*(km-200)
1111Si km >400 entonces
111111billetei0.85*billete
1111Fin_Si
11Fin_Si

11Si (personas > 2) entonces


1111billetei0.90*billete
11Fin_Si

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

1111billete = 20.0;1111// Precio base

1111if (km >200 ) {


111111billete = billete + (km-200)*0.03; // Incluyendo precio
11111111111111111111111111111111111111111// por km
Soluciones a los ejercicios propuestos 285

111111if (km > 400) {


11111111billete = billete*0.85;11// Descuento del 15 %
111111}11// Fin if interno (anidado al primero)
1111}11// Fin if externo

1111if (personas>2) {
111111billete=billete*0.90; // Descuento del 10%
1111} // Fin if (concatenado al anterior)

1111System.out.println(Precio del billete: +billete+ euros);

11}11// Fin main


} // Fin clase

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

1111System.out.println(Introduzca el numero de valores:);


1111n=Integer.parseInt(leer.readLine());

1111media=0.0;
1111for (int i=0; i<n; i++) {
286 Introduccin a la programacin con orientacin a objetos

111111System.out.println(Introduzca valor +(i+1)+ :);


111111valor=Double.parseDouble(leer.readLine());
111111media=media+valor;
1111}
1111media=media/n;

1111System.out.println(Media: +media);
11}
}

CAPTULO 5. ABSTRACCIN PROCEDIMENTAL Y DE DATOS

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

1111for (int i=0; i<n;i++) {


111111tirada = dado();
111111System.out.println(Tirada +i+ : +tirada);
1111}
11} // Fin mtodo main
11public static int dado() {
Soluciones a los ejercicios propuestos 287

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:

Determinacin del mximo de n valores:

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

11// Asignacin de los datos ledos y eco de la entrada

1111for (int i=0; i<=2; i++){//De manera general se puede


11111111111111111111111111111//sustituir 2 por valores.length

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

11public static double max(double [] valores) {


1111double max;
1111max=valores[0];
1111for (int i=1; i<=2; i++){111//De manera general se puede
11111111111111111111111111111111// sustituir 2 por valores.length
111111if (max < valores [i]){
11111111max = valores [i];
111111}
1111}
1111return max;
11} // Fin mtodo max

11public static double min(double [] valores) {


1111double minimo;
1111minimo=valores[0];
1111for (int i=1; i<=2; i++){
111111if (minimo > valores [i]){
11111111minimo = valores [i];
111111}
1111}
1111return minimo;
11} // Fin mtodo min

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

se hardirectamente por la lnea de rdenes.

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}

11// Salida de resultados


1111System.out.println(El maximo es : +maximo);

11} // Fin mtodo main


11public static double max(double a, double b) {
1111double maximo=a;
1111if (b > maximo){
111111maximo=b;
1111}
1111return maximo;
11} // Fin mtodo max versin de dos parmetros

11public static double max(double a, double b, double c) {


1111double maximo=a;
1111if (b > maximo){
111111maximo=b;
1111}
1111if (c > maximo){
111111maximo=c;
1111}
1111return maximo;
11}11// Fin mtodo max versin con tres parmetros

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

La expresin para determinar este nmero de subconjuntos es la dada en el enunciado: c (n,k) =


=n! /(k! (n-k)! ). En el tringulo de Pascal las filas daran el nmero total de elementos (n) y las colum-
nas el nmero de elementos del subconjunto (k). En ambos casos empezaramos a contar en cero.
Estos coeficientes c (n, k) se denominan binmicos porque son los que aparecen en la expresin
de las potencias del binomio:

(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

Figura A.5.3. Diagrama de estructura para el ejemplo del tringulo de Pascal

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)

11public static int fact(int n) {


1111int producto=1;
1111for (int i=1; i<=n;i++){
111111producto=producto*i;
1111}
1111return producto;
11}11// Fin mtodo fact
} // Fin clase

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

Figura A.5.4. Asignacin de las referencias a y 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}

Figura A.5.5. Relacin entre las matrices a, b y c

Con esto, el resultado final de la impresin sera,

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

es que la impresin de Valores2 produce la siguiente salida,

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

1111for (int i=0; i<a.length; i++) {


111111for (int j=0; j<a[0].length; j++){
11111111System.out.print(a[i][j]+ );
111111}
111111System.out.println();
1111}

1111transponer(a);

1111System.out.println();

1111for (int i=0; i<a.length; i++) {


111111for (int j=0; j<a[0].length; j++){
11111111System.out.print(a[i][j]+ );
111111}
111111System.out.println();
Soluciones a los ejercicios propuestos 295

1111}
11}

11public static void transponer(int [][] a) {


1111int aux;
1111for (int i=0; i<a.length; i++)
111111for (int j=0; j<i; j++) {
11111111aux =a[i][j];
11111111a[i][j]=a[j][i];
11111111a[j][i]=aux;
111111}
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

a) Mtodo del trapecio

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.

El rea total serla suma de las dos reas:

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.

La Figura A.5.8 ilustra la situacin.

a b x

Figura A.5.8. Divisin del intervalo a-b en trapecios

La integral sera la suma de todas las reas de los trapecios:

f(a) 1 f (a 1 h)f(a 1 h) 1 f(a 1 2 h)


3
Integral 5 h } }1
2
}}}
2
1 ...

f(a 1 (n 2 2) h) 1 f(a 1 (n 2 1) h) f(a 1 (n 2 1) h) 1 f(b)


1 }}}}
2
1 }}}
2 4
con lo cual al final tenemos,
Soluciones a los ejercicios propuestos 297

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 (f(a) + f(b) )/2


11Para ii1 mientras i < n incremento iii+1
1111integral i integral + f(a+i*h)
11Fin_Para

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

11public static double trapecio(int n, double a, double b) {


1111double integral, h;

1111h=(b-a)/n; // Incremento a usar

1111// Ahora se aplica la regla del trapecio


1111integral= (funcion(a)+funcion(b))/2;
1111for (int i=1; i<n; i++){
111111integral=integral+ funcion(a+i*h);
1111}
1111integral=integral*h;

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

Figura A.5.9. Diagrama para la explicacin del mtodo de Monte Carlo

Na ~ E a
b

f(x) dx

Anlogamente, el nmero total de puntos N t es proporcional al rea total,

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,

// Mtodo que calcula una integral por medio del


// mtodo de Montecarlo
11public static double montecarlo(int Nt, double a, double b,
1111111111111111111111111111double f0, double f1) {
1111int Na;
1111double integral, h_x, h_ y, x, y, z, fz;
1111// Inicializando variables
1111Na=0;
1111h_x=b-a;111111111111// Intervalo entre a y b
1111h_ y=f1-f0;1111111111// Intervalo entre f0 y f1
1111for (int i=1; i<=Nt; i++) {
111111x=a+h_x*Math.random();11// Seleccin al azar de un punto
111111y=f0+h_ y*Math.random(); //sobre los segmentos a-b y f0-f1
111111z=funcion(x);
111111if (y<=z) {
11111111Na=Na+1;
111111} // Fin if
1111}// Fin for
1111integral = Na*h_x*h_ y/Nt;
1111return integral;
11}11//Fin mtodo Monte Carlo

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

Figura A.5.10. Diagrama de estructura para el ejercicio de integracin numrica


El cdigo correspondiente sera,
/*
111Clculo numrico de la integral de seno de un ngulo
111entre 0 y 180 grados (0 y Pi radianes)
*/

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

// Mtodo que calcula una integral por medio de la regla


// del trapecio

11public static double trapecio(int n, double a, double b) {


1111double integral, h;

1111h=(b-a)/n;1111111111// Incremento a usar

1111// Ahora se aplica la regla del trapecio


1111integral = (funcion(a)+funcion(b))/2;
1111for (int i=1; i<n; i++){
111111integral=integral+ funcion(a+i*h);
Soluciones a los ejercicios propuestos 301

1111}
1111integral=integral*h;

1111return integral;
11}111//Fin mtodo trapecio

// Mtodo que calcula una integral por medio del


// mtodo de Montecarlo

11public static double montecarlo(int Nt, double a, double b,


1111111111111111111111111111double f0, double f1) {
1111int Na;
1111double integral, h_x, h_ y, x, y, z, fz;

1111// Inicializando variables

1111Na=0;
1111h_x=b-a;111111111111// Intervalo entre a y b
1111h_y=f1-f0;1111111111// Intervalo entre f0 y f1

1111for (int i=1; i<=Nt; i++) {


111111x= a+h_x*Math.random(); // Seleccin al azar de un punto
111111y=f0+h_ y*Math.random(); //sobre los segmentos a-b y f0-f1
111111z=funcion(x);
111111if (y<=z) {
11111111Na=Na+1;
111111} // Fin if
1111}// Fin for

1111integral = Na*h_x*h_ y/Nt;

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

11Si (m < n) entonces


1111aux i m
1111m i n
1111n i aux
11Fin_Si

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;

1111BufferedReader leer =new BufferedReader


11111111111111111(new InputStreamReader(System.in));

1111System.out.println(Introduzca primer numero:);


1111m=Integer.parseInt(leer.readLine());

1111System.out.println(Introduzca segundo numero:);


1111n=Integer.parseInt(leer.readLine());
1111System.out.println();
1111System.out.println(euclides(n, m));
11}

// Algoritmo de Euclides para obtener el mximo comn


// divisor de dos nmeros enteros

11public static int euclides(int n, int m) {


1111int resto=0;
1111boolean sigue=true;

1111if (m<n) {1111// Ordenando los valores


111111int aux=m;
111111m=n;
111111n=aux;
1111}

1111while (sigue) {
Soluciones a los ejercicios propuestos 303

111111resto= m%n;11111// Determinando el resto


111111if (resto!=0) {
11111111m=n;
11111111n=resto;
111111}
111111else {
11111111resto=n;
1111111sigue=false;
111111}
1111}111// Fin while

1111return resto;

11}11// Fin mtodo euclides

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

La salida, por tanto, sera:

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

De acuerdo a su definicin, la funcin de Ackermann tiene un caso base que corresponde a m 5 0.


Teniendo en cuenta que la funcin estdefinida para enteros positivos, automticamente sabemos que
la otra opcin es la de m . 0, no haciendo falta contrastarla en una condicin (if). Por la misma razn,
respecto a n basta con contrastar si n 5 0 puesto que si no, la nica opcin es que sea n . 0. Con estas
consideraciones, el pseudocdigo para el algoritmo recursivo sera,

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:

a) Pasar n 2 1 discos de 1 a 3 con 2 como varilla auxiliar


b) Poner el disco que queda en 1 (el ms grande) en la varilla 2
c) Repetir moviendo los n 2 1 discos de la varilla 3 a la varilla 2 usando la varilla 1 como auxiliar

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.

La formulacin recursiva sera la siguiente,

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

public static void hanoi(int n,int i,int j) {


if (n>0) {
hanoi(n-1, i, 6-i-j); // Moviendo los n-1 discos superiores
// a la varilla auxiliar

System.out.println(i+->+j); // Moviendo el ultimo disco


// a la varilla destino

hanoi(n-1,6-i-j,j); // Moviendo los n-1 discos a la varilla


// destino
}
} // Fin metodo recursivo hanoi

} // 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.

Con esta informacin el pseudocdigo para el correspondiente algoritmo sera,

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

public static int calcular_ potencia(int x, int n){


int valor=1;
if (n>0) {
valor = x * calcular_ potencia(x, n-1);
}
return valor;
} // Fin del mtodo
}//Fin clase

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:

int suma(int numero){


if (numero ==0)
return 0;
else
return numero+suma(numero-1);

} //Fin del mtodo suma


Soluciones a los ejercicios propuestos 309

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,

int suma(int numero){


int valor;
if (numero ==0) {
valor = 0;
}
else {
valor = numero+suma(numero-1);
}
return valor;
} //Fin del mtodo suma

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

} //Fin del main


public static void imprime(int a[], int n){
if (n > 0) {
imprime(a, n-1);
310 Introduccin a la programacin con orientacin a objetos

System.out.print(a[n-1]);
}
} //Fin del mtodo imprime
}

La salida de este programa sera:

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

El pseudocdigo del algoritmo sera,

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;

BufferedReader leer =new BufferedReader


(new InputStreamReader(System.in));

System.out.println(Introduzca primer numero:);


m=Integer.parseInt(leer.readLine());

System.out.println(Introduzca segundo numero:);


n=Integer.parseInt(leer.readLine());
System.out.println();

if (m<n) { // Ordenando los valores


int aux=m;
m=n;
n=aux;
}
System.out.println(euclides(n, m));

}
312 Introduccin a la programacin con orientacin a objetos

/* Algoritmo de Euclides recursivo para obtener el mximo comn


divisor de dos nmeros enteros */
public static int euclides(int n, int m) {
int resto, valor;
resto = m%n;

if (resto==0) {
valor=n;
}
else {
valor = euclides(resto, n);
}

return valor;

} // Fin mtodo euclides


} // Fin clase

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.

CAPTULO 7. CLASES Y OBJETOS

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

Figura A.7.1. Diagrama para la clase Producto


Soluciones a los ejercicios propuestos 313

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

Figura A.7.2. Diagrama de clases del sistema descrito


La parte ms clara, con los requisitos indicados, es la de herencia entre los distintos tipos de
empleados. En el diagrama se ha considerado que la empresa estorganizada como un conjunto
de equipos de desarrollo (podra ser que se tuviera que considerar como un conjunto de departa-
mentos) y que cada equipo de desarrollo estformado por un conjunto de programadores.

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

Curso verano Curso completo

Estudiante Estudiante
curso de Profesor Profesor
completo verano titular asociado

Figura A.7.3. Diagrama de clases para el modelo de Universidad

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

Punto (double,double) Linea (Punto,Punto)


x ( ):double Linea (Punto,double)
y ( ):double a ( ):double
igual (Punto):boolean b ( ):double
y (double):double

Figura A.7.4. Relacin entre las clases Punto y Lnea

y1 5 a 1 bx1 , y2 5 a 1 bx2

Restando las dos ecuaciones:


(y 2y )
b 51}} 2
( x12 x2)
y

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;

public Punto(double x_i, double y_i) {


x=x_i;
y=y_i;
}

public double x() {


return x;
}
public double y( ) {
return y;
}

public boolean igual(Punto p) {


return (x==p.x() && y==p.y());
}
} // Fin clase punto
316 Introduccin a la programacin con orientacin a objetos

class Linea {
private double a;
private double b;

public Linea(Punto p1, Punto p2) {


b= (p1.y()-p2.y())/(p1.x()-p2.x());
a= p1.y() - b*p1.x();
}

public Linea(Punto p, double b_i) {


b= b_i;
a= p.y() - b*p.x();
}

public double a() {


return a;
}
public double b() {
return b;
}

public double y(double x_i) {


return a+b*x_i;
}
} // Fin clase Linea

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,

Ordenada en el origen: -1.0


Pendiente: 2.0
y para x=5 : 9.0
Soluciones a los ejercicios propuestos 317

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

Figura A.7.5. Relacin entre las clases Nombre y Persona

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

public void imprime_nombre() {


System.out.print(nombre+ );
System.out.print(apellido_1 + );
System.out.println(apellido_2);
318 Introduccin a la programacin con orientacin a objetos

}
} // Fin clase Nombre

class Persona {
private Nombre nombre_ persona;
private int dni;

public Persona(Nombre nombre, int dni) {


nombre_ persona=nombre;
this.dni=dni;
}

public void imprime_datos( ) {


nombre_ persona.imprime_nombre();
System.out.println(DNI: +dni);
System.out.println();
}

public Nombre nombre() {


return nombre_ persona;
}

public int dni() {


return dni;
}

} // Fin clase Persona

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

/*Declaramos una matriz de 3 elementos de clase persona.


Es una matriz de objetos */
Persona [] individuos = new Persona [3];
Nombre aux;

aux=new Nombre(Juan, Guijarro, Sobrino);


individuos[0]= new Persona(aux, 67453219);

aux=new Nombre(Marta, Salvador, Rodriguez);


individuos[1]= new Persona(aux, 10567953);

individuos[2]= new Persona


(new Nombre(Ana, Guijarro, Salvador),
28765301);

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

} // Fin del mtodo main


} // Fin de la clase Ejemplo

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

Juan Guijarro Sobrino


DNI: 67453219

Marta Salvador Rodriguez


DNI: 10567953

Ana Guijarro Salvador


DNI: 28765301

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;

public Persona(Nombre nombre, int dni) {


nombre_ persona=nombre;
this.dni=dni;
}

public void imprime_datos( ) {


nombre_ persona.imprime_nombre();
System.out.println(DNI: +dni);
System.out.println();
}

public Nombre nombre() {


return nombre_ persona;
}

public int dni() {


return dni;
320 Introduccin a la programacin con orientacin a objetos

public void madre(Persona madre){


this.madre=madre;
}
public void padre(Persona padre){
this.padre=padre;
}
public Persona da_madre() {
return madre;
}
public Persona da_ padre() {
return padre;
}
} // Fin clase Persona

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;

aux=new Nombre(Juan, Guijarro, Sobrino);


individuos[0]= new Persona(aux, 67453219);

aux=new Nombre(Marta, Salvador, Rodriguez);


individuos[1]= new Persona(aux, 10567953);

individuos[2]= new Persona


(new Nombre(Ana, Guijarro, Salvador),
28765301);

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

}// Fin del for

} // Fin del mtodo main


} // Fin de la clase Ejercicio

Resultado:
Personas en la lista

Juan Guijarro Sobrino


DNI: 67453219

Marta Salvador Rodriguez


DNI: 10567953

Ana Guijarro Salvador


DNI: 28765301

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

Figura A.7.6. Diagrama UML de la clase Polinomio

IMPLEMENTACIN
El cdigo para la clase Polinomio podra ser el siguiente,

class Polinomio{
private int n;
private double [] coeficientes;

public Polinomio(double [] coeficientes) {


this.coeficientes = coeficientes;
}

public double y(double x){


double valor, y=0.0;
int n=coeficientes.length;

for (int i=0; i < n; i++){


y=y+coeficientes[i]*Math.pow(x,i);
}
return y;
}
}//Fin clase Polinomio

El programa principal podra ser,

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;

BufferedReader leer =new BufferedReader


(new InputStreamReader(System.in));

System.out.println(Introduzca numero de terminos:);


n=Integer.parseInt(leer.readLine());
coeficientes = new double[n];

for (int i=0; i<n; i++){


System.out.println(Coeficiente del termino +i+ :);
Soluciones a los ejercicios propuestos 323

coeficientes[i]=Double.parseDouble(leer.readLine());
}

poli=new Polinomio(coeficientes); // Creando objeto

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

Figura A.8.2. Diagramas de clase para el Ejercicio 5 del Captulo 8


324 Introduccin a la programacin con orientacin a objetos

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,

Estoy en un objeto de clase Dos con i:3


Estoy en un objeto de clase Tres con i:2

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

Figura A.8.1. Diagrama de clases para la jerarqua de empleados

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

public Empleado(String nombre, String apellido1, String apellido2){


this.nombre=nombre;
this.apellido1=apellido1;
this.apellido2=apellido2;
}

//Devuelve el nombre
public String nombre(){
return nombre;
}

//Devuelve el apellido1

public String apellido1(){


return apellido1;
}
//Devuelve el apellido2

public String apellido2(){


return apellido2;
}

//Mtodo abstracto

abstract double calcularSalario();

}//Fin clase Empleado


326 Introduccin a la programacin con orientacin a objetos

//Clase hija Encargado

class Encargado extends Empleado{


final double SALARIO=2000.0;

//Constructor

public Encargado(String nombre, String apellido1,


String apellido2){
super(nombre, apellido1, apellido2); //Utiliza el constructor del
// padre
}

//Mtodo calcularSalario

public double calcularSalario(){


return SALARIO;
}

public String toString(){


return Encargado: +nombre()+ +apellido1()+ +apellido2();
}
}//Fin clase Encargado

// Clase hija TrabajadorComision

class TrabajadorComision extends Empleado{


private final double SALARIO=1000.0;
private final double COMISION=0.1;
private double vendido;

//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;
}
public String toString(){
return Trabajador a comision: +nombre()+ +apellido1()
+ +apellido2();
}

}//Fin clase TrabajadorComision

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

trabajador = encargado1; // Referencia de superclase a objeto de


// clase Encargado
System.out.print(trabajador.toString());
System.out.println( gana +trabajador.calcularSalario());

trabajador= trabajadorComision1; // Referencia de superclase a


//objeto de clase trabajador

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

Con esta interfaz la clase TrabajadorComisin podra implementarse de la forma siguiente,

class TrabajadorComision extends Empleado implements Valores{


private double vendido;

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

public String toString(){


328 Introduccin a la programacin con orientacin a objetos

return trabajador a comision:+nombre()+ +apellido1()+


+apellido2();
}
}//Fin clase TrabajadorComision

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

abstract public void dibujar();

}//Fin clase ObjetoGrafico

El mtodo moverObjeto se puede implementar aqu ya que el desplazamiento se hace siempre


igual, independientemente de la figura. El segundo mtodo se ha definido como abstracto porque cam-
bia segn el objeto a dibujar. El cdigo para las clases que representan a las dos figuras indicadas en
los requisitos puede ser:

class Rectangulo extends ObjetoGrafico{


Rectangulo(){
super(); //utiliza el constructor de la clase padre
}

public void dibujar(){


//implementacin del mtodo
}
}//Fin clase Rectangulo

class Circunferencia extends ObjetoGrafico{


Circunferencia(){
super();
}
public void dibujar(){
//implementacin del mtodo
}
}//Fin clase Circunferencia

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 Objetos implements Segunda {


public void A(){
System.out.println(Mtodo A);
}
public void B(){
System.out.println(Mtodo B);
}
public void C(){
System.out.println(Mtodo C);
}
}//Fin clase Objetos

Un ejemplo de uso de la clase Objetos se recoge en el siguiente programa principal,

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

for (int i = 0; i < clientes; i++) {


System.out.println(); //Presentacin
System.out.println(Cliente +(i+1));
System.out.print(Cuenta:);
cuenta=Integer.parseInt(entrada.readLine()); //Lee la cuenta
salida.writeInt(cuenta);
System.out.print(nombre:);
nombre=entrada.readLine();
salida.writeUTF(nombre);
System.out.print(saldo:);
saldo=Double.parseDouble(entrada.readLine());
salida.writeDouble(saldo);
}
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(Introduzca el nombre del
+fichero como argumento);
}
catch(IOException e){
System.out.println(No se abrio bien el fichero\n+
e.toString());
}

salida.close();

} // Fin mtodo main


} // Fin clase

Ejercicio 4
Teniendo en cuenta que debemos capturar primero las excepciones ms especficas, el orden tendra
Soluciones a los ejercicios propuestos 331

que ser NumberFormatException, Exception.

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

/* Este bloque de cdigo no corresponde al enunciado del problema.


Simplemente se utiliza para generar un fichero de datos del que
poder leer.*/

//Empieza el bloque de generacin del fichero de datos


try {
DataOutputStream salida =new DataOutputStream
(new FileOutputStream(datos));
for (int j=0; j<10;j++){
salida.writeInt(j);
}
salida.close();
}
catch(IOException e) {
System.out.println(Error en la escritura);
}
//Acaba el bloque de generacin del fichero de datos

//Empieza el cdigo del mtodo main del ejercicio propiamente


//dicho

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

// Excepcin EOF que se usa para leer hasta fin de fichero


catch(EOFException e) {
System.out.println(\nFin del fichero\n);
}
332 Introduccin a la programacin con orientacin a objetos

finally {
if (entrada!=null){
entrada.close();
}
}
} //Fin try externo

catch(IOException io) {
System.out.println(Error en la E/S\n\n+io.toString());
}

} // Fin mtodo main


} // Fin clase Ejercicio

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

Figura A.9.1. Diagrama UML para la clase Secuencial

IMPLEMENTACIN
La clase quedara como sigue,
class Secuencial{
protected FileOutputStream salida=null;

public Secuencial(String nombre){


try{
salida = new FileOutputStream(nombre);
}
catch(IOException e){
System.out.print(Error al abrir el fichero
+e.toString());
}
}

public void escribe(int entero){


try{
Soluciones a los ejercicios propuestos 333

salida.write(entero);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}

public void cerrarFichero(){


try{
salida.close();
}
catch(IOException e){
System.out.print(Error al cerrar el fichero
+e.toString());
}
}
}// Fin clase Secuencial

Un ejemplo de su uso se muestra en el siguiente programa,

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

Figura A.9.2. Diagrama UML para la clase SecuencialDatos

IMPLEMENTACIN
El cdigo correspondiente es el siguiente,

class SecuencialDatos{
protected DataOutputStream salida=null;

public SecuencialDatos(String nombre){


try{
salida = new DataOutputStream
(new FileOutputStream(nombre));
}
catch(IOException e){
System.out.print(Error al abrir el fichero
+e.toString());
}
}

/*El mtodo escribe se sobrecarga para leer valores de tipo int,


double, float, String y char */

public void escribe(int entero){

try{
salida.writeInt(entero);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}

public void escribe(double doble){

try{
salida.writeDouble(doble);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}

public void escribe(float numero){

try{
salida.writeFloat(numero);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
Soluciones a los ejercicios propuestos 335

}
}

public void escribe(String cadena){

try{
salida.writeUTF(cadena);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}

public void escribe(char caracter){

try{
salida.writeChar(caracter);
}
catch(IOException e){
System.out.print(Error al escribir en el fichero
+e.toString());
}
}

public void cerrarFichero(){


try{
salida.close();
}
catch(IOException e){
System.out.print(Error alcerrar el fichero
+e.toString());
}
}
} // Fin de la clase SecuencialDatos

El uso de la clase se ilustra en el siguiente programa,

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

+ Registro (cuenta:int, nombre:String, saldo:double)


+ cuenta ( ):int
+ nombre ( ):String
+ saldo ( ):double
+ l_registro ( ):int
+ lee_registro (fichero:RandomAccessFile):Registro
+ pon_registro (fichero:RandomAccessFile,
regis:Registro):Registro

Figura A.9.4. Diagrama para la clase Registro

Figura A.9.4. Diagrama para la clase Registro


Soluciones a los ejercicios propuestos 337

Fichero_directo
# fichero:RandomAccessFile
# registro:Registro
# l_registro:int

+ Fichero_directo (nombre:String, modo:String)


+ pon_registro (regis:Registro):void
+ salta_registro (nregistro:int):void
+lee_registro ( ):Registro
+ cerrar_ fichero ( ):void

Figura A.9.5. Diagrama para la clase Fichero_directo

IMPLEMENTACIN
El cdigo correspondiente a las dos clases anteriores es el siguiente,

import java.io.*;

/* Clase que representa un registro con tres campos*/

class Registro {
protected int cuenta;
protected String nombre;
protected double saldo;
protected int l_registro=44;

public Registro(int cuenta, String nombre, double saldo) {


this.cuenta=cuenta;
this.nombre=nombre;
this.saldo=saldo;
} // Fin mtodo constructor

public int cuenta(){


return cuenta;
} // Fin mtodo cuenta

public String nombre(){


return nombre;
} // Fin mtodo nombre

public double saldo(){


return saldo;
} // Fin mtodo saldo

public int l_registro(){


return l_registro;
} // Fin mtodo l_registro

public Registro lee_registro(RandomAccessFile fichero) {


try {
cuenta=fichero.readInt();
338 Introduccin a la programacin con orientacin a objetos

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)

public void pon_registro(RandomAccessFile fichero,


Registro regis) {
try {
String cadena;
fichero.writeInt(regis.cuenta());
fichero.writeUTF(regis.nombre());
fichero.writeDouble(regis.saldo());
}
catch(IOException e) {
System.out.println(Error escribiendo registro);
e.toString();
}
} // Fin mtodo pon_registro

} // Fin clase Registro

/* Clase que representa un fichero de acceso directo */

class Fichero_directo {
protected RandomAccessFile fichero;
protected int l_registro=0;
protected Registro registro=new Registro(0, , 0.0);

public Fichero_directo(String nombre, String modo){


try{
fichero=new RandomAccessFile(nombre, modo);
l_registro=registro.l_registro();
}
catch(IOException e) {
System.out.println(Error de entrada/salida);
e.toString();
}
} // Fin mtodo constructor

public void pon_registro(Registro regis) {


registro.pon_registro(fichero, regis);
} // Fin mtodo pon_registro

public void salta_registro(int nregistro) {


Soluciones a los ejercicios propuestos 339

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();
}

} // Fin mtodo salta_registro

public Registro lee_registro() {


return registro.lee_registro(fichero);
} // Fin mtodo lee_registro

public void cerrar_fichero() {


try{
fichero.close();
}
catch(IOException e) {
System.out.println(Error de entrada salida);
e.toString();
}

} // Fin mtodo cerrar_fichero

} // Fin clase Fichero_directo

Como ejemplo de uso de la estructura de clases anterior tenemos el siguiente programa,

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

Fichero_directo fichero= new Fichero_directo(args[0], rw);


340 Introduccin a la programacin con orientacin a objetos

BufferedReader leer= new BufferedReader(new InputStreamReader


(System.in));

while(seguir) {
cont++;
System.out.println(Introduzca nombre del cliente);
nombre_in=leer.readLine();

// Creando una cadena con 30 caracteres


fin=30;
longitud=nombre_in.length();
if (longitud < 30){
fin=nombre_in.length();
}
for (int i=0; i<fin; i++) {
aux[i]=nombre_in.charAt(i);
}
if (longitud < 30) {
for (int i=fin; i<30;i++)
aux[i]= ;
}

String nombre= new String(aux, 0, 30);

System.out.println(Introduzca saldo del cliente);


saldo=Double.parseDouble(leer.readLine());
registro=new Registro(cont, nombre, saldo);
System.out.println(registro.cuenta()+ +registro.nombre()
+ +registro.saldo());
fichero.pon_registro(registro);

System.out.println(Desea introducir otro cliente(s/n)?);


si_no=leer.readLine().charAt(0);

while (si_no != s && si_no != n) {


System.out.println(Debe introducir s (si) o n (no));
si_no=leer.readLine().charAt(0);
}
if (si_no == n){
seguir=false;
}
}

// Bsqueda de un registro

System.out.println(Que cuenta desea consultar?);


n=Integer.parseInt(leer.readLine());
fichero.salta_registro(n);
registro=fichero.lee_registro();
System.out.println(registro.cuenta()+ +registro.nombre()
+ +registro.saldo());

fichero.cerrar_fichero();
Soluciones a los ejercicios propuestos 341

}//Fin de main
}//Fin clase FicheroAleatorio

CAPTULO 10. ORDENACIN Y BSQUEDA

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

aux i lista (inicio)


lista (inicio) i lista (indice_min)
lista (indice_min) i aux
Llamar al propio mtodo con lista, inicio+1, fin
Fin_Si
Fin

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;

} //Fin mtodo bsqueda secuencial recursiva

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.

Con la informacin anterior, el pseudocdigo del algoritmo recursivo sera la siguiente,

Inicio
Leer lista, clave, derecha, izquierda
posicion i Parte entera de (derecha+izquierda)/2

Si izquierda > derecha


valor i -1
Si_no
Si clave = lista (posicion)
valor i posicion
Si_no
Si clave > lista(posicion)
valoribsqueda binaria con lista, clave, derecha y posicion+1
Si_no
valoribsqueda binaria con lista, clave, posicion+1 e
izquierda
Fin_Si
Fin_Si
Fin_Si
Devuelve valor
Fin

IMPLEMENTACIN
Un mtodo en Java que implementa este algoritmo se presenta a continuacin,

public static int binaria_rec(int[] lista, int clave,


int derecha, int izquierda) {

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

valor= binaria_rec(lista, clave, posicion-1, izquierda);


}
}
}

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

Prcticas propuestas Casos de estudio


Parte I. Programacin estructurada y modular Parte I. Programacin estructurada y modular
Parte II. Programacin Orientada a Objetos Parte II. Programacin Orientada a Objetos
344 Introduccin a la programacin con orientacin a objetos

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

PARTE I. PROGRAMACIN ESTRUCTURADA Y MODULAR


PRCTICA 1. Implemente un programa que ilustre el funcionamiento de los operadores de divi-
sin. Para ello el programa debe realizar el cociente de dos nmeros enteros, de un entero con un
nmero real y por ltimo calcular el resto de un cociente.
PRCTICA 2. Construya un programa que devuelva los distintos nmeros primos existentes des-
de el 1 hasta un nmero entero introducido como parmetro por la lnea de rdenes. Sugerencia: Use
un bucle para recorrer todos los nmeros considerados y otro anidado con el anterior, que para un
nmero dado, vaya comprobando si el resto del cociente con los anteriores (en realidad basta con mirar
hasta la mitad de los anteriores) es o no cero.
PRCTICA 3: Desarrolle un programa que calcule las soluciones reales de una ecuacin de segun-
do grado introduciendo los coeficientes reales o enteros por la lnea de rdenes. Sugerencia: Use un
if para distinguir el caso de discriminante negativo.
PRCTICA 4. Desarrolle un programa que determine el precio de un billete de ida y vuelta en tren
o en autobs. Se sabe que: a) el precio es directamente proporcional a la distancia a recorrer, b) si el
nmero de das de la estancia es superior a 7 y la distancia superior a 800 kilmetros el billete tiene
una reduccin del 30% si se viaja en tren y del 40% si se viaja en autobs, c) el precio del kilmetro
es de 0,06 euros.
PRCTICA 5. Utilizando las propiedades y caractersticas de la programacin modular, disee un
programa que simule el funcionamiento de una sencilla calculadora con un conjunto bsico de opera-
ciones. El programa consistir bsicamente en la presentacin de un men al usuario en el que se le
indicarn las operaciones de las que dispone. stas sern: suma, resta, multiplicacin, divisin y poten-
cia de 2 operandos, que podrn ser nmeros enteros o reales. Adems, existir una opcin dentro del
men para finalizar el programa. Si la opcin que introduce el usuario no es la de finalizacin, el pro-
grama le solicitar los datos necesarios para realizar la operacin elegida. El programa debe controlar
la introduccin errnea de datos, mostrando un mensaje de error cuando esto se produzca y ofrecien-
do al usuario la posibilidad de que vuelva a introducir los datos correctamente.
PRCTICA 6. Construya un programa que obtenga el trmino n de la serie de Fibonacci. 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:

Fibonacci(n) 5 Fibonacci(n21)+Fibonacci(n22) para todo n . 1


Fibonacci 5 n para n # 1
Prcticas y casos de estudio propuestos 345

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

Fibonacci(n) = Fibonacci(n21) 1 Fibonacci(n 2 2) para todo n . 1


Fibonacci 5 n para n # 1

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.

PARTE II. PROGRAMACIN ORIENTADA A OBJETOS

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

PARTE I. PROGRAMACIN ESTRUCTURADA Y MODULAR

Caso 1. Sistema informtico para la gestin de reservas de billetes


de avin
Ante la inminente creacin del aeropuerto de Ciudad Real, se desea desarrollar el sistema informtico
de gestin de reservas de billetes para los vuelos de los aviones de la nueva compaa AeroLneas
Manchegas. Los datos que hay que tener en cuenta para establecer la reserva de un asiento son los
siguientes:

a) En cada avin de la compaa habr N asientos de 1. clase y M de 2. clase, siendo N , M.


b) En 1. clase, habr 4 asientos por fila (2 de ventanilla y 2 de pasillo)
c) En 2. clase, habr 6 asientos por fila (2 de ventanilla, 2 centrales y 2 de pasillo)

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.

Caso 2. Juego de las tres en raya


Implemente un juego entre dos jugadores sobre un tablero teniendo en cuenta los siguientes requisitos:
a) El juego consiste en colocar, por turnos, una serie de fichas sobre un tablero de 3 3 3. Cada
jugador dispone de 5 fichas de un mismo color. Las fichas pueden situarse en cualquier posi-
cin, y gana el jugador que logra poner 3 fichas alineadas (tres en raya), vertical, horizontal o
diagonalmente. Cada jugada consiste en situar una ficha en una posicin libre; una vez situa-
da la ficha, sta no puede moverse en el transcurso de la partida. La partida puede terminar
con la victoria de un jugador o en tablas, situacin en la que ningn jugador logra alinear tres
fichas. El usuario, a partir de ahora el contrincante, jugar contra el ordenador.
b) Tanto el nombre del contrincante como la informacin de quin comienza la primera partida
debe suministrarse desde el teclado, no por lnea de rdenes. En partidas sucesivas (jugadas
consecutivamente) el jugador inicial se ir alternando. El nombre del contrincante se mostrar
por pantalla al solicitar una jugada (fila y columna) y al informar del resultado de la partida
(hay un ganador o se producen tablas). El juego contina, partida tras partida, hasta que el con-
trincante indique que desea dejar de jugar (se recomienda utilizar un men de opciones que
solicite la pulsacin de una tecla al finalizar cada partida). Se debe controlar la entrada de
datos incorrecta, en cuyo caso el programa no finalizar su ejecucin sino que ofrecer la posi-
bilidad de introducir nuevamente los datos por teclado. En particular, se deber controlar que
las coordenadas introducidas para indicar la casilla a la que el contrincante desea mover la
ficha estn entre los lmites aceptables.
c) El estado del tablero despus de cada jugada debe dibujarse utilizando caracteres ASCII,
representando las fichas de cada jugador con caracteres diferentes. La jugada (fila y columna)
que realiza el ordenador se generar de manera aleatoria (lgicamente tiene que ser una posi-
cin vaca). A tal efecto se recomienda el mtodo random()de la clase Math. Incluya un
mtodo que devuelva un valor lgico indicando si la casilla central est libre en cuyo caso el
ordenador colocar la ficha all. Es opcional la posibilidad de dotar al ordenador de una mejor
estrategia de juego para ganar la partida. Incluya un mtodo que compruebe si el contrincan-
te va a hacer tres en raya, es decir, que el contrincante tenga dos fichas en la misma lnea. El
mtodo devolver los valores i, j de la casilla libre de la lnea donde el contrincante ya tiene
las dos fichas. El ordenador colocar la siguiente ficha en esa posicin para evitar que el con-
trincante consiga tres en raya. El algoritmo deber usar bucles for para comprobar si existen
dos fichas en la misma columna o fila, y comprobar aparte si hay dos fichas del contrincante
en diagonal. Para saber si despus de un movimiento la partida termina porque se produce una
situacin de tres en raya, debe utilizarse un mtodo que tome como argumento el tablero
(habr que decidir con qu estructura de datos se implementa el mismo) y devuelva verdade-
ro, si hay tres en raya, o falso, en caso contrario.
Prcticas y casos de estudio propuestos 349

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.

PARTE II. PROGRAMACIN ORIENTADA A OBJETOS

Caso 3. Gestin de una biblioteca


Desarrolle un programa para gestionar una biblioteca. Bsicamente el programa debe permitir catalo-
gar las nuevas incorporaciones y consultar las existentes. Las publicaciones catalogadas pueden ser
peridicas y no peridicas. De momento las publicaciones peridicas slo incorporan revistas pero
est previsto en un futuro incorporar otras publicaciones. El sistema debe recoger esta estructura. Las
publicaciones no peridicas actualmente corresponden a libros y a trabajos de investigacin, que pue-
den ser informes tcnicos o tesis doctorales. Para cada uno de estos elementos se debe especificar
como mnimo en la catalogacin:

a) Revistas: Ttulo; Volumen; Nmero; Fecha de edicin(da, mes y ao); Materia


b) Libros: Autor/es; Ttulo; Editorial; Edicin; Ao de publicacin; ISBN; Materia
c) Informes tcnicos: Autor/es; Ttulo; Departamento donde se realiza el trabajo; Fecha (mes y
ao) de publicacin; Materia
d) Tesis doctorales: Autor (1 slo); Director; Tutor; Ttulo; Departamento de realizacin del tra-
bajo; Departamento de presentacin; Materia; Calificacin

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.

Caso 4. Tienda de informtica


Desarrolle un programa que permita gestionar una tienda, especializada en productos hardware, de
acuerdo a los siguientes requisitos:

a) La tienda vende slo algunos componentes hardware, clasificados en dispositivos de almace-


namiento (discos duros, disqueteras, lectores de CDROM y grabadoras de CDROM) y com-
ponentes no montables (monitores, teclados, altavoces, ratones e impresoras).
b) Todos los elementos de la tienda poseen una marca, un modelo, una referencia para la tienda
y se compran a un distribuidor por un precio determinado.
c) El precio de venta al pblico vendr determinado por un incremento porcentual, fijado por el
dueo y constante para todos los productos, sobre el precio de compra al distribuidor.
350 Introduccin a la programacin con orientacin a objetos

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.

El programa que se pide debe permitir, mediante el uso de mens:

a) Insertar y actualizar la informacin de los productos y de los clientes.


b) Almacenar en ficheros la informacin referente a los productos y a los clientes.
c) Permitir al propietario determinar el incremento del precio final de los productos, as como
establecer el mnimo de existencias al que hace referencia el apartado e) anterior.
d) Gestionar la venta de componentes, realizando todas las operaciones necesarias sobre la tien-
da y sobre los clientes (descontar los productos vendidos y emitir factura, simplemente visua-
lizndola por pantalla).
e) Listar un catlogo de productos con su descripcin tcnica y su precio final.
f) Listar los productos cuyas existencias sean iguales o inferiores al mnimo permitido.

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

Resumen de la notacin UML

Sumario

La vista esttica Relaciones entre clases


Diagramas de clases A) Relacin de generalizacin o herencia
Clases y objetos B) Relacin de asociacin
A) Clases C) Relacin de dependencia
B) Objetos
352 Introduccin a la programacin con orientacin a objetos

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

Figura C.1. Representacin simple de clases

que se encuentra, tal y como se muestra en la Figura C.1.


Es posible representar los atributos y los procedimientos correspondientes a la clase. A tal efecto
se divide el rectngulo en tres secciones con el nombre de la clase en la primera, los atributos en la

Nombre_de_la_clase

Atributos

Procedimientos

Figura C.2. Representacin de los atributos y procedimientos de una clase

segunda y los procedimientos en la tercera, ver Figura C.2.


En los atributos se puede especificar el tipo de cada uno e incluso un valor inicial usando la sin-
taxis, atributo: tipo=valor_inicial. En los procedimientos se puede especificar slo el nombre o bien
su firma (nombre, parmetros aceptados y el tipo de los mismos) y tambin el tipo de retorno del pro-
cedimiento. En este caso, la sintaxis es similar a la de los atributos por ejemplo, actualizar (valor: dou-

Cliente

nombre:String
nmero:int
saldo:double=0.0

ActualizarSaldo (valor:double):void
daSaldo ( ):double
daNmero ( ):int
daNombre ( ):String

Figura C.3. Caracterizacin de atributos y procedimientos


354 Introduccin a la programacin con orientacin a objetos

ble): double. Como ejemplo, vase la Figura C.3.


Los miembros de una clase que se puedan considerar globales a la misma, tal como los miembros
estticos (static) en Java, se representan subrayados en el diagrama de clase, tal y como ilustra la

Nombre clase

atributo esttico

Figura C.4. Representacin de miembros estticos de clase

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

Figura C.5. Representacin de la visibilidad de los miembros de una clase

miembro de la clase considerado, vase la Figura C.5.


Es interesante poder representar tambin las clases abstractas como parte del proceso de mode-
lado. UML lo permite y a tal efecto se usan diagramas de clase donde el nombre de la clase se indi-
ca en cursiva. En las situaciones donde el uso de la cursiva no es posible (como sobre una pizarra)
puede resultar til indicar que la clase es abstracta por medio de un valor etiquetado. Estrictamen-
te hablando, un valor etiquetado corresponde a un elemento de informacin sobre una entidad y
consiste en un identificador que indica una propiedad de la entidad, un signo de igual y el valor que
dicha propiedad adquiere. El valor etiquetado se coloca entre llaves ({}). Para indicar una clase abs-
tracta podemos simplemente asociar el valor etiquetado {abstracta} al nombre de la clase, vase la
Figura C.6.

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

Figura C.6. Representacin de una clase abstracta

PrimerCliente:Cliente

Figura C.7. Representacin de un objeto llamado PrimerCliente perteneciente a a clase 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.

RELACIONES ENTRE CLASES


A la hora de construir un modelo de un sistema es importante no slo identificar las clases presentes
o necesarias, sino tambin sus relaciones. En un modelo orientado a objetos podemos distinguir tres
tipos de relaciones, la de generalizacin o herencia, la de asociacin y la de dependencia.

A) RELACIN DE GENERALIZACIN O HERENCIA


La generalizacin o herencia es una relacin taxonmica entre clases. Una de las clases representa el
conjunto general y otra de ellas es un subconjunto especfico de esa clase general. La primera de las
clases se denomina clase padre o antecesora y la segunda es la clase hija o descendiente. En UML esta
relacin se simboliza por medio de una flecha, con la punta hueca, que apunta hacia la clase padre. Un

Clase Padre

Clase Hija1 Clase Hija2

Figura C.8. Notacin para la relacin de generalizacin o herencia


356 Introduccin a la programacin con orientacin a objetos

ejemplo de la notacin se muestra en la Figura C.8.

B) RELACIN DE ASOCIACIN
Esta relacin se definira como una relacin estructural entre ejemplares de la misma o diferentes cla-

Clase A Clase B

Figura C.9. Representacin bsica de la relacin de asociacin

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

Figura C.10. Multiplicidad en una relacin de asociacin. Un ejemplar de Clase A contiene


un mnimo de uno y un mximo indeterminado de ejemplares de 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

Figura C.11. Representacin de la navegabilidad de una relacin de asociacin


Resumen de la notacin UML 357

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

Figura C.12. Relacin de agregacin entre la Clase A y la 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

Figura C.13. Relacin de composicin entre la Clase A y la 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

Figura C.14. Relacin de dependencia entre la Clase A y 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

Gua de estilo en Java

Sumario

Formato de lneas Declaraciones


Ficheros Sentencias de control
Clases Documentacin
Visibilidad de miembros de clase
Identificadores
360 Introduccin a la programacin con orientacin a objetos

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,

public void metodoHaceAlgo(int valor1, double valor2,


int valor3){

resultado = aux* (final-inicial+desplazamiento)


+ referencia;

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,

public double calcularDescuento (double total) {


int aux=0; // Se sangra dos espacios
if (total>LIMITE) {
total = total * 0.9; // Se sangra otros dos espacios
}
return total;
}

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,

public void nombreMtodo(int valor1, double valor2)

en lugar de,

public void nombreMtodo (int valor1, double valor2)

obsrvese el espacio en blanco delante de la apertura de parntesis.

VISIBILIDAD DE MIEMBROS DE CLASE


1. Los atributos (datos) deben declararse privados (private) excepto los que se pretenda que
sean accesibles por herencia que deben ser protegidos (protected). Se debe evitar el uso de
datos pblicos.
2. Los procedimientos (mtodos) de la interfaz pblica deben declararse public, los de sopor-
te privados (private).

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

3. Para las variables y objetos utilice identificadores en minsculas. Si el identificador consta de


varias palabras se colocan separadas por el carcter de subrayado o bien todas seguidas. Es
este ltimo caso, la primera de ellas se escribe en minsculas y la inicial de las dems en
maysculas. Por ejemplo,

double valorTotal=0.0;
int cantidadInicial=0;
String nombre_cliente=Alfonso;

4. Para las constantes, el identificador debe usarse en maysculas. Si el identificador consta de


varias palabras se separan con el carcter de subrayado. Ejemplo correctos seran,

final int MAXIMO=10;


final double PRECISION=0.001;
final double ANCHURA_TOTAL=29.5;

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,

public void incorporar(double cantidad){


- - - cuerpo del mtodo - - -
}
public double obtenerTotal(){
- - - cuerpo del mtodo - - -
}

6. Para el identificador de los paquetes se recomienda usar minsculas.

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,

int lista [];

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;

5. Una recomendacin muy frecuente es la siguiente. En los if anidados si un if interno corres-


ponde a una clusula else externa se coloca el if a continuacin y en la misma lnea que el
else. El bloque de este nuevo if se cierra al nivel del else anterior. Por ejemplo,
364 Introduccin a la programacin con orientacin a objetos

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

Componentes AWT y Swing Programacin dirigida por sucesos


366 Introduccin a la programacin con orientacin a objetos

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


En este apartado se describen brevemente los dos grupos de herramientas grficas que Java ofrece para
la construccin de interfaces. Se trata de los componentes AWT (Abstract Window Toolkit), tambin
llamados componentes pesados, y los componentes ligeros, conocidos generalmente como compo-
nentes Swing. Vamos a considerarlos por separado.

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:

a) Los contenedores (la clase container)


Son componentes AWT que pueden incluir a otros componentes. Los contenedores (como muestra la
Figura E.1) heredan de la clase abstracta Component que encapsula los atributos de los componentes
visuales. Todos los elementos de una interfaz de usuario son subclases de la clase Component, la cual
contiene ms de cien mtodos que se encargan tanto de la gestin de sucesos (eventos), como de la
entrada por teclado o por ratn, o de repintar una ventana.

1
En ingls, Event Driven Programming.
Interfaces grficas de usuario 367

Object

Component

Container

Window Panel

Frame

Figura E.1. Jerarqua bsica de clases del AWT

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.

b) Componentes de interfaz de usuario


Estos componentes tambin heredan de la clase Component. Ejemplos de este tipo de componente
son los botones, las casillas de verificacin, las etiquetas, las barras de desplazamiento o las listas.
Aunque no forma parte de la jerarqua de ventanas que se muestra en la Figura E.1 conviene sea-
lar que existe otro tipo de ventanas llamado Canvas. Una ventana Canvas representa una ventana
vaca sobre la que se puede dibujar.

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:

JLabel(String s, Icon i, int linea). Se indica el texto inicial de la etiqueta, el icono y la


alineacin horizontal del contenido en la etiqueta, respectivamente.
JLabel(String s). Inserta slo el texto inicial.
JLabel(String s, int i). Inserta el texto inicial y la posicin del mismo.
JLabel(Icono i). Inserta un icono en la etiqueta.
JLabel(). Crea una etiqueta sin texto.

Para insertar texto en la etiqueta se usa el mtodo:


void setText(String s)

Para obtener el texto de una etiqueta se utiliza el mtodo:


String getText()

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

JTextField JButton JLabel


Figura E.2. Ejemplo de JFrame, J Button, JLabel y JTextField

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:

JButton(Icon i). Inserta un icono en el botn.


JButton(String s). Inserta un texto en el botn.
JButton(String s, Icon i). Inserta un texto y un icono en el botn.

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:

JRadioButton(Icon i). Inserta un icono en el botn.


JRadioButton(Icon i, boolean estado). Igual que el anterior pero adems indica si el botn

Figura E.3. Ejemplo de JRadioButton


370 Introduccin a la programacin con orientacin a objetos

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:

JCheckBox(Icon i). Inserta un icono en la casilla de verificacin.


JCheckBox(Icon i, boolean estado). Igual que el anterior pero adems indica si la casilla de
verificacin esta inicialmente seleccionada.
JCheckBox(String s). Inserta el texto asociado a la casilla de verificacin.
JCheckBox(String s, Icon i). Similar al anterior pero indicando el icono que se quiere inser-
tar en la casilla.
JCheckBox(String s, Icon i, boolean estado). Se indica el texto, el icono y el estado de
la casilla.

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.

JComboBox(). Es el constructor por defecto.


JComboBox(Vector v). Este constructor necesita un objeto de la clase Vector que contenga
los elementos que deben aparecer en la lista del combo.

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:

Figura E.4. Ejemplo de JCheckBox


Interfaces grficas de usuario 371

JTable(Object datos[][], Object colTitulos[])

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:

getSelectedText(). Devuelve el texto seleccionado por el usuario.


cut(). Corta el texto seleccionado y lo coloca en el portapapeles del sistema.
copy(). Copia el texto seleccionado y lo coloca en el portapapeles del sistema.
paste(). Pega el texto contenido en el portapapeles en la posicin seleccionada.
getText(). Devuelve el texto contenido en un componente.
setText(String s). Inserta texto en un componente.

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:

JTextArea(). Constructor por defecto.


JTextArea(String s). Inserta un texto inicial.
JTextArea(String s, int filas, int columnas). Inserta un texto inicial y adems indica
el nmero de filas y columnas que va a tener el componente.

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,

void setLayout (LayoutManager layoutObjeto)

donde layoutObjeto es una referencia a un gestor de organizacin. Si no se invoca a este mtodo se


372 Introduccin a la programacin con orientacin a objetos

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.

PROGRAMACIN DIRIGIDA POR SUCESOS


Hasta ahora hemos presentado los componentes de una IGU pero, cmo funciona una IGU? La mane-
ra de funcionar es diferente de los programas que hemos presentado hasta ahora. En los programas vis-
tos, las sentencias se van ejecutando una tras otra en un orden determinado. De hecho, la filosofa
bsica es que hay una sola entidad (el computador) que va ejecutando las instrucciones. En una IGU
no es ste el modelo aplicado sino el denominado de programacin dirigida o conducida por suce-
sos. En este modelo se crean entidades (objetos) que representan los sucesos de inters (como hacer
un click con el ratn, o moverlo, o pulsar una tecla). De hecho, estos objetos se pueden considerar
como los encargados de disparar el suceso cuando ste se produzca para que el sistema lo reconozca.
Cmo sabe el sistema cundo se ha producido un suceso? Porque construimos entidades (objetos) que
actan como gestores (u oyentes) de sucesos (event listeners). Estos objetos entran en juego cuando se
produce el suceso al que estn asociados. Como podemos ver, aqu la evolucin temporal del progra-
ma depende de los sucesos que se produzcan, de forma que el mismo programa se comporta de mane-
ra diferente si la serie de sucesos que experimenta son diferentes.
Para gestionar un suceso se deben seguir los siguientes pasos: Primero definir una clase Liste-
ner (oyente, receptora) para una determinada clase de suceso. Dicha clase debe implementar la inter-
faz receptora que corresponda al suceso que queramos gestionar. El segundo paso sera sobrescribir
los mtodos de la interfaz receptora encargados de capturar los sucesos que nos interesan. De esta for-
ma personalizamos el mtodo a nuestras necesidades, ya que indicamos cmo debe comportarse el
programa ante la ocurrencia de un determinado suceso. El tercer paso sera definir en la clase princi-
pal un objeto de la clase receptora, y adems registrarla para la notificacin de la ocurrencia de suce-
sos generados por los componentes. Adems, para que todo funcione correctamente se debe importar
el paquete java.awt.event que es el que contiene la mayora de las clases e interfaces de sucesos.
A continuacin, se muestra un ejemplo, Programa E.1, donde se han indicado los tres pasos ante-
riores. El programa muestra el ejemplo de clculo de un factorial del Programa 4.6, ahora con una inter-
faz grfica. En el programa se gestiona un suceso de un componente de clase TextField. Esta clase
dispara un suceso cuando el usuario escribe un nmero en el rea de texto y despus pulsa INTRO. ste
es el suceso que vamos a gestionar. Para ello, creamos una clase nueva llamada MiactionListener,
que representa nuestro receptor de sucesos. El mtodo heredado actionPerformed, de la clase recep-
tora MiactionListener, se ha sobrescrito para que muestre el factorial del nmero ledo cuando se
pulse INTRO.

Programa E.1. Ejemplo de gestin de sucesos

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
Interfaces grficas de usuario 373

Programa E.1. Ejemplo de gestin de sucesos (continuacin)

public class Principal extends Applet{

int numero; //Declaramos la variable donde se va a almacenar


// el nmero
TextField entrada; //Declaramos el rea de texto
Label etiqueta1; //Declaramos la etiqueta que muestra el resultado
double resultado; //Declaramos la variable resultado

//Se inicializan las etiquetas y se aaden a la ventana


public void init(){
etiqueta1=new Label(Programa Factorial);
entrada= new TextField(8);
add(entrada);
add(etiqueta1);
}

//Creacin y registro un receptor de sucesos para el rea de texto


public void start(){
entrada.addActionListener
(new MiactionListener(entrada)); //PASO 3
}

//Mtodo que calcula el factorial


public double factorialIterativo (int n){

int i;
i=0;
double factorial;
factorial=1.0;
do {
i=i+1;
factorial=factorial*i;
} while (i<n);
return factorial;
}

//clase para recibir los sucesos que se produzcan sobre el TextField


// PASO 1

class MiactionListener implements java.awt.event.ActionListener{

TextField otrocampoTexto;
MiactionListener(TextField campoTexto){

otrocampoTexto=campoTexto;
}

public void actionPerformed


374 Introduccin a la programacin con orientacin a objetos

(java.awt.event.ActionEvent evt){ //PASO 2


//Sobreescribo el mtodo para que cuando se introduzca un nmero
//y se pulse intro se lea el nmero y se calcule el
//factorial del mismo
numero=Integer.parseInt(otrocampoTexto.getText());
resultado=factorialIterativo(numero);
etiqueta1.setText(El resutado es +resultado);
}
}//De la clase MiactionListener

}//De la clase principal

EJEMPLO DE UTILIZACIN DE SWINGS


A continuacin, se presenta un ejemplo, Programa E.2, donde se utilizan varios de los componentes
anteriormente considerados. El programa implementa una calculadora con un campo de texto donde
introducir los datos y visualizar los resultados. Las distintas operaciones se representan sobre varios
botones. El cdigo que se muestra ha sido generado con un entorno de desarrollo integrado, en ingls
Integrated Development Environment (IDE) el cual facilita mucho la elaboracin de interfaces grfi-
cas ya que basta con elegir el componente a usar entre una paleta de componentes que el IDE mues-
tra. Otra ventaja de usar un IDE es que genera parte del cdigo automticamente, por ejemplo declara
los controles o escribe parte de las clases y los mtodos que gestionan los sucesos. Esta aplicacin se
ha realizado con el programa JBuilder, sin embargo existen otros como el VisualCafe o el Visual J++.

Programa E.2. Ejemplo de calculadora simple implementada en una IGU

import java.awt.*;
import javax.swing.*;
//El siguiente paquete se usa para gestionar los sucesos
import java.awt.event.*;

public class Principal {


public static void main(String[] args) {
//Se crea un objeto calculadora, se le asigna un tamao y
//se hace visible
Calculadora calc = new Calculadora();
calc.setSize(250,150);
calc.setVisible(true);
}
}

class Calculadora extends JFrame {


JPanel jPanel1 = new JPanel(); //Se crea un objeto panel
BorderLayout borderLayout1 = new BorderLayout(); //Se crea un gestor

//Se crea un campo de texto donde se introduciran


//los operandos y el resultado
JTextField txtEntrada = new JTextField();

//Se crea un botn para cada operacin


Interfaces grficas de usuario 375

Programa E.2. Ejemplo de calculadora simple implementada en una IGU (continuacin)

JButton btSuma = new JButton();


JButton btResta = new JButton();
JButton btMultiplicacion = new JButton();
JButton btDivision = new JButton();
JButton btResultado = new JButton();
376 Introduccin a la programacin con orientacin a objetos

Programa E.2. Ejemplo de calculadora simple implementada en una IGU (continuacin)

//Se crea una etiqueta que servir para indicar que la


//operacin se realiz
JLabel lblResultado = new JLabel();

double operando1, operando2 = 0;


double resultado=0;
int operador=-1;

public Calculadora() {
try{
dibujaVentana();
}
catch (Exception e){
System.out.println(Se ha producido la exception+e.toString());
}
}

private void dibujaVentana() throws Exception {


//Asigna el gestor creado al panel
this.getContentPane().setLayout(borderLayout1);
this.setTitle(Calculadora);

//Se configura el campo de texto y se asigna un tamao


txtEntrada.setPreferredSize(new Dimension(200, 19));
txtEntrada.setMinimumSize(new Dimension(200, 19));

//Se indica que el texto est alineado a la derecha


txtEntrada.setHorizontalAlignment(JTextField.RIGHT);
txtEntrada.setText();

//A continuacin se configuran todos los botones


//asignndole una etiqueta, un tipo de letra y
//un receptor (gestor) de sucesos
btSuma.setText(+);
btSuma.setFont(new Font(Dialog, 1, 18));
btSuma.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
btSuma_actionPerformed(e);
}
});

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

Programa E.2. Ejemplo de calculadora simple implementada en una IGU (continuacin)

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

//Se configura una etiqueta


lblResultado.setMaximumSize(new Dimension(500, 30));
lblResultado.setPreferredSize(new Dimension(250, 15));
lblResultado.setMinimumSize(new Dimension(250, 15));
this.getContentPane().add(jPanel1, BorderLayout.CENTER);

//Se aaden los componentes creados al panel


jPanel1.add(txtEntrada, null);
jPanel1.add(btSuma, null);
jPanel1.add(btResta, null);
jPanel1.add(btMultiplicacion, null);
jPanel1.add(btDivision, null);
jPanel1.add(btResultado, null);
jPanel1.add(lblResultado, null);
}

//Los siguientes mtodos definen las operaciones de la calculadora


static double sumar(double a1,double a2){
return a1+a2;
}//fin sumar

static double restar(double a1,double a2){


return a1-a2;
}//fin restar

static double multiplicar(double a1,double a2){


return a1*a2;
}//fin multiplicar

static double dividir(double a1,double a2){


if (a2!=0)
return a1/a2;
378 Introduccin a la programacin con orientacin a objetos

Programa E.2. Ejemplo de calculadora simple implementada en una IGU (continuacin)

else return 0;
}//fin dividir

//Los siguientes mtodos gestionan los sucesos que se produzcan


void btSuma_actionPerformed(ActionEvent e) {
Double aux=new Double(txtEntrada.getText());
operando1=aux.doubleValue();
txtEntrada.setText();
txtEntrada.requestFocus();
operador=1;
lblResultado.setText();

Figura E.5. Interfaz de la calculadora

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

La Figura E.5 muestra el aspecto final de la interfaz.

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

Diferencias entre las aplicaciones y las applets Funcionamiento de las applets


Creacin de una applet
380 Introduccin a la programacin con orientacin a objetos

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.

DIFERENCIAS ENTRE LAS APLICACIONES Y LAS APPLETS


La primera diferencia es que todas las aplicaciones necesitan un mtodo main para comenzar su eje-
cucin. Las applets, en cambio, no lo requieren, puesto que contienen otros mtodos que la inicializan
y que se invocan automticamente. En el ltimo apartado de este apndice describiremos algunos de
estos mtodos y su funcionamiento. La segunda diferencia es que las applets no utilizan para la entra-
da y salida los flujos o streams, como ocurre con las aplicaciones. En su lugar usan la interfaz pro-
porcionada por los AWT (descritos en el apndice E).
Las applets terminan su ejecucin cuando lo hace el visor o la pgina web en la que se estn ejecu-
tando, por esta razn no necesitan mecanismos de parada del programa (por ejemplo, un botn de cierre).

CREACIN DE UNA APPLET


Cada applet es una subclase de la clase Applet contenida en el paquete applet. Puesto que una
applet necesita emplear mtodos de la clase Applet, hay que importar siempre el paquete applet.
Adems, siempre se indica explcitamente que se hereda (extends) de Applet. Con todo esto, las
dos primeras lneas de una applet son:

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.

Programa F.1. Ejemplo de applet

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:

<APPLET CODE=HolaMundo.class WIDTH=300 HEIGHT=300>


</APPLET>

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:

C:\appletviewer HolaMundo.html <Enter>

La Figura F.2 muestra el resultado.

Figura F.1. Resultado de la applet HolaMundo


382 Introduccin a la programacin con orientacin a objetos

Figura F.2. Applet HolaMundo ejecutada desde un visor

FUNCIONAMIENTO DE LAS APPLETS


Una pregunta que puede plantearse es cmo arrancan y terminan las applets, puesto que no tie-
nen mtodo main. La tcnica es la siguiente. Las applets proporcionan cuatro mtodos: init(),
destroy(), start() y stop(). Estos mtodos gestionan los procesos de arranque, detencin, reac-
tivacin y parada definitiva de la applet. Dichos procesos los gestiona el sistema de ejecucin de Java
que se est usando.
El mtodo init() es similar a un mtodo constructor, ya que proporciona los valores iniciales
para una applet. Se llama cuando la applet se carga por primera vez en el navegador o visualizador.
El mtodo start() se llama despus de invocar al mtodo init(). La diferencia entre ambos mto-
dos es que init() slo se llama una vez durante la vida de la applet. En cambio, start() puede
llamarse varias veces, por ejemplo cada vez que el usuario regrese a la pgina despus de haber visi-
tado otra u otras. Adems, start() se utiliza cuando se requieren elementos dinmicos al estable-
cer inicialmente la applet (por ejemplo, en una animacin). Despus del arranque se invocan el resto
de mtodos.
El mtodo stop()se ejecuta cuando la applet deja de ser visible. Al regresar a la pgina se vuel-
ve a invocar el mtodo start(). El mtodo destroy() libera los recursos reservados para la applet,
por lo que se usa cuando el usuario decide cerrar la applet.
Estos cuatro mtodos estn definidos en la clase Applet pudiendo el usuario sobrescribirlos segn
sus necesidades.
Teniendo en cuenta lo visto hasta ahora estamos en condiciones de convertir una aplicacin en una
applet. Para ello, basta recordar tres cosas: en las applets la entrada y salida de datos se realiza a travs
de la interfaz del AWT. Segundo, las applets no necesitan mecanismos de parada ya que terminan su
ejecucin cuando se cierra la pgina web o la ventana del visor. Tercero, las applets no requieren un
mtodo main, puesto que su funcionamiento es realizado por el paquete applet, el cual se encarga
entre otras cosas de crear la ventana y establecer su visibilidad.
Como ejemplo de conversin de una aplicacin en una applet convertiremos el Programa 4.6 mos-
trado en el Captulo 4 que calculaba de forma iterativa el factorial de un nmero que se introduca por
por teclado. El cdigo de dicha aplicacin era:
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));
Applets 383

System.out.println (Introduzca numero para calcular el +


factorial:);
n=Integer.parseInt (leer.readLine());
System.out.println (Numero introducido: +n); // Eco de
// la entrada

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

Programa F.2. Applet que calcula el factorial de un nmero

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class Factorial extends Applet{

int numero; //declaramos la variable donde


//se va a almacenar el nmero
TextField entrada; //declaramos el rea de texto
Label etiqueta1; //declaramos la etiqueta que muestra el resultado
double resultado; //declaramos la variable resultado

public void init(){


//Se inicializan las etiquetas y se aaden a la ventana
384 Introduccin a la programacin con orientacin a objetos

Programa F.2. Applet que calcula el factorial de un nmero (continuacin)

etiqueta1=new Label(Programa Factorial);


entrada= new TextField(8); // TextField con 8 caracteres
add(entrada);
add(etiqueta1);
}

public void start(){


//Se crea y registra un gestor de sucesos para el rea de texto
entrada.addActionListener(new MiactionListener(entrada));
}

//Mtodo que calcula el factorial


public double factorialIterativo (int n){

int i;
i=0;
double factorial;
factorial=1.0;
do {
i=i+1;
factorial=factorial*i;
} while (i<n);
return factorial;
}

//Clase para recibir los sucesos que se produzcan sobre el TextField


class MiactionListener implements java.awt.event.ActionListener{

TextField otrocampoTexto;

MiactionListener(TextField campoTexto){
otrocampoTexto=campoTexto;
}

// Se sobrescribe el mtodo para que cuando se introduzca un nmero


// y se pulse intro se lea el numero y se calcule el
// factorial del mismo
public void actionPerformed (java.awt.event.ActionEvent evt){

numero=Integer.parseInt(otrocampoTexto.getText());
resultado=factorialIterativo(numero);
etiqueta1.setText(El resultado es +resultado);
}
}//Fin de la clase interna MiactionListener

}//Fin de la clase Factorial

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.

Anda mungkin juga menyukai