Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Java 2. Curso de Programación - Javier PDF
Java 2. Curso de Programación - Javier PDF
C A PÍT U L O 2. PR O G R A M A C IÓ N O R IE N T A D A A O B JE T O S .............................. 23
C A PÍT U L O 3. E LE M EN TO S D EL L E N G U A JE ............................................................ 37
C A P ÍT U L O 4. ESTR U C T U R A DE UN P R O G R A M A .................................................. 63
C A P ÍT U L O 7. M A T R IC E S .................................................................................................... 163
C A P ÍT U L O 8. M É T O D O S ..................................................................................................... 215
PA R TE 4. A P É N D IC E S .................................................................. 751
A. A Y U D A ................................................................................................................................... 753
B. JA V A C O M PA R A D O C O N C /C + + ................................................................................ 755
D. C O N T E N ID O D E L C D -R O M ......................................................................................... 761
E. C Ó D IG O S DE C A R A C T E R E S ........................................................................................ 763
F. ÍN D IC E .................................................................................................................................... 769
CONTENIDO
P R Ó L O G O ............................................................................................................................ X X III
C A P ÍT U L O 1. F A S E S EN E I. D E S A R R O L L O D E U N P R O G R A M A 3
Q U É ES UN P R O G R A M A ............................................................................................... 3
LEN G U A JES D E P R O G R A M A C IÓ N ......................................................................... 4
C o m p ila d o re s................................................................................................................... 6
Intérpretes.......................................................................................................................... 6
¿Q U É ES J A V A ? ................................................................................................................. 7
H ISTO R IA DE J A V A ......................................................................................................... 8
¿PO R Q U É A PR EN D ER J A V A ? ................................................................................... 9
R EA L IZ A C IÓ N DE UN PR O G R A M A EN J A V A ................................................... 9
C óm o crear un p ro g ram a.............................................................................................. 11
Interfaz de línea de ó rd e n es......................................................................................... 12
¿Q ué hace este p ro g ram a?...................................................................................... 12
G uardar el program a escrito en el d is c o ............................................................ 13
C om pilar y ejecutar el p ro g ra m a ......................................................................... 13
B iblioteca de fu n c io n e s.......................................................................................... 15
G uardar el program a ejecutable en el disco....................................................... 15
D epurar un p ro g ra m a .............................................................................................. 16
E ntorno de desarrollo in te g ra d o ................................................................................. 16
E JER C IC IO S R E S U E L T O S ............................................................................................. 19
E JER C IC IO S P R O P U E S T O S .......................................................................................... 21
X JA V A: CU R SO DE PROGRAM A CIÓN
C A P ÍT U L O 2. P R O G R A M A C IÓ N O R IE N T A D A A O B J E T O S ........................ 23
C A P ÍT U L O 3. E L E M E N T O S D E L L E N G U A J E ...................................................... 37
PA L A B R A S C L A V E .......................................................................................................... 47
C O M E N T A R IO S ................................................................................................................. 47
D E C LA R A C IÓ N D E C O N ST A N T E S S IM B Ó L IC A S ............................................ 48
¿P or qué utilizar c o n sta n te s?............................ 49
D E C L A R A C IÓ N D E U N A V A R IA B L E ...................................................................... 49
Iniciación d e una v ariab le............................................................................................. 50
E X PR E SIO N ES N U M É R IC A S ....................................................................................... 51
C O N V E R S IÓ N E N T R E T IPO S DE D A T O S .............................................................. 51
O P E R A D O R E S ..................................................................................................................... 52
O peradores aritm ético s.................................................................................................. 52
O peradores de re la ció n .................................................................................................. 53
O peradores ló g ico s......................................................................................................... 54
O peradores unitarios....................................................................................................... 55
O peradores a nivel de b i t s ............................................................................................ 55
O peradores de asig n ació n ............................................................................................. 56
O perador c o n d ic io n a l.................................................................................................... 57
P R IO R ID A D Y O R D E N D E E V A L U A C IÓ N ........................................................... 58
E JE R C IC IO S R E S U E L T O S ............................................................................................. 58
E JE R C IC IO S P R O P U E S T O S ........................................................................................... 60
C A P ÍT U L O 4. E S T R U C T U R A D E U N P R O G R A M A ............................................. 63
E ST R U C T U R A D E U N A A P L IC A C IÓ N JA V A ...................................................... 63
Paquetes y protección de clase s.................................................................................. 67
Protección de una c la s e ................................................................................................. 68
S entencia im port.............................................................................................................. 69
D efiniciones y declaracio n es....................................................................................... 70
Sentencia sim p le.............................................................................................................. 71
Sentencia com puesta o blo q u e.................................................................................... 72
M éto d o s.............................................................................................................................. 72
D efinición de un m é to d o ........................................................................................ 72
M étodo m a in .................................................................................................................... 73
C rear objetos de una c la s e ............................................................................................ 73
C óm o acceder a los m iem bros de un o b je to ........................................................... 75
Protección de los m iem bros de una c la s e ................................................................ 76
M iem bro de un objeto o de una c la s e ....................................................................... 77
R eferencias a o b jeto s..................................................................................................... 79
P asando argum entos a los m é to d o s ........................................................................... 82
PR O G R A M A JA V A FO R M A D O PO R M Ú LTIPLES F IC H E R O S ..................... 83
A C C E SIB IL ID A D D E V A R IA B L E S ............................................................................ 85
E JE R C IC IO S R E S U E L T O S ............................................................................................. 86
E JE R C IC IO S P R O P U E S T O S ........................................................................................... 88
X I I JA V A: C U R S O DE PROGRAM ACIÓN
C A P ÍT U L O 5. C L A S E S D E U S O C O M Ú N ................................................................... 89
D A TO S N U M ÉR IC O S Y C A D EN A S DE C A R A C T E R E S ................................... 89
EN TR A D A Y S A L ID A ..................................................................................................... 91
Flujos de e n tra d a ............................................................................................................ 93
Flujos de salid a................................................................................................................ 94
E xcepciones...................................................................................................................... 95
Flujos estándar de E /S ................................................................................................... 96
D eterm inar la clase a la que pertenece un o b je to ........................................... 97
B u fferedlnputS tream ............................................................................................... 98
B u ffered R ead er........................................................................................................ 99
P rintStream ................................................................................................................... 100
T rabajar con tipos de datos p rim itiv o s..................................................................... 102
C lases que encapsulan los tipos p rim itiv o s ...................................................... 103
C lase L eer......................................................................................................................... 106
¿D Ó N D E SE U B IC A N LAS C LA SES Q U E DAN S O P O R T E ?........................... 109
V ariable C L A S S P A T H .................................................................................................. 110
C A R Á C TE R FIN DE F IC H E R O ...................................................................................... 110
C A R A C TER ES V \n.............................................................................................................. 112
M ÉTO D O S M A T E M Á T IC O S......................................................................................... 114
EJER C IC IO S R E S U E L T O S ............................................................................................. 116
EJER C IC IO S P R O P U E S T O S .......................................................................................... 119
C A P ÍT U L O 6. S E N T E N C IA S D E C O N T R O L ............................................................. 121
C A P ÍT U L O 7. M A T R IC E S .................................................................................................. 163
C A P ÍT U L O 8. M É T O D O S ................................................................................................... 215
R EC U RSIV ID A D 585
CO N TEN ID O X I X
O R D E N A C IÓ N D E D A T O S ........................................................................................... 591
M étodo de la b u rb u ja..................................................................................................... 592
M étodo de in serc ió n ...................................................................................................... 595
M étodo q u ick so rt............................................................................................................ 596
C om paración de los m étodos ex p u e sto s.................................................................. 600
B Ú SQ U E D A D E D A T O S .................................................................................................. 600
B úsqueda secuencial...................................................................................................... 600
B úsqueda b in a ria............................................................................................................ 601
Búsqueda de cad en as..................................................................................................... 602
O R D E N A C IÓ N DE FIC H ER O S EN D IS C O ............................................................. 605
O rdenación de ficheros. A cceso secuencial............................................................ 606
O rdenación de Ficheros. A cceso a le a to rio ............................................................... 614
A L G O R ITM O S H A S H ...................................................................................................... 616
M atrices h ash .................................................................................................................... 617
M étodo hash a b ie rto ...................................................................................................... 618
M étodo hash con d esb o rd am ien to ............................................................................. 619
E lim inación de elem entos............................................................................................. 620
C lase C H ash A b ierto ...................................................................................................... 620
U n ejem plo de una m atriz h a s h .................................................................................. 624
E JE R C IC IO S R E S U E L T O S ...................................................................................... 627
E JE R C IC IO S P R O P U E S T O S .......................................................................................... 631
¿Q U É ES IN T E R N E T ?...................................................................................................... 688
In tra n e t.............................................................................................................................. 689
E x tra n e t............................................................................................................................. 689
T erm inología In tern et.................................................................................................... 689
SERV IC IO S EN IN T E R N E T ............................................................................................ 692
PÁ G IN A S W E B .................................................................................................................... 695
Q ué es H T M L .................................................................................................................. 695
Etiquetas básicas H T M L .............................................................................................. 696
Etiquetas de form ato de te x to ..................................................................................... 697
U R L .................................................................................................................................... 699
Enlaces entre p á g in a s.................................................................................................... 699
G ráfico s............................................................................................................................. 701
M arco s............................................................................................................................... 702
O tro s................................................................................................................................... 703
PÁGIN AS W EB D IN Á M IC A S ....................................................................................... 703
A P P L E T S ............................................................................................................................... 705
C rear un a p p le t................................................................................................................ 705
La clase A p p let................................................................................................................ 707
public void init()........................................................................................................ 708
public void s ta rt()..................................................................................................... 708
public void paint(G raphics g ) ............................................................................... 708
public void s to p ()..................................................................................................... 708
public void d estro y O ............................................................................................... 709
Un ejem plo sim ple.......................................................................................................... 709
C O N TE N ID O X X I
PA R TE 4. A P É N D IC E S ................................................................. 751
A. A Y U D A ................................................................................................................................... 753
B. JA V A C O M P A R A D O C O N C /C + + ............................................................................ 755
C . P L A T A F O R M A S U N IX /L IN U X ................................................................................. 759
D. C O N T E N ID O D E L C D -R O M ....................................................................................... 761
E . C Ó D IG O S D E C A R A C T E R E S .................................................................................... 763
F. ÍN D I C E ................................................................................................................................... 769
PARTE
Programación básica
• Fases en el desarrollo de un program a
• Program ación orientada a objetos
• Elem entos del lenguaje
• E structura de un program a
• Clases de uso com ún
• Sentencias de control
• M atrices
• M étodos
CA PÍTU LO 1
©F.J.Cebalos/RA-MA
FASES EN EL DESARROLLO DE
UN PROGRAMA
E n este capítulo aprenderá lo que es un program a, cóm o escribirlo y qué hacer pa
ra que el ordenador lo ejecute y m uestre los resultados perseguidos. T am bién ad
quirirá conocim ientos generales acerca de los lenguajes de program ación
utilizados para escribir program as. D espués, nos centrarem os en un lenguaje de
program ación específico y objetivo de este libro, Java, presentando sus antece
dentes y m arcando la pauta a seguir para realizar un program a sencillo.
QUÉ ES UN PROGRAMA
Probablem ente alguna vez haya utilizado un ordenador para escribir un docum ento
o para divertirse con algún juego. R ecuerde que en el caso de escribir un docu
m ento, prim ero tuvo que poner en m archa un procesador de textos, y que si quiso
divertirse con un juego, lo prim ero que tuvo que hacer fue poner en m archa el
juego. T anto el procesador de textos com o el ju eg o son program as de ordenador.
LENGUAJES DE PROGRAMACIÓN
Un program a tiene que escribirse en un lenguaje entendible p o r el ordenador.
D esde el punto de vista físico, un ordenador es una m áquina electrónica. Los ele
m entos físicos (m em oria, unidad central de proceso, etc.) de que dispone el orde
nador para representar los datos son de tipo binario; esto es, cada elem ento puede
diferenciar d o s estados (dos niveles de voltaje). C ada estado se denom ina genéri
cam ente b it y se sim boliza por 0 ó / . P or lo tanto, para representar y m anipular
inform ación num érica, alfabética y alfanum érica se em plean cadenas de bits. S e
gún esto, se denom ina byte a la cantidad de inform ación em pleada por un ordena
d o r para representar un carácter; generalm ente un byte es una cadena de ocho bits.
i í S
L
A sí, por ejem plo, cuando un program a le dice al ordenador que visualice un
m ensaje sobre el m onitor, o que lo im prim a sobre la im presora, las instrucciones
correspondientes para llevar a cabo esta acción, para que puedan ser entendióles
por el ordenador, tienen que estar alm acenadas en la m em oria com o cadenas de
bits. Esto hace pensar que escribir un program a utilizando ceros y unos (lenguaje
m áquina), llevaría m ucho tiem po y con m uchas posibilidades de com eter errores.
Por este m otivo, se desarrollaron los lenguajes ensam bladores.
zar. Un código nem otécnico es una palabra o abreviatura fácil de recordar que re
presenta una tarea que debe realizar el procesador del ordenador. Por ejem plo:
printft "hola" ):
Compiladores
Para traducir un program a escrito en un lenguaje de alto nivel (program a fuente) a
lenguaje m áquina se utiliza un program a llam ado com pilador. Este program a to
m ará com o datos nuestro program a escrito en lenguaje de alto nivel y dará com o
resultado el m ism o program a pero escrito en lenguaje m áquina, program a que ya
puede ejecutar directa o indirectam ente el ordenador.
Por ejem plo, un program a escrito en el lenguaje C necesita del com pilador C
para poder ser traducido. Posteriorm ente el program a traducido podrá ser ejecuta
do directam ente p o r el ordenador. E n cam bio, para traducir un program a escrito
en el lenguaje Java necesita del com pilador Java; en este caso, el lenguaje m áqui
na no corresponde al del ordenador sino al de una m áquina ficticia, denom inada
m áquina virtual Java, que será puesta en m archa por el ordenador para ejecutar el
program a.
Intérpretes
A diferencia de un com pilador, un intérprete no genera un program a escrito en
lenguaje m áquina a partir del program a fuente, sino que efectúa la traducción y
ejecución sim ultáneam ente para cada una de las sentencias del program a. Por
ejem plo, un program a escrito en el lenguaje B asic necesita el intérprete B asic para
ser ejecutado. D urante la ejecución de cada una de las sentencias del program a,
ocurre sim ultáneam ente la traducción.
¿QUÉ ES JAVA?
Java es un lenguaje de program ación de alto nivel con el que se pueden escribir
tanto program as convencionales com o para Internet.
Según lo expuesto, Java incluye dos elem entos: un com pilador y un intérpre
te. El com pilador produce un código de bytes que se alm acena en un fichero para
ser ejecutado por el intérprete Java denom inado m áquina virtual de Java.
¿P or qué no se diseñó Java para que fuera un intérprete m ás entre los que hay
en el m ercado? La respuesta es porque la interpretación, si bien es cierto que pro
porciona independencia de la m áquina, conlleva tam bién un problem a grave, y es
la pérdida de velocidad en la ejecución del program a. Por esta razón la solución
fue d iseñar un com pilador que produjera un lenguaje que pudiera ser interpretado
a velocidades, si no iguales, sí cercanas a la de los program as nativos (program as
en código m áquina propio de cada ordenador), logro conseguido m ediante la m á
quina virtual de Java.
8 JA V A , C U R SO D E PROGRAM A CIÓN
C on todo, las aplicaciones todavía adolecen de una falta d e rendim iento apre-
ciable. Este es uno de los problem as que siem pre se ha achacado a Java. A fortu
nadam ente, la diferencia de rendim iento con respecto a aplicaciones equivalentes
escritas en código m áquina nativo ha ido dism inuyendo hasta m árgenes m uy re
ducidos gracias a la utilización de com piladores JIT (Just In Tim e - com pilación
al instante).
Un com pilador JIT interacciona con la m áquina virtual para convertir el códi
go de bytes en código m áquina nativo. C om o consecuencia, se m ejora la veloci
dad durante la ejecución. Sun sigue trabajando sobre este objetivo y prueba de
ello son los resultados que se están obteniendo con el nuevo y potente m otor de
ejecución H otSpot (H otSpot P erfom ance E ngine) que ha diseñado, o por los m i
croprocesadores específicos p ara la interpretación hardw are d e código de bytes.
HISTORIA DE JAVA
El lenguaje de program ación Java fue desarrollado por Sun M icrosystem s en
1991. N ace com o parte de un proyecto de investigación para desarrollar softw are
para com unicación entre aparatos electrónicos de consum o com o vídeos, televiso
res, equipos de m úsica, etc. D urante la fase de investigación surgió un problem a
que dificultaba enorm em ente el proyecto iniciado: cada aparato tenía un m icro-
procesador diferente y m uy poco espacio de m em oria; esto provocó un cam bio en
el rum bo de la investigación que desem bocó en la idea de escribir un nuevo len
guaje de program ación independiente del dispositivo que fue bautizado inicial-
m ente com o Oak.
Java está fundam entado en C++. Q uiere esto d ecir que m ucha de la sintaxis y
diseño orientado a objetos se tom ó de este lenguaje. P or lo tanto, a los lectores
que estén fam iliarizados con C++ y la PO O les será m uy fácil aprender a d esa rro
llar aplicaciones con Java. Q uiero advertir a este tipo de potenciales usuarios de
Java que en este lenguaje no existen punteros ni aritm ética de punteros, las cade
nas de caracteres son objetos y la adm inistración de m em oria es autom ática, lo
que elim ina la problem ática que presenta C++ con las lagunas de m em oria al o l
vidar liberar bloques de la m ism a que fueron asignados dinám icam ente.
E n to rn o d e d es a rro llo
de Java
1. E d ita r el p ro g ra m a
2 . C o m p ilarlo
3. E je c u ta rlo
4 . D e p u ra rlo
A sí m ism o, el C D que acom paña al libro incluye Java 2 SD K versión 1.3 para
W indow s 9x, W indow s 20(X)/NT, y Unix. Se trata del nuevo JD K 1.3. En cual
quier caso, en Internet se encuentran todas las versiones para W indow s, M acin
tosh y Unix (Solaris y otros). Vea tam bién el apéndice C.
□ ¡dk1.3
G bin
GE G demo
5) G d o cs
E G include
B G include old
E G i»e
G l>b
• La carpeta bin contiene las herram ientas de desarrollo. Esto es, los program as
para com pilar (javac), ejecutar (Java), depurar jd b ) , y docum entar (Javadoc)
los program as escritos en el lenguaje de program ación Java, y otras herra
m ientas com o appletview er para ejecutar y depurar applets sin tener que utili
zar un navegador, j a r para m anipular ficheros .ja r (un fichero .ja r es una
colección de clases Java y otros ficheros em paquetados en uno solo), ja va h
que es un fichero de cabecera para escribir m étodos nativos, ja v a p para d es
com pilar ficheros com pilados y extcheck para detectar conflictos ja r.
C A PÍTU LO I: FASES EN EL D ESA RRO LLO DE UN PROGRAM A 11
• La carpeta irtclude contiene los ficheros de cabecera que dan soporte para
añadir a un program a Java código nativo (código escrito en un lenguaje dis
tinto de Java, por ejem plo Ada o C++).
• L a carpeta include-old contiene los ficheros de cabecera que dan soporte para
añadir a un program a Java código nativo utilizando interfaces antiguas.
• El nuevo JD K 1.3 incluye un com pilador JIT para ejecutar el código en Java.
Por om isión el com pilador JIT em pleado será jre\bin\sym jit.dll.
Sólo falta un editor de código fuente Java. Es suficiente con un editor de texto
sin form ato; por ejem plo el bloc de notas de W indow s. No obstante, todo el tra
bajo de edición, com pilación, ejecución y depuración, se hará m ucho m ás fácil si
se utiliza un entorno de desarrollo con interfaz gráfica de usuario que integre las
herram ientas m encionadas, en lugar de tener que u tilizar las interfaz de línea de
órdenes del JD K , com o verem os a continuación.
E ntornos de desarrollo integrados para Java hay m uchos: Forte de Sun, Visual
Cafe' de Sym antec, JB uilder de B orland, K awa de T ek-T ools, Visual A g e W in
dow s de IBM , pcG R A SP de A ubum U niversity, Visual J++ de M icrosoft, etc. Las
versiones de dem ostración operativas de algunos de ellos las tiene en el C D que se
adjunta con este libro. C oncretam ente, pcG R A SP es un entorno integrado sencillo,
que no requiere licencia, y que se ajusta a las necesidades de las aplicaciones que
serán expuestas en este libro.
E sta sencilla aplicación la realizarem os desde los dos puntos de vista com en
tados anteriorm ente: utilizando la interfaz de línea de órdenes del JD K y utilizan
do un entorno de desarrollo integrado.
class HolaMundo
I
/*
* P u n t o de e n t r a d a a la aplicación.
*
* a r g s : m a t r i z de p a r á m e t r o s p a s a d o s a l a a p l i c a c i ó n
* m e d i a n t e l a l i n e a de ó r d e n e s . Puede e s t a r v a c i a .
*/
public static void m a in (S trin g [] args)
I
System .out.println("H ola mundo!! ! " ) ;
La prim era línea declara la clase de objetos H olaM undo, porque el esqueleto
de cualquier aplicación Java se basa en la definición de una clase. A continuación
se escribe el cuerpo de la clase encerrado entre los caracteres { y }. A m bos ca
racteres definen un bloque de código. T odas las acciones que va a llevar a cabo un
program a Java se colocan dentro del bloque de código correspondiente a su clase.
Las clases son la base d e los program as Java. A prenderem os m ucho sobre ellas en
los próxim os capítulos.
C A P ÍT U L O I: FASES EN EL D ESA RRO LLO DE UN PROGRAM A 13
Las siguientes líneas encerradas entre /* y */ son sim plem ente un com entario.
Los com entarios no son tenidos en cuenta p o r el com pilador, pero ayudan a en
tender un program a cuando se lee.
El program a editado está ahora en la m em oria. Para que este trabajo pueda tener
continuidad, el program a escrito se debe grabar en el disco utilizando la orden co
rrespondiente del editor. M uy im portante: el nom bre del program a fuente debe ser
el m ism o que el de la clase que contiene, respetando m ayúsculas y m inúsculas. En
nuestro caso, el nom bre de la clase es H olaM undo, por lo tanto el fichero debe
guardarse con el nom bre H olaM undo.java.
El siguiente paso es com pilar el program a; esto es, traducir el program a fuente a
código de bytes para posteriorm ente poder ejecutarlo. La orden para com pilar el
program a H olaM undo.java es la siguiente:
javac HolaMundo.java
pa t h = %p a t h % : c : \ j d k l . 3 \ b i n
-M S-D O S P I C T E S ||
l A □ ! leí tal ® ls Al
M i c r o s o f t ( R ) Windows 98
( C ) C o p y r iq lit M i c r o s o f t C o rp 1 * 8 1 - 1 9 » » .
C :\ W IN D B M S > p < l h - # p * t M ; e : \ j « * « \ j i l k 1 . 3 \ b i n
G :\W IH II 0WS>cd C : \ C a p 0 1 \ H o Ia M u n d o
C : \ C a p 0 1 \ H o l. iH u n d o > d ir
E l v o l u n e n de la u n i d a d C na t i m a e t i q u e t a
E l m in e r o de s e r i e d e l u o t u n e n e s 2 F 1 F - 0 7 F 8
D i r e c t o r i o de C : \ C a p 0 1 V H o ia M u n d o
<111R> 05/06/011 2 7 : 2 8 .
< 0 IR > D 5/06/ D0 2 2 : 2 8 ..
H 0 IA M I T 1 J8U 299 05/06/00 0 :0 0 H o loH undo.jaoa
1 a rch ivas 29» b ytes
2 d ire cto rio s 1.530.392.576 bytes lib r e s
C : \ C a p 0 1 \ H o laMimdo>
O bsérvese que para com pilar un program a hay que especificar la extensión
.java. El resultado de la com pilación será un fichero H olaM undo.class que con
tiene el código de bytes que ejecutará la m áquina virtual de Java.
1 -M S-D O S B R E J|
1 A iji° j J í-.ilN fe l ESI f f l s a |
<0 IR >
05/06/00 22: ? :
1
< 0 R>
05/06/00 2 2 :2 8 ..
II0L8MU-1 JflU 22? 05/06/00 0 : 0 0 N o lo M u n d o . jo mo
1 o rc h iu o * 2 2 ? bytes
2 d ire cto rio s 1 .538.32 2.5 /6 bytes l i b r e *
< D IR >
05/ U6/ D 0 2 2 : 2 8 .
<111R>
05/116/011 2 2 : 2 8 ..
H0L«MU*1 JflU 222 05/06/00 0:00 H o lo M und o.jo *o
II0LBMU-1 CLfl 625 05/06/00 22:60 H o lo M u n d o .e lo ss
2 orc h iu o s 726 b y t e s
2 d ire c to rio s 1. 5 2 1 1 .7 6 6 . 2 / 6 b y t e s l i b r e s
Biblioteca de funciones
S y s t e m . o u t . p r i n t l n ( ”H o l a m u n d o ! ! ! " ) ;
Com o hem os visto, cada vez que se realiza el proceso de com pilación del progra
ma actual. Java genera autom áticam ente sobre el disco un fichero .class. Este fi
chero puede ser ejecutado directam ente desde el sistem a operativo, con el soporte
de la m áquina virtual de Java, que se lanza invocando a la utilidad ja v a con el
nom bre del fichero com o argum ento.
1 6 JA VA : C U R SO D E PROGRAM A CIÓN
Hay otro tipo de errores que no dan lugar a m ensaje alguno. P or ejem plo: un
program a que no term ine nunca de ejecutarse, debido a que presenta un lazo,
donde no se llega a dar la condición de term inación. Para detener la ejecución se
tienen que pu lsar las teclas C trl+ C (en un entorno integrado se ejecutará una or
den equivalente a D etener ejecución).
Depurar un programa
Una vez ejecutado el program a, la solución puede ser incorrecta. E ste caso exige
un análisis m inucioso de cóm o se com porta el program a a lo largo de su ejecu
ción; esto es, hay que entrar en la fase de depuración del program a.
Para depurar un program a Java debe com pilarlo con la opción -g. Por ejem
plo, desde la línea de órdenes esto se haría así:
javac -g A r i t m e t i c a . j ava
D M B 1 J J P J M JjJK J £ |= l* l * 1 * 1 * 1 ± M * I J a i
| G o n o M e C SD 1 Rw noveC SD I Fort Seo: — |--------------------- |10
c la a a HolaMundo
í
• P u n c o d e e n t r a d o a l o apli c a c i ó n .
* orgs: m a t r i z d e p a r á m e t r o s p o s a d o s o l a a p l i c a c i ó n
• m e d i a n t e la l i n e a d e órd e n e s . P u e d e e s t a r vacia.
'• i i
i---------------------------------------------------------------------------------------------------------------------
—
j a v a c C : \ C a p 0 1 \ H o la M iin d c i\ H o la M u n d o . j a v a
| P fo r.e sG F i n is h e d
Para editar y ejecutar la aplicación H olaM undo anterior utilizando este entor
no de desarrollo integrado, los pasos a seguir se indican a continuación:
En capítulos posteriores, im plem entará aplicaciones com puestas por varios fi
cheros fuente. En este caso, los pasos a seguir son los siguientes:
3. Finalm ente, suponiendo que la com pilación se efectuó satisfactoriam ente, eje
cutarem os la aplicación seleccionando la orden R un C aptured, o bien Run, del
menú R un. Este paso exige que se esté visualizando el fichero que contiene la
clase aplicación.
C uando una aplicación consta de varios ficheros puede resultar m ás cóm odo
para su m antenim iento crear un proyecto. Esto perm itirá presentar una ventana
que m ostrará una entrada por cada uno de los ficheros fuente que com ponen la
aplicación. Para visualizar el código de cualquiera de estos ficheros, bastará con
hacer doble clic sobre la entrada correspondiente.
O M B I - P I P I E I - 5 1 » ! i = | . = 1k | A l M l x l j U m i H r l m
... i — .. .. . — .—— i~ i .... — . — i
p u b l i c c l a s e C A p llc n c io n
( F i le s
p u b lic s ta tic v o i d ro o ln (Strlng[] args)
< 6 CApicacicri
/ / P u n t o de e n t r a d o a l a a p l i c a c i ó n CApIcaoon «va
C R o c io n a l r 1 , r 2 ; CRacional java
rl = n e v C R a c l o n a l (); // crear un o b j e t o CRaclonal
r 1 . A s i g n a r D a t o s (2, 5);
r 2 = rl;
r 1 . A s i g n a r D a t o s (3, 7 );
r l. V is u a liz a r R a c io n a l(); // ae visualiza 3/7
r 2 .V l s u a l l z a r R a c i o n a l ( ) ; // ae visualiza 3/7
SL
P r o c e s e F l n is h e d
3. El paso siguiente es añadir al proyecto el resto de los ficheros que lo com po
nen. Para ello se ejecuta la orden Edil P roject del m enú P roject, y a través del
botón A d d Files se añaden dichos ficheros.
4. Finalm ente, para com pilar y ejecutar la aplicación, se procede com o se expli
có anteriorm ente.
EJERCICIOS RESUELTOS
Para practicar con un program a m ás, escriba el siguiente ejem plo y pruebe los re
sultados. Hágalo prim ero desde la línea de órdenes y después con el entorno de
desarrollo integrado preferido por usted. El siguiente ejem plo visualiza com o re
sultado la sum a, la resta, la m ultiplicación y la división de dos cantidades enteras.
Edición
A bra el procesador de textos o el editor de su entorno integrado y edite el progra
m a ejem plo q ue se m uestra a continuación. R ecuerde, el nom bre del fichero
fuente tiene que coincidir con el nom bre de la clase, C Aritm etica, respetando m a
yúsculas y m inúsculas, y debe tener extensión .java.
d a t o l = 20;
d a t o 2 = 10:
20 JA VA : C U R SO DE PROGRAM A CIÓN
// Suma
re su lta d o = datol + dato2;
System .out.printlnídatol + " + " + dato2 + " = " + r e s u lta d o ):
// R e s t a
re su lta d o = datol - dato2;
System .out.p rin t ln t d a t o l + " - " + dato2 + " = " + r e s u lta d o ):
// P r o d u c t o
re su lta d o = datol * dato2;
System .out.p rin tln fd a to l + ” * " + dato2 + " = " + r e s u l t a d o ) :
II C o c i e n t e
r e s u lt a d o = datol / dato2:
System, out. p r i n t l n t d a t o l + " / " + d a t o 2 + •' = " + r e s u l t a d o ) :
U na vez editado el program a, guárdelo en el disco con el nom bre C Aritm eti-
ca.java.
d a t o l = 20:
d a t o 2 = 10:
20 i 10 = 30
O bserve que la expresión resultante está form ada por cinco elem entos: d a to l,
" + ", dato2, " = ", y resultado. U nos elem entos son num éricos y otros son cons-
CA PÍTU LO 1: FASES EN EL D ESA RRO LLO DE UN PRO G R A M A 2 1
tantes de caracteres. Para unir los cinco elem entos en uno solo, se ha em pleado el
operador +.
EJERCICIOS PROPUESTOS
Practique la edición, la com pilación y la ejecución con un program a sim ilar al
program a A ritm e tic a ja v a realizado en el apartado anterior. Por ejem plo, m odifí-
quelo para que ahora realice las operaciones de sum ar, restar y m ultiplicar con
tres datos: d a to /, dato2. dato3. En un segundo intento, puede tam bién com binar
las operaciones aritm éticas.
CA PÍTU LO 2
<S>F.J.Ceballos/KA-MA
PROGRAMACIÓN ORIENTADA A
OBJETOS
La program ación orientada a objetos (PO O ) es un m odelo de program ación que
utiliza objetos, ligados m ediante m ensajes, para la solución de problem as. Puede
considerarse com o una extensión natural de la program ación estructurada en un
intento de potenciar los conceptos de m odularidad y reutilización del código.
¿A qué objetos nos referim os? Si nos param os a pensar en un determ inado
problem a que intentam os resolver podrem os identificar entidades de interés, las
cuales pueden ser objetos potenciales que poseen un conjunto de propiedades o
atributos, y un conjunto de m étodos m ediante los cuales m uestran su com porta
m iento. Y no sólo eso, tam bién podrem os ver, a poco que nos fijem os, un con
ju n to de interrelaciones entre ellos conducidas p o r m ensajes a los que responden
m ediante m étodos.
cuentaOl.Transferencia(cuenta02);
Objetos
Un program a orientado a objetos se com pone solam ente de objetos, entendiendo
por objeto u na encapsulación genérica de datos y de los m étodos para m anipular
los. D icho de otra form a, un objeto es una entidad que tiene unos atributos parti
culares. las propiedades, y unas form as de operar sobre ellos, los métodos.
Por ejem plo, una ventana de una aplicación W indow s es un objeto. El color
de fondo, la anchura, la altura, etc. son propiedades. Las rutinas, lógicam ente
transparentes al usuario, que perm iten m axim izar la ventana, m inim izarla, etc. son
m étodos.
Mensajes
C uando se ejecuta un program a orientado a objetos, los objetos están recibiendo,
interpretando y respondiendo a m ensajes de otros objetos. Esto m arca una clara
diferencia con respecto a los elem entos de datos pasivos de los sistem as tradicio
nales. En la POO un m ensaje está asociado con un m étodo, de tal form a que
cuando un objeto recibe un m ensaje la respuesta a ese m ensaje es ejecutar el m é
todo asociado.
P o r ejem plo, cuando un usuario quiere m axim izar una ventana de una aplica
ción W indow s, lo que hace sim plem ente es pulsar el botón de la m ism a que reali
za esa acción. Eso, provoca que W indow s envíe un m ensaje a la ventana para
indicar que tiene que m axim izarse. C om o respuesta a este m ensaje se ejecutará el
m étodo program ado para ese fin.
Métodos
U n m étodo se im plem enta en una clase de objetos y determ ina cóm o tiene que
actuar el objeto cuando recibe el m ensaje vinculado con ese m étodo. A su vez, un
m étodo puede tam bién enviar m ensajes a otros objetos solicitando una acción o
inform ación.
o bjeto
m e n s a je s d ato s
m é to d o s
Clases
U na clase es un tipo de objetos definido por el usuario. U na clase equivale a la
generalización de un tipo específico de objetos. Por ejem plo, piense en un m olde
para hacer flanes; el m olde es la clase y los flanes los objetos.
CCuenta c l i e n t e O I = new C C u e n t a O ; // n u e v a c u e n t a
A fortunadam ente no tendrá que escribir todas las clases que necesite en su
program a, porque Java proporciona una biblioteca de clases estándar para realizar
las operaciones m ás habituales que podam os requerir. Por ejem plo, en el capítulo
anterior, vim os que la clase S y stem tenía un atributo o u t que era un objeto de la
clase P rin tS tre a m que, de form a predeterm inada, está ligado al dispositivo de
salida (a la pantalla). A su vez, la clase P rin tS tre a m proporciona un m étodo de
nom inado p r in tln que acepta com o argum ento una cadena de caracteres. De esta
form a, cuando el objeto o u t reciba el m ensaje p rin tln , responderá ejecutando este
m étodo, que envía la cadena pasada com o argum ento al dispositivo de salida.
class COrdenador
I
// ...
I
O bservam os que para declarar una clase hay que utilizar la palabra reservada
class seguida del nom bre de la clase y del cuerpo de la m ism a. El cuerpo de la
clase incluirá entre { y } los atributos y los m étodos u operaciones que definen su
com portam iento.
Los atributos tam bién pueden incluir inform ación sobre el estado del objeto;
por ejem plo, en el caso de un ordenador, si está encendido o apagado, si la pre
sentación en pantalla está activa o inactiva, etc.
C A P ÍT U L O 2: PR O G R A M A C IÓ N O R IE N TA D A A O BJETO S 2 7
class COrdenador
1
S t r i n g Marca:
S t r in g Procesador:
Strin g Pantalla:
boolean OrdenadorEncendido;
boolean P re se n ta c ió n :
II...
I
O bserve que se han definido cinco atributos: tres de ellos, M arca, Procesador
y P antalla, pueden contener una cadena de caracteres (una cadena de caracteres
es un objeto de la clase S trin g perteneciente a la biblioteca estándar). Los otros
dos atributos, O rdenadorE ncendido y P resentación , son de tipo b o o lean (un atri
buto de tipo b o o lean puede contener un valor tr u e o false; verdadero o falso).
Debe respetar las m ayúsculas y las m inúsculas.
El com portam iento define las acciones que el objeto puede em prender. Por
ejem plo, pensando acerca de un objeto de la clase C O rdenador, esto es, de un o r
denador, algunas acciones que éste puede hacer son:
0 Ponerse en m archa
0 A pagarse
0 D esactivar la presentación en la pantalla
0 A ctivar la presentación en la pantalla
0 C argar una aplicación
Para definir este com portam iento hay que crear m étodos. Los m étodos son
rutinas de código definidas dentro de la clase, que se ejecutan en respuesta a algu
na acción tom ada desde dentro de un objeto de esa clase o desde otro objeto de la
m ism a o de otra clase. R ecuerde que los objetos se com unican m ediante m ensajes.
void EncenderOrdenadort)
I
if ( O r d e n a d o r E n c e n d i d o — t r u e ) // s i e s t á encendido...
S y s t e m . o u t . p r i n t l n ( " E l orden ador ya e s t á en m a r c h a . " ) :
e l s e // s i no e s t á e n c e n d i d o , e n c e n d e r l o .
I
OrdenadorEncendido = true:
S y s t e m . o u t . p r i n t l n t " E l o r d e n a d o r s e ha e n c e n d i d o . " ) :
void EstadoO
I
S y s t e m . o u t . p r i n t l n t " \ n E s t a d o del o r d e n a d o r : " +
" \ n M a r c a " + Marca +
"\nProcesador " + Procesador +
“\ n P a n t a lla " + P a n ta lla + " \ n " ) ;
i f (OrdenadorEncendido = t r u e ) // s i el o r d e n a d o r e s t á e n c e n d i d o . . .
S y s t e m . o u t . p r i n t l n ( " E l o r d e n a d o r e s t á e n c e n d i d o . ” ):
e l s e // s i no e s t á e n c e n d i d o . . .
S ystem .o ut.p r i n t l n ( " E l ordenador está apagado."):
I
En este instante, si nuestras pretensiones sólo son las expuestas hasta ahora,
ya tenem os creada la clase C O rdenador. Para poder crear objetos de esta clase y
trabajar con ellos, tendrem os que escribir un program a, o bien añadir a esta clase
CA PÍTU LO 2: PRO G R A M A CIÓ N O R IEN TA D A A O BJETO S 2 9
el m étodo m ain. Siem pre que se trate de una aplicación (no de un applet) es obli
gatorio que la clase que define el com ienzo de la m ism a incluya un m étodo m ain.
C uando se ejecuta una clase Java com pilada que incluye un m étodo m ain . éste es
lo prim ero que se ejecuta.
c l a s s COrdenador
I
S t r i n g Marca:
S t r in g Procesador;
Strin g Pantalla;
boolean OrdenadorEncendido;
boolean P r e s e n t a c ió n ;
void EncenderOrdenadorí)
I
if ( O r d e n a d o r E n c e n d i d o = = t r u e ) // s i e s t á e n c e n d i d o . . .
S y s t e m . o u t . p r i n t l n C E l o r d e n a d o r ya e s t á e n c e n d i d o . " ) ;
e l s e // s i no e s t á e n c e n d i d o , e n c e n d e r l o .
I
OrdenadorEncendido = true;
S y s t e m . o u t . p r i n t l n t " E l o r d e n a d o r s e ha e n c e n d i d o . ” );
void Estadoí)
I
S y s t e m . o u t . p r i n t l n ( " \ n E s t a d o del o r d e n a d o r : ” +
”\nMarca ” + Marca +
"XnProcesador ” + Procesador +
” \ n P a n t a l l a ” + P a n t a l l a + ” \ n “ );
i f ( O r d e n a d o r E n c e n d i d o = = t r u e ) // s i el ordenador e s t á . e n c e n d i do . ..
S y stem .o ut.p r i n t l n ( " E l ordenador está e n ce n d id o.");
e l s e // s i no e s t á e n c e n d i d o . . .
S y stem .o ut.p r i n t l n ( " E l ordenador está apagado."):
I
• L a prim era línea crea un objeto de la clase C O rdenador y alm acena una refe
rencia al m ism o en la variable M iO rdenador. Esta variable la utilizarem os pa
ra acceder al objeto en las siguientes líneas. A hora quizás em piece a entender
p o r qué anteriorm ente decíam os que un program a orientado a objetos se com
pone solam ente de objetos.
• Las tres líneas siguientes establecen los atributos del objeto referenciado por
M iO rdenador. Se puede observar que para acceder a los atributos o propieda
des del objeto se utiliza el operador punto (.). De esta form a quedan elim ina
das las am bigüedades que surgirían si hubiéram os creado m ás de un objeto.
• En las dos últim as líneas el objeto recibe los m ensajes E ncenderO rdenador y
E stado. La respuesta a esos m ensajes es la ejecución de los m étodos respecti
vos, que fueron explicados anteriorm ente. A quí tam bién se puede observar
que para acceder a los m étodos del objeto se utiliza el operador punto.
nom bre_objeto, n o m b r e jn ie m b ro
El o r d e n a d o r s e ha e n c e n d i d o .
E s t a d o del o rd e n a d o r :
M ar c a A s t
P ro c e s a d o r I n t e l Pentium
P a n t a l l a TFT
Otra form a de crear objetos de una clase y trabajar con ellos es incluir esa cla
se en el m ism o fichero fuente de una clase aplicación, entendiendo por clase apli
cación una que incluya el m étodo m a in y cree objetos de otras clases. Por
ejem plo, volvam os al instante ju sto antes de añadir el m étodo m a in a la clase
C O rdenador y añadam os una nueva clase pública denom inada C M iO rdenador
que incluya el m étodo m a in . El resultado tendrá el esqueleto que se observa a
continuación:
CA PÍTU LO 2: PR O G R A M A C IÓ N O R IEN TA D A A O BJETO S 3 1
class COrdenador
I
// ...
I
En el capítulo anterior aprendim os que una aplicación está basada en una cla
se cuyo nom bre debe coincidir con el del program a fuente que la contenga, res
petando m ayúsculas y m inúsculas. Por lo tanto, guardem os el código escrito en un
fichero fuente denom inado C M iO rdenador.java. F inalm ente, com pletam os el c ó
digo com o se observa a continuación, y com pilam os y ejecutam os la aplicación.
A hora es la clase C M iO rdenador la que crea un objeto de la clase C Ordenador.
El resto del proceso se desarrolla com o se explicó en la versión anterior. Lógica
m ente, los resultados que se obtengan serán los m ism os que obtuvim os con la ver
sión anterior.
p u b l i c c l a s s CMiOrdenador
1
p u b l i c s t a t i c v o i d main (StringH args)
C O r d e n a d o r M i O r d e n a d o r = new C O r d e n a d o r ( ) ;
MiOrdenador.Marca = " A s t " ;
M iO rdenador.Procesador = " I n t e l Pentium";
M iO rde na do r. P a n t a l1a = "TFT” ;
M i O r d e n a d o r . E n c e n d e r O r d e n a d o r t );
M i O r d e n a d o r . E s t a d o ( ):
1
1
class COrdenador
I
S t r i n g Marca;
S t r in g Procesador;
Strin g Pantalla;
boolean OrdenadorEncendido:
boolean P r e s e n t a c ió n ;
void EncenderOrdenadort)
{
if ( O r d e n a d o r E n c e n d i d o = = t r u e ) // s i e s t á encendido...
S y s t e m . o u t . p r i n t l n ( " E l o rd e n a d o r ya e s t á encendido."):
32 JA VA : C U R SO D E PROGRAM A CIÓN
voi d E s t a d o t )
I
S y s t e m . o u t . p r i n t l n t " \ n E s t a d o del o r d e n a d o r : " +
" \ n M a r c a ” + Marca +
"\nProcesador " + Procesador +
” \ n P a n t a 11 a " + P a n t a l l a + " \ n " ) :
i f ( O r d e n a d o r E n c e n d i d o — t r u e ) II s i el ordenador es t á encendido...
S y s t e m . o u t . p r i n t l n t " E l o r d e n a d o r e s t é e n c e n d i d o . ” ):
e l s e // s i n o e s t á e n c e n d i d o . . .
S y s t e m . o u t . p r i n t l n C E l ordenador está apagado."):
Según lo expuesto hasta ahora, en esta nueva versión tam bién tenem os un fi
chero, el que alm acena la aplicación, que tiene el m ism o nom bre que la clase que
incluye el m étodo m a in , que es por donde se em pezará a ejecutar la aplicación.
CARACTERÍSTICAS DE LA POO
Las características fundam entales de la PO O son: abstracción, encapsulam iento,
herencia y polim orfism o.
Abstracción
Por m edio de la abstracción conseguim os no detenernos en los detalles concretos
de las cosas que no interesen en cada m om ento, sino generalizar y centrarse en los
aspectos que perm itan tener una visión global del problem a. Por ejem plo, el estu
dio de un ordenador podem os realizarlo a nivel de funcionam iento d e sus circuitos
electrónicos, en térm inos de corriente, tensión, etc., o a nivel de transferencia e n
tre registros, centrándose así el estudio en el flujo de inform ación entre las unida
des que lo com ponen (m em oria, unidad aritm ética, unidad de control, registros.
CA PÍTU LO 2: PRO G R A M A CIÓ N O R IEN TA D A A OBJETOS 3 3
etc.), sin im portam os el com portam iento de los circuitos electrónicos que com po
nen estas unidades.
Encapsulamiento
Esta característica perm ite ver un objeto com o una caja negra en la que se ha in
troducido de alguna m anera toda la inform ación relacionada con dicho objeto.
Esto nos perm itirá m anipular los objetos com o unidades básicas, perm aneciendo
oculta su estructura interna.
Herencia
La herencia perm ite el acceso autom ático a la inform ación contenida en otras cla
ses. D e esta forma, la reutilización del código está garantizada. C on la herencia
todas las clases están clasificadas en una jerarquía estricta. C ada clase tiene su su-
perclase (la clase superior en la jerarquía), y cada clase puede tener una o m ás
subclases (las clases inferiores en la jerarquía).
j j
C la s e C la s e
C O rd e n a d o rS o b re m e s a C O rd e n a d o rP o rtá til
Las clases que están en la p aite inferior en la jerarquía se dice que heredan de
las clases que están en la parte superior en la jerarquía.
El térm ino heredar significa que las subclases disponen de todos los m étodos
y propiedades de su superclase. Este m ecanism o proporciona una form a rápida y
cóm oda de extender la funcionalidad de una clase.
En Jav a cada clase sólo puede tener una superclase, lo que se denom ina he
rencia sim ple. En otros lenguajes orientados a objetos, com o C++, las clases pue-
34 JA V A: CU R SO DE PROGRAM A CIÓN
U na interfaz es una colección de nom bres de m étodos, sin incluir sus defini
ciones, q ue puede ser añadida a cualquier clase para proporcionarla com porta
m ientos adicionales no incluidos en los m étodos propios o heredados.
Polimorfismo
Esta característica perm ite im plem entar m últiples form as de un m ism o m étodo,
dependiendo cada una de ellas de la clase sobre la que se realice la im plem enta-
ción. Esto hace que se pueda acceder a una variedad de m étodos distintos (todos
con el m ism o nom bre) utilizando exactam ente el m ism o m edio de acceso. M ás
adelante, cuando estudie en profundidad las clases y subclases, estará en condi
ciones de entender con claridad la utilidad de esta característica.
CONSTRUCTORES Y DESTRUCTORES
U n constructor es un procedim iento especial de una clase que es llam ado auto
m áticam ente siem pre que se crea un objeto de esa clase. Su función es iniciar el
objeto.
EJERCICIOS RESUELTOS
Para practicar con una aplicación m ás, escriba el siguiente ejem plo y pruebe los
resultados. H ágalo prim ero desde la línea d e órdenes y después con el entorno de
desarrollo integrado preferido por usted. El siguiente ejem plo m uestra una clase
para representar núm eros racionales. E sta clase puede ser útil porque m uchos nú
m eros no pueden ser representados exactam ente utilizando un núm ero fracciona
rio. Por ejem plo, el núm ero racional 1/3 representado com o un núm ero
CA PÍTU LO 2: PRO G R A M A CIÓ N O R IEN TA D A A O BJETO S 3 5
Edición
Abra el procesador de textos o el editor de su entorno integrado y edite la aplica
ción propuesta, com o se m uestra a continuación:
class CRacional
I
int Numerador:
int Denominador:
void V i s u a l i z a r R a c i o n a l ()
I
S y s t e m . o u t . p r i n t l n ( N u m e r a d o r + " / " + Denominador):
I
rl.A s ig n a rD a t o s ( 2 , 5);
rl.V i suali z a rR a c io n a lO ;
CRacional r l = new C R a c i o n a l O :
r l .A s i g n a r D a t o s t 2 . 5 ) :
r l . V i s u a l i z a r R a c i o n a l ( );
EJERCICIOS PROPUESTOS
1. Añada a la aplicación C O rdenador.java el m étodo A pagarO rdenador.
2. Diseñe una clase C Coche que represente coches. Incluya los atributos m a rca ,
m odelo y co lo r; y los m étodos que sim ulen, enviando m ensajes, las acciones de
arrancar el m otor, cam biar de velocidad, acelerar, frenar y parar el motor.
CA PÍTU LO 3
©F.JX'cbalos/RAMA
Los corchetes “ []” indican que la inform ación encerrada entre ellos es opcio
nal, y los puntos suspensivos que pueden aparecer m ás elem entos de la m is
ma forma. Por ejem plo, la sintaxis para definir una constante es:
final static tipo idctel = c t e l [ . i dc te 2 = cte2]...;
CARACTERES DE JAVA
Los caracteres de Java pueden agruparse en letras, dígitos, espacios en blanco, ca
racteres especiales, signos de puntuación y secuencias de escape.
El com pilador Java trata las letras m ayúsculas y m inúsculas com o caracteres
diferentes. P or ejem plo los identificadores A ñ o y año son diferentes.
Espacios en blanco
Los espacios en blanco en exceso son ignorados por el com pilador. P or ejem
plo, el código siguiente se com porta exactam ente igual que el anterior:
Secuencias de escape
C ualquier carácter de los anteriores puede tam bién ser representado p o r una se
cuencia de escape. U na secuencia de escape está form ada p o r el carácter \ seguido
de una letra o de una com binación d e dígitos. Son utilizadas para acciones com o
nueva línea, tabular y para hacer referencia a caracteres no im prim ibles.
S ecuencia A S C II D efinición
TIPOS DE DATOS
R ecuerde las operaciones aritm éticas que realizaba el program a Aritm etica.java
que vim os en un capítulo anterior. Por ejem plo, una de las operaciones que reali
zábam os era la sum a de dos valores:
Para que el com pilador Java reconozca esta operación es necesario especificar
previam ente el tipo de cada uno de los operandos que intervienen en la m ism a, así
com o el tipo del resultado. Para ello, escribirem os una línea co m o la siguiente:
La declaración anterior le dice al com pilador Java que datol, dato2 y resulta
do son de tipo entero (int).
Los tipos de datos en Java se clasifican en: tipos prim itivos y tipos referen-
ciados.
Tipos primitivos
Hay ocho tipos prim itivos de datos que podem os clasificar en: tipos num éricos y
el tipo b o o lean . A su vez, los tipos num éricos se clasifican en tipos enteros y ti
pos reales.
C ada tipo prim itivo tiene un rango diferente de valores positivos y negativos,
excepto el b o o lean que sólo tiene dos valores: tr u e y false. El tipo de datos que
se seleccione para declarar las variables de un determ inado program a dependerá
del rango y tipo de valores que vayan a alm acenar cada una de ellas y de si éstos
son enteros o fraccionarios.
byte
byte b = 0 :
short
short i = 0. j = 0:
int
El tipo in t se utiliza para declarar datos enteros com prendidos entre -2 1 47483648
y +2147483647. Un valor in t se define com o un dato de 32 bits de longitud, in
dependientem ente de la plataform a en la que se ejecute el código byte d e Java. El
siguiente ejem plo declara e inicia tres variables «, b y c, de tipo int:
i n t a = 2000:
i n t b = -30;
int c = 0xF003; / * v a l o r en h e x a d e c i m a l */
long
El tipo long se utiliza para declarar datos enteros com prendidos entre los valores
-9223372036854775808 y + 9223372036854775807. Un valor long se define
como un dato de 64 bits de longitud, independientem ente de la plataform a en la
que se ejecute el código byte de Java. El siguiente ejem plo declara e inicia las va
riables a, b y c, de tipo long:
long c = 0xlF00230F; /* v a lo r en h e x a d e c i m a l */
char
c h a r c a r = 0;
Un carácter es representado internam ente por un entero, que puede ser expre
sado en decim al, hexadecim al u octal, com o verem os m ás adelante.
float
El tipo flo a t se utiliza para declarar un dato en com a flotante de 32 bits en el for
m ato IE EE 754 (este form ato utiliza 1 bit para el signo, 8 bits para el exponente y
24 para la m antisa). Los datos de tipo flo a t alm acenan valores con una precisión
aproxim ada de 7 dígitos. Para especificar que una constante (un literal) es de tipo
float, hay que añadir al final de su valor la letra T o ‘F \ El siguiente ejem plo d e
clara las variables a , b y c , de tipo real de precisión sim ple:
f l o a t a = 3.14159F;
f l o a t b = 2 . 2 e - 5 F : /* 2 . 2 e - 5 = 2 . 2 p o r 10 e l e v a d o a 5 * /
f l o a t c = 2 / 3 F : 7* 0.6666667 */
C A P ÍT U L O 3: ELEM EN TO S DEL L EN G U A JE 4 3
double
boolean
Tipos referenciados
Hay tres clases de tipos referenciados: clases, interfaces y arrays. T odos ellos se
rán objeto de estudio en capítulos posteriores.
LITERALES
Un literal es la expresión de un valor de un tipo prim itivo, de un tipo S trin g
(cadena de caracteres) o la expresión n u il (valor nulo o desconocido). P or ejem
plo, son literales: 5, 3.14, ‘a ’, "h o la " y nuil. En realidad son valores constantes.
Literales enteros
El lenguaje Java perm ite especificar un literal entero en base 10, 8 y 16.
I [ + ] |-1 1i t e r a l _ e n t e r o [ 1 1 | L I ]
U n literal entero decim al puede tener uno o m ás dígitos del 0 a 9, de los cua
les el prim ero de ellos es distinto de 0. Por ejem plo:
Un literal entero octal puede tener uno o m ás dígitos del 0 a 7, precedidos por
0 (cero). Por ejem plo:
Literales reales
Un literal real está form ado p o r una p a rte entera, seguido p o r un pu n to decim al, y
una p a rte fra ccionaria. Tam bién se perm ite la notación científica, en cuyo caso se
añade al valor una e o E, seguida p o r un exponente positivo o negativo.
I [ + ] | - l p a r t e - e n t e r a . p a r t e - f r a c c i o n a r ial I e | E ] I [ + ] | - l e xp on e n t e ]
17.24
17.244283
.008e3
27E-3
U na constante real tiene siem pre tipo d o u b le , a no ser que se añada a la m is
ma u n a / o F, en cu yo caso será de tipo flo at. P or ejem plo:
Tam bién se pueden utilizar los sufijos d o O para especificar explícitam ente
que se trata de una constante de tipo d o u b le . P or ejem plo:
e s p a c i o en b l a n c o
’x ' le tra minúscula x
’\n ' r e t o r n o de c a r r o más a v a n c e d e linea
' \u0007' pitido
'\u 0 0 1 B' c a r á c t e r A S C I I Esc
“E s t o e s una c o n s t a n t e d e c a r a c t e r e s "
"3.1415926"
“P a s e o de P e r e d a 10. S a n t a n d e r "
/ * cadena v a c i a */
"Lenguaje V ' J a v a V " ' /* produce: Lenguaje "Java" */
46 JA V A: C U R SO D E PROGRAM A CIÓN
En el ejem plo siguiente el carácter Vz fuerza a que la cadena "O p u lse Entrar"
se escriba en una nueva línea:
System.out.p rin tln ("D ista n cia : " + distancia + " Km.");
IDENTIFICADORES
Los identificadores son nom bres dados a tipos, literales, variables, clases, interfa
ces, m étodos, paquetes y sentencias de un program a. L a sintaxis para form ar un
identificador es la siguiente:
I l e t r a \ _ \ $ I [ I l e t r a \ d 1 g i t o \ _ \ 11 ] . . .
Las letras pueden ser m ayúsculas o m inúsculas. Para Java una letra m ayús
cula es un carácter diferente a esa m ism a letra en m inúscula. Por ejem plo, los
identificadores Sum a, sum a y S UMA son diferentes.
Suma
Cálculo_Números_Prirnos
íordenar
Vi s u a l i z a r D a t o s
C A PÍTU LO 3: ELEM EN TO S D E L L EN G U A JE 4 7
PALABRAS CLAVE
Las palabras clave son identificadores predefinidos que tienen un significado es
pecial para el com pilador Java. Por lo tanto, un identificador definido por el usua
rio, no puede tener el m ism o nom bre que una palabra clave. El lenguaje Java,
tiene las siguientes palabras clave:
Las palabras clave deben escribirse siem pre en m inúsculas, com o están.
COMENTARIOS
Un com entario es un m ensaje a cualquiera que lea el código fuente. A ñadiendo
com entarios se hace m ás fácil la com prensión de un program a. La finalidad de los
com entarios es explicar el código fuente. Java soporta tres tipos de com entarios:
/*
* La e j e c u c i ó n d e l p r o g r a m a c o m i e n z a c o n e l m é t od o m a í n O .
* La l l a m a d a a l c o n s t r u c t o r de l a c l a s e no t i e n e l u g a r a menos
* q u e s e c r e e un o b j e t o d e l t i p o " CE 1e m e n t o s J a v a '
* en el m ét o d o m a i n ( ).
*/
• C om entario d e una sola línea. E ste tipo de com entario com ienza con una do
ble barra (//) y se extiende hasta el final de la línea. P or ejem plo:
// A g r e g a r aquí el c ó d i g o de i n i c i a c i ó n
• Com entario de docum entación. Este tipo de com entario com ienza con /** y
term ina con */. Son com entarios especiales que ja va d o c utiliza para generar la
48 JA V A: CU R SO DE PROGRAM A CIÓN
docum entación acerca del program a, aunque tam bién se pueden em plear de
m anera idéntica a los com entarios tradicionales.
/* *
* P u n t o de e n t r a d a principal para la aplicación.
*
* Parámetros:
* a r g s : M a t r i z de p a r á m e t r o s p a s a d o s a l a a p l i c a c i ó n
* a t r a v é s de la l i n e a de ó r d e n e s .
*/
class CElementosJava
!
final static i n t c t e l - 1:
final static S tr in g cte2 = " P u l s e un a t e c l a para continuar":
void T e s t ( )
1
final double cte3 - 3.1415926:
// ...
I
II...
I
C om o se observa en el ejem plo anterior, declarar una constante sim bólica su
pone anteponer el calificador final, o bien los calificadores fin a l y static. al tipo y
nom bre de la constante, que será iniciada con el valor deseado. D istinguim os dos
casos: que la constante esté definida en el cuerpo de la clase, fuera de todo m éto
do, com o sucede con d e l y cte2 , o que esté definida dentro de un m étodo, como
sucede con ele3. En el prim er caso, la constante puede estar calificada, adem ás de
con final, con static; en este caso, sólo existirá una copia de la constante para to
dos los objetos que se declaren de esa clase (en nuestro caso, la clase es CEle
m entosJava). Si no se especifica static, cada objeto incluiría su propia copia de la
constante; es claro que esta form a de proceder no parece lógica por tratarse de la
m ism a constante, razón por la que no se hace uso de ella. En el segundo caso no
se puede utilizar static, la constante sólo es visible dentro del m étodo, y sólo
existe durante la ejecución del m ism o; en este caso se dice que la constante es lo
cal al m étodo. U na constante local no pueden ser declarada static.
CA PÍTU LO 3: ELEM ENTOS DEL LENGUAJE 4 9
cte3 - 3.14;
En el ejem plo siguiente se declaran tres variables de tipo short, una variable
de tipo int. y dos variables de tipo S trin g :
class CE1ementosüava
I
short día. mes . año;
void T e s t ()
I
1n t c o n t a d o r - 0;
S t r i n g Nombre = Apellidos -
d1 a = 20:
Apellidos = "Ceballos";
II...
I
II ...
50 JA VA : CU R SO D E PROGRAM ACIÓN
El tipo, prim itivo o referenciado, determ ina los valores que puede tom ar la
variable así com o las operaciones que con ella pueden realizarse. Los operadores
serán expuestos un poco m ás adelante.
Según la definición anterior, las variables día, m es y año son accesibles desde
todos los m étodos no s ta tic de la clase CElem entos Java. E stas variables, declara
das en el bloque de la clase pero fuera de cualquier otro bloque, se denom inan va
riables m iem bro de la clase (atributos de la clase).
En cam bio, las variables contador, N om bre y A pellidos han sido declaradas
en el bloque de código correspondiente al cuerpo del m étodo Test. Por lo tanto,
aplicando la definición anterior, sólo serán accesibles en este bloque. En este caso
se dice que dichas variables son locales al bloque donde han sido declaradas. Una
variable local se crea cuando se ejecuta el bloque donde se declara y se destruye
cuando finaliza la ejecución de dicho bloque; dicho de otra form a, una variable
local se destruye cuando el flujo de ejecución sale fuera del ám bito de la variable.
U na variable local no puede ser declarada static.
class CE1ementosJava
short vari;
void T e s t ( )
1
in t var2;
System .out.p rintln(var2); II e r r o r : v a r i a b l e n o i n i c i a d a
S y s t e m . o u t . p r i n t l n ( v a r i ); // c o r r e c t o : v a r i e s i g u a l a 0
CA PÍTU LO 3: ELEM EN TO S DEL LENGUAJE 5 1
EXPRESIONES NUMÉRICAS
Una expresión es un conjunto de operandos unidos m ediante operadores para es
pecificar una operación determ inada. T odas las expresiones cuando se evalúan
retornan un valor. P o r ejem plo:
a + 1
suma + c
cantidad * p re cio
7 * M ath.sqrt(a) - b / 2 (sqrt indica raíz cuadrada)
// C o n v e r s i ó n i m p l í c i t a
b y t e b D a t o = 1; s h o r t s D a t o = 0 : i n t i D a t o = 0; long I D a t o = 0:
f l o a t f D a t o = 0 : d o u b l e d D a t o = 0:
sDato = bDato:
iDato = sDato;
IDato = iDato:
fDato = 1D a t o :
dDato = f D a t o + I D a t o - i D a t o * s D a t o / bDato;
S y s t e m . o u t . p r i n t l n ( d D a t o ) : // r e s u l t a d o : 1 . 0
Java perm ite una conversión explícita (conversión forzada) del tipo de una
expresión m ediante una construcción denom inada cast, que tiene la forma:
(tipo) expresión
C ualquier valor de un tipo entero o real puede ser convertido a o desde cual
quier tipo num érico. N o se pueden realizar conversiones entre los tipos enteros o
reales y el tipo boolean. P or ejem plo:
52 JA VA : C U R S O DE PROG RAM A CIÓN
// C o n v e r s i ó n e x p l í c i t a ( c a s t )
b y t e b D a t o - 0: s h o r t s D a t o - 0: i n t i D a t o = 0: long I D a t o = 0:
f l o a t f D a t o = 0; d o u b l e d O a t o - 2:
f D a t o - ( f 1o a t I d D a t o :
1D a t o - ( 1 o n g ) f D a t o ;
i D a t o - ( i n t )1 D a t o :
sDato - ( s h o rt) iD a to :
bDato - ( b y t e M s D a t o + i D a t o - I D a t o * fD a t o / dDato):
S y s t e m . o u t . p r i n t l n ( b D a t o ) : // r e s u l t a d o : 2
f l o a t r:
r = <f l o a t ) M a t h . s q r t ( 10): // el r e s u l t a d o s e r e d o n d e a p e r d i e n d o
// p r e c i s i ó n y a que s q r t d e v u e l v e un
// v a l o r d e t i p o d o u b l e
OPERADORES
Los operadores son sím bolos que indican cóm o son m anipulados los datos. Se
pueden clasificar en los siguientes grupos: aritm éticos, relaciónales, lógicos, uni
tarios, a nivel de bits, de asignación y operador condicional.
Operadores aritméticos
Los operadores aritm éticos los utilizam os para realizar operaciones m atem áticas y
son los siguientes:
O p erad or O p e ra c ió n
+ Sum a. Los operandos pueden ser enteros o reales.
- Resta. Los operandos pueden ser enteros o reales.
* M ultiplicación. Los operandos pueden ser enteros o reales.
/ D ivisión. Los operandos pueden ser enteros o reales. Si am bos ope
randos son enteros el resultado es entero. En el resto de los casos el
resultado es real.
% M ódulo o resto de una división entera. L os operandos tienen que
ser enteros.
C A PÍTU LO 3: ELEM EN TO S D E L LEN G U A JE 5 3
1nt a - 10. b - 3.
float x = 2.0F. y;
y - x + a // El r e s u l t a d o e s 1 2 . 0 de U p o f l o a t
c - a / b // El r e s u l t a d o e s 3 de t i p o i n t
c = a X b // El r e s u l t a d o e s 1 de t i p o i n t
y = a / b // El r e s u l t a d o e s 3 de t i p o i n t . Se
// convierte a f l o a t para a s i g n a r l o a y
c » <i n t ) ( x / y); // El r e s u l t a d o e s 0 . 6 6 6 6 6 6 7 de t i p o f l o a t . Se
// convierte a i n t p a r a a s i g n a r l o a c ( c = 0)
C uando en una operación aritm ética los operandos son de diferentes tipos,
am bos son convertidos al tipo del operando de precisión m ás alta. P or ejem plo,
para realizar la sum a x+a el valor del entero a es convertido a float, tipo de x. No
se m odifica a. sino que su valor es convertido a float sólo para realizar la suma.
Los tipos sh o rt y byte son convertidos de m anera autom ática a int.
Operadores de relación
Los operadores de relación o de com paración perm iten evaluar la igualdad y la
m agnitud. El resultado de una operación de relación es un valor booleano true o
false. Los operadores de relación son los siguientes:
O p erad or O p e ra c ió n ___________________________________________________
Los operandos tiene que ser de un tipo prim itivo. Por ejem plo:
i n t x = 10. y = 0:
b o o l e a n r;
r = x =“ y : // r = false
r = x > y: // r = true
r = x != y : // r = true
Un operador d e relación equivale a una pregunta relativa a cóm o son dos ope
randos entre sí. Por ejem plo, la expresión x= = y equivale a la pregunta ¿x es igual
a y ? U na respuesta sí equivale a un valor verdadero (true) y una respuesta no
equivale a un valor falso (false).
Operadores lógicos
El resultado de una operación lógica (A N D , OR, X O R y N O T) es un valor boo-
leano verdadero o falso (tru e o false). Las expresiones que dan com o resultado
valores booleanos (véanse los operadores de relación) pueden com binarse para
form ar expresiones booleanas utilizando los operadores lógicos indicados a con
tinuación. Los operandos deben ser expresiones que den un resultado boolean.
O p erad or O p e ra c ió n ___________________________________________________
&& o & A N D . D a com o resultado true si al evaluar cada uno de los operan-
dos el resultado es true. Si uno de ellos es false, el resultado es fal
se. Si se utiliza && (no & ) y el prim er operando es false, el
segundo operando no es evaluado.
II o I OR. El resultado es false si al evaluar cada uno de los operandos el
resultado es false. Si uno de ellos es true, el resultado es true. Si se
utiliza II (no I) y el prim er operando es true, el segundo operando
no es evaluado (el carácter I es el A S C II 124).
! N O T. El resultado de aplicar este operador es false si al evaluar su
operando el resultado es true, y true en caso contrario.
A XO R. D a com o resultado true si al evaluar cada uno de los operan-
dos el resultado de uno es true y el del otro false; en otro caso el
resultado es false.
i n t p = 10, q = 0:
boolean r:
r = p != 0 && q ! = 0: // r = f a l s e
C A PÍTU LO 3: ELEM EN TO S DEL LEN G U A JE 5 5
r = p ! = 0 | | q > 0 : // r - true
r - q < p && p o 10: II r - true
r “ !i": //si r = true. entonces r = false
Operadores unitarios
Los operadores unitarios se aplican a un solo operando y son los siguientes: !,
++ y — . El operador ! ya lo hem os visto y los operadores ++ y — los vere
m os m ás adelante.
O p erad or O p e ra c ió n _______________________________________________
C om plem ento a 1 (cam biar ceros por unos y unos por ceros). El ca
rácter ~ es el A SC II 126. El operando debe de ser de un tipo prim i
tivo entero.
C am bia de signo al operando (esto es, se calcula el com plem ento a
dos que es el com plem ente a 1 m ás 1). El operando puede ser de un
_____________ tipo prim itivo entero o real.
int a = 2, b = 0. c = 0:
c = - a; // r e s u l t a d o c = -2
c = ~b: // r e s u l t a d o c = -1
O p erad or O p e ra c ió n
& O peración A N D a nivel de bits.
1 O peración OR a nivel de bits (carácter A SCII 124).
A
O peración X O R a nivel de bits.
« D esplazam iento a la izquierda rellenando con ceros por la derecha.
» D esplazam iento a la derecha rellenando con el bit de signo por la
izquierda.
» > D esplazam iento a la derecha rellenando con ceros p o r la izquierda.
Operadores de asignación
El resultado de una operación de asignación es el valor alm acenado en el operan
do izquierdo, lógicam ente después de que la asignación se ha realizado. El valor
que se asigna es convertido im plícita o explícitam ente al tipo del operando de la
izquierda (véase el apartado “C onversión entre tipos de datos"). Incluim os aquí
los operadores de increm ento y decrem ento porque im plícitam ente estos operado
res realizan una asignación sobre su operando.
O p erad or O p e ra c ió n
++ Increm ento.
— D ecrem ento.
= A signación simple.
*= M ultiplicación m ás asignación.
/= D ivisión m ás asignación.
%= M ódulo m ás asignación.
+= Sum a m ás asignación.
-= R esta m ás asignación.
«= D esplazam iento a izquierdas m ás asignación.
»= D esplazam iento a derechas m ás asignación.
» > D esplazam iento a derechas m ás asignación rellenando con ceros.
&= O peración A N D sobre bits m ás asignación.
1= O peración OR sobre bits m ás asignación.
A_ O peración X O R sobre bits m ás asignación.
Los operandos tienen que ser de un tipo prim itivo. A continuación se m ues
tran algunos ejem plos con estos operadores.
i n t x = 0, n= 1 0 . i = l :
n++: // I n c r e m e n t a e l v a l o r de n en 1.
CA PITU LO 3: ELEM ENTOS DEL L EN G U A JE 5 7
++n; / i n c r e m e n t a e l v a l o r de n en 1.
x - + +n : / I n c r e m e n t a n en 1 y a s i g n a e l r e s u l t a d o a x .
x = n++; / A s i g n a e l v a l o r de n a x y después
/ i n c r e m e n t a n en 1.
i + = 2: / R e a l i z a la o p e r a c ió n i = i + 2.
x * = n - 3: / R ea liz a la operación x = x * ( n - 3 ) y no
/ x = x * n - 3.
n > > = 1; / R e a l i z a l a o p e r a c i ó n n = n >> 1 l a c u a l d e s p l a z a
/ el c o n t e n i d o de n 1 b i t a l a d e r e c h a .
Operador condicional
El operador condicional (?;), llam ado tam bién operador ternario, se utiliza en ex
presiones condicionales, que tienen la form a siguiente:
Lina expresión entre paréntesis, siem pre se evalúa prim ero. Los paréntesis tie
nen m ayor prioridad y son evaluados de m ás internos a m ás externos.
O p erad or A so c ia tiv id a d
O [] . izquierda a derecha
----- ! + + — derecha a izquierda
new ( t i p o ) e x p r e s i ó n derecha a izquierda
* / X izquierda a derecha
+ - izquierda a derecha
<< » » > izquierda a derecha
< < = > > = instanceof izquierda a derecha
== != izquierda a derecha
& izquierda a derecha
A
izquierda a derecha
izquierda a derecha
n izquierda a derecha
II izquierda a derecha
? ; derecha a izquierda
= *= /= %= + = — <<= » = »>= &= |= * derecha a izquierda
En Java, todos los operadores binarios excepto los de asignación son evalua
dos de izquierda a derecha. En el siguiente ejem plo, prim ero se asigna z a y y a
continuación y a x.
i n t x = 0 . y = 0, z = 15;
x = y = z; // r e s u l t a d o x = y = z - 15
EJERCICIOS RESUELTOS
La siguiente aplicación utiliza objetos de una clase C E cuacion para evaluar ecua
ciones de la forma:
a x i + bx 2 +cx + d
CA PITU LO 3: ELEMENTOS D E L LEN G U A JE 5 9
El m étodo Ecuación sim plem ente asignará los valores pasados com o argu
m entos a los atributos representativos de los coeficientes de la ecuación.
p u b lic double V a l o r P a r a ( d o u b l e x)
1
double re su lta d o :
// Real i z a r c á l c u l o s
return resultado: < Valor devuelto por
el método
class CEcuacion
1
II El t é r m i n o de m a y o r g r a d o t i e n e e x p o n e n t e 3 f i j o
d o u b l e c 3 . c 2 . e l . cO: // c o e f i c i e n t e s
p u b l i c v o i d E c u a c i ó n t d o u b l e a. d o u b l e b. d o u b l e c . d o u b l e d )
I
c 3 - a ; c 2 = b; el - c ; cO=d;
60 JA V A: C U R S O DE PROGRAM A CIÓN
p u b lic double V a l o r P a r a ( d o u b l e x)
I
double r e s u l t a d o ;
r e s u l t a d o = c 3 * x * x * x + c 2 * x * x + c l * x + cO:
r e t u r n r e s u l t a d o ; // d e v o l v e r e l v a l o r c a l c u l a d o
El siguiente paso es añadir al m ism o fichero fuente una clase aplicación pú
blica que utilice la clase de objetos C Ecuacion. E sta clase aplicación puede ser de
la form a siguiente:
double r = e c l . V a l o r P a r a ( l ) ;
S y s t e m . o u t . p r i n t l n ( r );
r = e c l .ValorP ara(1.5):
System .out.pri n t l n ( r ) ;
EJERCICIOS PROPUESTOS
1. Escriba una aplicación que visualice en el m onitor los siguientes m ensajes:
B i e n v e n i d o a l mundo de J a v a .
P o d r á s d a r s o l u c i ó n a much o s problemas.
CA PÍTU LO 3: ELEM EN TO S DEL LENGUAJE 6 1
i n t a - 10, b = 3 , c = 1 , d , e :
f l o a t x. y :
x - a / b;
c = a < b && c ;
d - a + b++;
e - + + a - b;
y = ( f l o a t ) a / b:
3. Escriba las sentencias necesarias para evaluar la siguiente ecuación para valores
de a = 5 , b = -1.7, c = 2 y x = 10.5.
a x3+ bx2 - e x + 3
5. Decida qué tipos de datos necesita para escribir un program a que calcule la suma
y la m edia de cuatro núm eros de tipo int.
b 2 -A o c
2a
para valores d e a = l , b = 5 y c = 2.
CA PÍTU LO 4
ESTRUCTURA DE UN PROGRAMA
En este capítulo estudiará cóm o es la estructura de una program a Java. Partiendo
de un program a ejem plo sencillo analizarem os cada una de las partes que com po
nen su estructura, así tendrá un m odelo para realizar sus propios program as. T am
bién verem os cóm o se construye un program a a partir de varios m ódulos de clase.
Por últim o, estudiarem os los conceptos de ám bito y accesibilidad de las variables.
T oda aplicación Java está form ada por al m enos una clase que define un m é
todo nom brado m a in , com o se m uestra a continuación:
public c la s s CMiApi i c a c i o n
I
public static void main( S t r i n g f ] a rg s)
I
// e s c r i b a aqui el c ó d i g o que q u i e r e e j e c u t a r
I
I
■30 C -22.00 F
-24 C -11.20 F
90 C 194.00 F
96 C 204.80 F
La relación entre los grados centígrados y los grados fa h ren h eit viene dada
por la expresión grados fa h ren h eit = 9/5 * grados centígrados + 32. L os cálculos
los vam os a realizar para un intervalo de - 3 0 a 100 grados centígrados con incre
m entos de 6.
class CGrados
I
prívate f lo a t gradosC; // g r a d o s centígrados
El código anterior m uestra que un objeto de la clase C G rados tendrá una es
tructura interna form ada p o r el atributo:
• C entígradosA signar que perm ite asignar a un objeto un valor en grados centí
grados.
• F ahrenheitO btener que perm ite retornar el valor grados fa h ren h eit equiva
lente a g ra d o sC grados centígrados.
• C entígradosO btener que perm ite retornar el valor alm acenado en el atributo
gradosC .
Sin casi dam o s cuenta estam os abstrayendo (separando por m edio de una
operación intelectual) los elem entos naturales que intervienen en el problem a a
resolver y construyendo objetos que los representan.
R ecordando lo visto anteriorm ente, una aplicación Java tiene que tener un
objeto aplicación, que aporte un m étodo m a in , por donde em pezará y term inará la
ejecución de la aplicación, adem ás de otros que consideram os necesarios. ¿Cóm o
podem os im aginar esto de una form a gráfica? La figura siguiente da respuesta a
esta pregunta:
mensajes/respuestas
CentígradosAsi gnar
FahrenheitObtener
Entonces, ¿qué tiene que hacer el objeto aplicación? Pues, visualizar cuántos
grados fa h ren h eit son - 3 0 C, - 2 4 C n grados centígrados 96 C. Y, ¿cóm o
hace esto? E nviando al objeto CG rados los m ensajes C entígradosA signar y F ah
renheitO btener una vez para cada valor desde - 3 0 a 100 grados centígrados con
66 JA VA : C U R SO DE PROGRAM A CIÓN
public c la ss CApGrados
[
// D e f i n i c i ó n de c o n s t a n t e s
final sta t ic int lim ln fe rio r - -30;
final s t a t i c i n t l i m S u p e r i o r = 10 0 :
final s t a t i c i n t i n c r e m e n t o = 6:
// A s i g n a r a l o b j e t o g r a d o s e l v a l o r en g r a d o s c e n t í g r a d o s
g r a d o s . C e n t í g r a d o s A s i g n a r ( g r a d o s C e n t ):
// O b t e n e r d e l o b j e t o g r a d o s l o s g r a d o s
gradosFahr = g r a d o s .FahrenheitObtener
// E s c r i b i r l a s i g u i e n t e l i n e a d e l a t a b l a
S y s t e m . o u t . p r i n t l n ( g r a d o s C e n t + ” C" + " \ t ” + gradosFahr + F " );
// S i g u i e n t e v a l o r
gradosCent += incremento:
Seguro que pensará que todo el proceso se podría haber hecho utilizando so
lam ente el objeto aplicación, escribiendo todo el código en el m étodo m ain. lo
cual es cierto. Pero, lo que se pretende es q ue pueda ver de una form a clara que,
en general, un program a Java es un conjunto de objetos que se com unican entre sí
m ediante m ensajes con el fin de obtener el resultado perseguido, y que la solución
del problem a puede resultar m ás sencilla cuando consiga realizar una representa
ción del problem a en base a los objetos naturales que se deducen de su enunciado.
Piense que en la realidad se enfrentará a problem as m ucho m ás com plejos y, por
lo tanto, la descom posición en objetos será vital para resolverlos.
En el ejem plo realizado podem os observar que una aplicación Java consta de:
Sabem os tam bién que una clase encapsula los atributos de los objetos que
describe y los m étodos para m anipularlos. Pues bien, cada m étodo consta de:
Los apartados que se exponen a continuación explican brevem ente cada uno
de estos com ponentes que aparecen en la estructura de un program a Java.
ja va
J
lang
j applet
J predeterm inado
J
IO nel
J o tros paquetes
J
1 ... )
awt
J
El nivel superior se denom ina ja v a. En el siguiente nivel tenem os paquetes
com o lang. applet o io.
Por om isión una clase tiene el nivel de protección de paquete ; p o r ejem plo, la
clase CGrados del ejem plo anterior tiene este nivel de protección. En cam bio,
cuando se desea que una clase tenga protección pública, hay que calificarla como
tal utilizando la palabra reservada public; la clase CApGrados del ejem plo ante
rior tiene este nivel de protección. O tro ejem plo: echando un vistazo a la docu
CA PÍTU LO 4: ESTRU C TU R A DE UN PRO G R A M A 6 9
m entación de Java, se puede observar que la clase S y ste m del paquete ja v a .la n g
es pública, razón por la cual se ha podido utilizar en la aplicación C ApG rados.
Sentencia import
Una clase de un determ inado paquete puede hacer uso de otra clase de otro pa
quete de dos formas:
1. U tilizando su nom bre com pleto en todas las partes del código donde haya que
referirse a ella. Por ejem plo:
j a v a . l a n g . S y s t e m . o u t . p r i n t l n ( g r a d o s F a h r );
System.out.println(gradosFahr);
C om o se puede com probar en el ejem plo anterior, im portar una clase perm ite
al program a referirse a ella m ás tarde sin utilizar el nom bre del paquete. Esto es,
la sentencia im p o rt sólo indica al com pilador e intérprete de Java dónde encontrar
las clases, no trae nada dentro del program a Java actual.
En el caso concreto del ejem plo expuesto, si elim inam os la sentencia im p ort,
todo seguirá funcionando igual. Esto es así porque las clases del paquete ja -
v a .la n g son im portadas de m anera autom ática para todos los program as, no suce
diendo lo m ism o con el resto de los paquetes, que tienen que ser im portados
explícitam ente.
70 JA V A : C U R SO DE PROGRAM A CIÓN
public c la ss CApGrados
I
11...
S y s t e m . o u t . p r i n t l n í g r a d o s C e n t + " C ” + " \ t " + g r a d o s F a h r + “ F “ ):
II ...
I
Tam bién puede im portar un paquete com pleto de clases utilizando com o co
m odín un asterisco en lugar del nom bre específico de una clase. P or ejem plo:
import j a v a . l a n g . * : II i m p o r t a r l a s c l a s e s p ú b l i c a s de e s t e paquete
// a l a s que s e r e f i e r a e l c ó d i g o
En realidad, para ser exactos, la sentencia im p o rt del ejem plo anterior im
p orta todas las clases públicas del paquete ja v a .la n g que realm ente se usen en el
código del program a.
Definiciones y declaraciones
U na declaración introduce uno o m ás identificadores en un program a. U na decla
ración es una definición, a m enos que no haya asignación de m em oria.
T oda variable debe ser definida antes de ser utilizada. La definición de una
variable, declara la variable y adem ás le asigna m em oria:
in t gradosCent:
f lo a t gradosFahr:
gradosCent = lim l n f e r io r :
g r a d o s F a h r = 0:
in t gradosCent = lim l n f e r io r ;
f l o a t g r a d o s F a h r = 0:
class UnaClase
•I
Nivel d e la c la se D e c l a r a c i ó n de v a r i a b l e s (atributos)
S e n t e r ic i as
Sentencia simple
Una sentencia sim ple es la unidad ejecutable m ás pequeña de un program a Java.
Las sentencias controlan el flujo u orden de ejecución. U na sentencia Java puede
formarse a partir de: una palabra clave (for, while, if ... else, etc.), expresiones,
declaraciones o llam adas a m étodos. C uando se escriba una sentencia hay que te
ner en cuenta las siguientes consideraciones:
• T oda sentencia sim ple term ina con un punto y com a (;).
• Dos o m ás sentencias pueden aparecer sobre una m ism a línea, separadas una
de otra por un punto y com a, aunque esta form a de proceder no es aconsejable
porque va en contra de la claridad que se necesita cuando se lee el código de
un program a.
• Una sentencia nula consta solam ente de un punto y com a. C uando veam os la
sentencia w hile, podrá ver su utilización.
72 JA VA : C U R SO DE PRO G R A M A CIÓ N
grados.CentlgradosAsi gnar(gradosCent):
g r a d o s F a h r = g r a d o s .F a h r e n h e i t O b t e n e r ( );
S y s t e m . o u t . p r i n t l n t g r a d o s C e n t + " C" + "\t" + g r a d o s F a h r + " F n );
g r a d o s C e n t + = inc re me nt o:
Métodos
Un m étodo es una colección de sentencias que ejecutan una tarea específica. En
Java, un m étodo siem pre pertenece a una clase y su definición nunca puede con
tener a la definición de otro m étodo; esto es. Java no perm ite m étodos anidados.
Definición de un método
La definición de un m étodo consta de una cabecera y del cuerpo del m étodo en
cerrado entre llaves. La sintaxis para escribir un m étodo es la siguiente:
[ modificador] t i p o - r e s u l t a d o n o m b r e - m é t o d o ( . [ l i s t a d e p a r á m e t r o s])
I
d e c l a r a d o r e s de v a r i a b l e s locales:
sentencias;
[ r e t u r n [ ( ] e x p r e s ió n l) ] ] :
Las variables declaradas en el cuerpo del m étodo son locales a dicho m étodo
y por definición solam ente son accesibles dentro del mismo.
El tipo del resultado especifica qué tipo de valor retorna el m étodo. Éste,
puede ser cualquier tipo prim itivo o referenciado. Para indicar que no se devuelve
nada, se utiliza la palabra reservada void. El resultado de un m étodo es devuelto a
la sentencia que lo invocó, por m edio de la siguiente sentencia:
r e t u r n [ ( ] expresión[ ) ] :
CA PÍTU LO 4: ESTRU C TU R A D E UN PROGRAM A 7 3
v o i d m Escri bi r ( )
I
// ...
return:
La lista de p a rám etros de un m étodo son las variables que reciben los valores
de los argum entos especificados cuando se invoca al m ism o. C onsisten en una
lista de cero, uno o m ás identificadores con sus tipos, separados por com as. A
continuación se m uestra un ejem plo:
Método main
T oda aplicación Java tiene un m étodo denom inado m ain, y sólo uno. Este m étodo
es el punto de entrada a la aplicación y tam bién el punto de salida. Su definición
es com o se m uestra a continuación:
Sabem os que las clases son plantillas para crear objetos. Pero, ¿cóm o se crea un
objeto? Para crear un objeto de una clase hay que utilizar el operador new , análo
gam ente a com o m uestra el ejem plo siguiente:
74 JA V A: C U R SO DE PROGRAM A CIÓN
C G r a d o s g r a d o s = new C G r a d o s O :
En este ejem plo se observa que para crear un objeto de la clase C Grados hay
que especificar a continuación del operador new el nom bre de la clase del objeto
seguido de paréntesis. ¿P o r qué paréntesis? ¿Es acaso C G rados un m étodo? Así
es. M ás adelante aprenderá que toda clase tiene al m enos un m étodo predetermi
nado especial denom inado igual que ella, que es necesario invocar para crear un
objeto; ese m étodo se denom ina constructor de la clase.
O tro ejem plo; ahora con una clase de la biblioteca Java. El paquete java.util
proporciona una clase denom inada G r e g o r ia n C a le n d a r . Un objeto de esta clase
representa una fecha, incluyendo tam bién opcionalm ente la hora. El siguiente có
digo crea tres objetos de esta clase,./7*/,/7?2 y fh 3 . iniciados, el prim ero con la fe
cha y hora actual por om isión, el segundo con la fecha especificada, y el tercero
con la fecha y hora especificadas:
G r e g o r i a n C a l e n d a r f h l = new G r e g o r i a n C a l e n d a r t );
G r e g o r i a n C a l e n d a r f h 2 = new G r e g o r l a n C a l e n d a r ( 2 0 0 1 . 1. 2 1 ) :
G r e g o r i a n C a l e n d a r f h 3 = new G r e g o r i a n C a l e n d a r ( 2 0 0 1 . 1, 2 1 , 12, 30, 15):
Las sentencias anteriores son válidas porque, com o puede com probar si lo de
sea, la clase G r e g o r ia n C a le n d a r proporciona varias form as de construir un ob
jeto : sin utilizar parám etros, con tres parám etros (año, m es y día), con seis
parám etros (año, m es, día, hora, m inutos y segundos), etc.
C uando se crea un nuevo objeto utilizando new. Java asigna automáticam ente
la cantidad de m em oria necesaria para ubicar ese objeto. Si no hubiera suficiente
espacio de m em oria disponible, el operador new lanzará una excepción O u tO f-
M e m o r y E r r o r cuyo estudio posponem os. D espués de saber esto quizás se pre
gunte: ¿Q uién libera esa m em oria y cuándo lo hace? La respuesta es otra vez la
m isma: Java se encarga de hacerlo en cuanto el objeto no se utilice, cosa que ocu
rre cuando ya no exista ninguna referencia al objeto. Por ejem plo, en el código
que se m uestra a continuación, la m em oria asignada a los objetos f h l , f h 2 y fh3
será liberada cuando finalice la ejecución del m étodo m ain.
import j a v a . u t i l .*:
p u b l i c c l a s s CFechaHora
1
public static void m a in ( S t r in g [ ] args)
I
Gr e g o r i an C a l e n d a r f h l = new G r e g o r i a n C a l e n d a r í ):
Gr e g o r i an C a l e n d a r f h2 = new G r e g o r i a n C a l e n d a r t 2001, 1. 21):
Gr e g o r i an C a l e n d a r f h3 = new G r e g o r i an C a l e n d a r ( 20 0 1 . 1. 21. 12. 30, 15):
// .. .
CA PÍTU LO 4: E ST R U C TU R A D E UN PRO G R A M A 7 5
A hora basta con que sepa que Java cuenta con una herram ienta denom inada
recolector d e basura que busca objetos que no se utilizan con el fin de destruirlos
liberando la m em oria que ocupan. M ás adelante aprenderá sobre este m ecanism o.
m i O b j e t o . a t r i buto:
mi O b j e t o . m e t o d o ( [ a r g u m e n t o s ] );
Lógicam ente, com o pueden existir varios objetos de la m ism a clase, es nece
sario especificar de quién es el m iem bro. Si el m iem bro es a su vez un objeto, la
sintaxis se extiende siguiendo la m ism a sintaxis: objeto.m broO bjeto.m iem bro.
R ecuerde que el operador punto (.) se evalúa de izquierda a derecha.
g r a d o s . C e n t í g r a d o s A s i g n a r ( g r a d o s C e n t ):
Es im portante asim ilar que un program a orientado a objetos sólo se com pone
de objetos que se com unican m ediante m ensajes. D esde este conocim iento, no
tiene sentido pensar que un m étodo se pueda invocar aisladam ente, esto es, sin
que exista un objeto para el que es invocado. Por ejem plo, si en el m étodo m a in
de nuestra aplicación ejem plo pudiéram os escribir:
76 JA VA : C U R SO DE PROGRAM A CIÓN
CentígradosAsignar(gradosCent);
seguro que nos preguntaríam os ¿a quién se asigna el valor gra d o sC en tl Los mé
todos static que estudiarem os m ás tarde son una excepción a la regla.
class CGrados
I
prívate f lo a t gradosC; // g r a d o s centígrados
Un m iem bro de una clase declarado p rivado puede ser accedido únicam ente
p o r lo m étodos de su clase. En el ejem plo anterior se puede observar que el atri
buto gradosC es privado y es accedido por el m étodo C entígrados A sig n a r.
g r a d o s . g r a d o s C = 30:
el com pilador Java m ostraría un error indicando que el m iem bro gradosC no es
accesible desde esta clase, p o r tratarse de un m iem bro privado de CGrados.
G eneralm ente los atributos de una clase de objetos se declaran privados, es
tando así ocultos para otras clases, siendo posible el acceso a los m ism os única
m ente a través de los m étodos públicos de dicha clase. El m ecanism o de
ocultación de m iem bros se conoce en la program ación orientada a objetos com o
encapsulación: proceso de ocultar la estructura interna de datos de un objeto y
p erm itir el acceso sólo a través de la interfaz pública definida, entendiendo por
interfaz pública el conjunto de m iem bros públicos de una clase. ¿Q ué beneficios
reporta la encapsulación? Q ue un usuario de una determ inada clase no pueda es
cribir código en base a la estructura interna del objeto, sino sólo en base a la in
terfaz pública; esta form a de proceder obliga a pensar en objetos y a trabajar con
ellos. En el capítulo “C lases” que expondrem os m ás adelante abundarem os más
sobre lo dicho y sobre otras m uchas cuestiones.
Un atributo de la clase alm acena inform ación com ún a todos los objetos de
esa clase. Se define agregándole previam ente la palabra reservada static. y existe
aunque no haya objetos definidos de la clase.
class COrdenador
I
S trin g Marca:
S t r in g Procesador;
Strin g Pantalla;
s t a t i c byte Garantía;
boolean O rdenadorEncendido:
boolean P re se nta c ió n ;
// ...
En el ejem plo que se m uestra a continuación se puede observar que para esta
blecer el valor del atributo privado G arantía se ha utilizado un m étodo estático. Si
CA PÍTU LO 4: ESTRU C TU R A D E UN PRO G R A M A 79
public c la ss CM iAplicacion
I
public static v o i d main (Stringf] args)
1
C O r d e n a d o r .E s t a b l e c e r G a r a n t 1 a ( <b y t e )3):
)
:
class COrdenador
I
private S t r i n g Marca;
private S t r in g Procesador;
private S t r i n g P a n t a l l a ; _________________________ _ _ _
private s t a t i c byte Garantía;
private boolean OrdenadorEncendido:
private boolean P re se nta c ió n :
/ / ...
El m étodo E stablecerG arantía del ejem plo anterior puede acceder a G arantía
porque es un m iem bro estático pero no podría incluir, por ejem plo, una sentencia
com o M arca = " A s t” porque M arca no es static.
A hora puede com prender por qué el m étodo m a in es static: para que pueda
ser invocado aunque no exista un objeto de su clase. Por ejem plo, el m étodo m a in
de la clase C M iA plicacion anterior, es invocado cuando se ejecuta la aplicación,
independientem ente de que exista un objeto de esa clase.
Referencias a objetos
Según lo q ue hem os aprendido hasta ahora, para crear un objeto de una clase hay
que hacerlo explícitam ente utilizando el operador new. Por ejem plo:
C G r a d o s g r a d o s = new C G r a d o s O :
El operador new devuelve una referencia al nuevo objeto, que se alm acena en
una variable del tipo del objeto. En el ejem plo anterior, la referencia devuelta por
80 JA VA : C U R SO DE PROGRAM A CIÓN
el op erad o r new es alm acenada en la variable grados del tipo C G rados. La clase
C G rados se encuadra dentro de lo que hem os denom inado tipos referenciados.
E s p a c io d e m e m o ria
g rad o s O b je to re fe re n c ia d o
íelUIUIlUlcl ^
g ra d o s C = 3 0
class CRacional
I
prívate int Numerador:
prívate int Denominador:
rl.A s ig n a rD a t o s ( 3 . 7);
rl .V isu a liza rR a cio n a l(): // s e v i s u a l i z a 3/7
r2 . V i s u a l i z a r R a c i o n a l ( ) ; // s e v i s u a l i z a 3/7
C R a c io n a l r3;
r 2 = new C R a c i o n a l O : // c r e a r un o b j e t o C R a c i o n a l
r 2 . A s i g n a r D a t o s ( 2 . 5):
r 3 = C R a c i o n a l . S u m a r ( r 1. r 2 ) : // r 3 = 3 /7 + 2/5
r 3 . V i s u a l i z a r R a c i o n a l ( ) : // s e v i s u a l i z a 2 9 / 3 5
La clase C R acional encapsula una estructura d e datos form ada por dos en te
ros: num erador y denom inador; y para acceder a esta estructura proporciona la
interfaz pública form ada p o r los m étodos:
• A signarD atos que perm ite establecer el num erador y el denom inador de un
núm ero racional.
• V isualiza/R acional q ue perm ite visualizar un racional en form a de quebrado.
• S u m a r que devuelve el núm ero racional resultante de sum ar otros dos pasados
com o argum entos.
r l a r2, sim plem ente se creó una nueva referencia al m ism o objeto referenciado
por r l . Por lo tanto, m odificar el objeto al que se refiere r l es m odificar el objeto
al que se refiere r2 porque r l y r2 referencian el m ism o objeto.
E s p a c io d e m e m o ria
O b je to re fe re n c ia d o
N u m e ra d o r
D e n o m in a d o r
r l - new C R a c i o n a l ( ) ; II c r e a r un o b j e t o C R a c i o n a l rl
r l . A s i g n a r D a t o s ( 3 , 7);
r 2 - new C R a c i o n a l O ; // c r e a r un o b j e t o C R a c i o n a l r2
r2 .A sig n a rD a to s(2 . 5):
E s p a cio d e m e m o ria
r1 r° i
3 /7 2 /5
II...
CRa cio nal r3;
r 2 = new C R a c i o n a l O : // c r e a r un o b j e t o C R a c i o n a l
r2 .A sign a rD a to s(2 , 5);
C A PÍTU LO 4: ESTRU C TU R A D E UN PROGRAM A 8 3
r3 = CRacio na l . S u m a r t r l . r 2 ) ; // r 3 = 3 /7 + 2 /5
r 3 . V i s u a l i z a r R a c i o n a l ( ); // s e v i s u a l i z a 2 9 / 3 5
A nalicem os el m étodo Sum ar. E ste m étodo tiene dos parám etros de tipo C Ra
cional. D espués de que el m étodo ha sido invocado desde m ain , a y b señalan a
los m ism os objetos que r l y r2. Esto significa que los objetos pasados a los pará
m etros de un m étodo son siem pre referencias a dichos objetos, lo cual significa
que cualquier m odificación que se haga a esos objetos dentro del m étodo afecta al
objeto original. En cam bio, las variables de un tipo prim itivo pasan por valor, lo
cual significa que se pasa una copia, p o r lo que cualquier m odificación que se ha
ga a esas variables dentro del m étodo no afecta a la variable original.
neralm ente se alm acena cada clase en un único fichero para favorecer su m ante
nim iento y posterior reutilización.
public void V i s u a l i z a r R a c i o n a l ()
I
Sy ste m .o u t.p rin tln (N u m e ra d or + ’ /" + Denominador):
I
r l = new C R a c i o n a l ( ) ; // c r e a r un o b j e t o C R a c i o n a l
rl .AsignarD atos(2. 5):
r2 = rl ¡
r l .A s i g n a r D a t o s ( 3 . 7):
r l .Visual iz a r R a c io n a l(): / / s e vi s u a liz a 3/7
r2 . V i s u a l i z a r R a c i o n a l (): // s e v i s u a l i z a 3/7
C uando se com pile C Aplicacion, que por om isión pertenece al paquete pre
determ inado. puesto que necesita utilizar la clase C R acional, buscará tam bién ésta
en el m ism o paquete, lo que supone buscar su fichero com pilado, o en su defecto
su fichero fuente, en el directorio actual de trabajo. Por lo tanto, antes de com pilar
la aplicación asegúrese de que el fichero C R acional.class o C Racional.java está
en el m ism o directorio que C Aplicacion.java.
ACCESIBILIDAD DE VARIABLES
A unque este tem a ya ha sido tratado, realizam os ahora un resum en. Se denom ina
ám bito de una variable a la parte de un program a donde dicha variable puede ser
referenciada por su nom bre. U na variable puede ser lim itada a una clase, a un
m étodo, o a un bloque de código correspondiente a una sentencia com puesta.
class UnaClase
V a ria b le lim itada
a u n a cla se v a r i a b l e s m i e mb r o d e la c l a s e (atributos)
— una s e n t e n c i a compuesta
V a ria b le lim itada
a un bloque Variables locales
86 JA VA : C U R SO DE PROGRAM A CIÓN
U na variable m iem bro de una clase puede ser declarada en cualquier sitio
dentro de la clase siem pre que sea fuera de todo m étodo. La variable está disponi
ble para todo el código de la clase.
En general, una variable local existe y tiene valor desde su punto de declara
ción hasta el final del bloque donde está definida. C ada vez que se ejecuta el blo
que que la contiene, la variable local es nuevam ente definida, y cuando finaliza la
ejecución del m ism o, la variable local deja de existir. Un elem ento con carácter
local es accesible solam ente dentro del bloque al que pertenece.
EJERCICIOS RESUELTOS
Con los conocim ientos que hem os adquirido hasta ahora vam os a realizar una
aplicación sencilla para sim ular una cuenta bancaria.
U na cuenta bancaria vista com o un objeto tiene, por una parte, atributos que
definen su estado, com o Tipo d e interés y Saldo, y p o r otra, operaciones que defi
nen su com portam iento, com o E stablecer tipo d e interés. Ingresar dinero. Retirar
dinero. Sa ld o a ctual o A b o n a r intereses.
ti poDelnterés = t i ;
// O p e r a c i o n e s
S y s t e m . o u t .p r in t ln ( C u e n t a O l. S a ld o A c t u a l( )):
CuentaOl. In g r e sa r D iñ e r o (500000):
CuentaOl.Reti ra rD in e ro í 200000):
System .out.println(CuentaO l.SaldoActual()):
C u e n t a O l . A b o n a r I n t e r e s e s * ):
System .out.println(CuentaO l.SaldoActual()):
El m étodo IngresarD inero acum ula la cantidad pasada com o argum ento sobre
el saldo actual.
El m étodo RetirarD inero verifica si hay suficiente dinero com o para poder
retirar la cantidad solicitada. En caso negativo lo notifica y term ina; en caso posi
tivo, resta del saldo la cantidad retirada.
EJERCICIOS PROPUESTOS
1. E scriba la aplicación C ApG rados.java y com pruebe los resultados.
3. M odifique los lím ites inferior y superior de los grados centígrados, el increm ento,
y ejecute de nuevo la aplicación.
5. R econstruya la aplicación C ApG rados.java para que cada clase esté alm acenada
en un fichero: la clase C G rados en el fichero C G rados.java y la clase C ApG rados
en el fichero C ApG rados.java.
CA PÍTU LO 5
©F.J.Cebalos/RA-MA
El paquete ja v a .io contiene las clases de objetos que proporcionan los m éto
dos necesarios para escribir inform ación en diversos dispositivos, por ejem plo en
la salida estándar (que se corresponde norm alm ente con la pantalla d e su ordena
dor) y para leer inform ación desde otros dispositivos, por ejem plo, desde la entra
da estándar (que es norm alm ente el teclado de su ordenador).
En este capítulo aprenderá cóm o leer y escribir inform ación desde sus aplica
ciones, y a trabajar con las clases utilizadas m ás frecuentem ente.
T anto los datos leídos com o los resultados obtenidos serán alm acenados en
variables pertenecientes a la estructura interna de uno o m ás objetos o declaradas
en algún m étodo. L os datos serán leídos a través de los m étodos proporcionados
por las clases de E/S y serán asignados a las variables directam ente por ellos, o
bien utilizando una sentencia de asignación de la form a:
d = a + b * c: //el valor de a + b * c s e a s i g n a a d
a + b * c = d: //el v a l o r de d no s e p u e d e a s i g n a r a a + ¿ * c
L os datos num éricos serán alm acenados en variables de alguno de los tipos
prim itivos expuestos en el capítulo 3. Por ejem plo:
S t r i n g cadena: // c a d e n a p e r m i t e r e f e r e n c i a r un o b j e t o S t r i n g
c a d e n a = " h o l a ” : // e q u i v a l e a: c a d e n a = new S t r i n g f " h o l a " );
C uando se asigna un valor a una variable estam os colocando ese valor en una
localización de m em oria asociada con esa variable.
i n t n v a r = 10; // v a r i a b l e de un t i p o p r i m i t i v o ( i n t )
String svar = "hola": // r e f e r e n c i a a un o b j e t o de t i p o S t r i n g
E s p a cio d e m e m o ria
n var
svar — ►
10 h ola
CA PÍTU LO 5: CL A SES DE U SO C O M Ú N 9 1
n v a r - 20;
svar = "a d ió s";
E s p a cio d e m e m o ria
n var
svar — ►
20 adiós
ENTRADA Y SALIDA
F recuentem ente un program a necesitará obtener inform ación desde un origen o
enviar inform ación a un destino. Por ejem plo, obtener inform ación desde, o enviar
inform ación a: un fichero en el disco, la m em oria del ordenador, otro program a,
Internet, etc.
Flujo d e s d e el origen
O rig e n
P ro g ra m a
1
D e s tin o 1
Flujo h a c ia el d es tin o
1
Entonces, para que un program a pueda obtener inform ación desde un origen
tiene que ab rir un flujo y leer la inform ación. A nálogam ente, para que un progra
ma puede en v iar inform ación a un destino tiene que abrir un flujo y escribir la
inform ación.
Los algoritm os para leer y escribir datos son siem pre m ás o m enos los mis
mos:
Leer E s c r ib ir
Abrir un flu jo desde un origen A brir un flu jo hacia un destino
M ientras haya información Mientras haya información
Leer información Escribir información
Cerrar el flu jo Cerrar el flujo
D ebido a que todas las clases relacionadas con flujos pertenecen al paquete
ja v a .io de la biblioteca estándar de Java, un program a que utilice flujos de E/S
tendrá que im portar este paquete:
import j a v a . i o . * ;
Las clases del paquete ja v a .io están divididas en dos grupos distintos, ambos
derivados de la clase O b je ct del paquete ja v a .la n g . según se m uestra en la figura
siguiente. El grupo de la izquierda ha sido diseñado para trabajar con datos de tipo
byte y el de la derecha con datos de tipo char. A m bos grupos presentan clases
análogas que tienen interfaces casi idénticas, por lo que se utilizan de la misma
manera.
Las clases som breadas son clases abstractas. U na clase abstracta no permite
que se creen objetos de ella. Su m isión es proporcionar m iem bros com unes que
serán com partidos por todas sus subclases.
C A PÍTU LO 5: CLA SES DE USO COM ÚN 9 3
Flujos de entrada
La clase In p u tS tre a m es una clase abstracta que es superclase de todas las clases
que representan un flujo en el que un destino lee bytes de un origen. C uando una
aplicación define un flujo de entrada, la aplicación es destino de ese flujo de
bytes, y es todo lo que se necesita saber.
La prim era versión de read sim plem ente lee bytes individuales de un flujo de
entrada; concretam ente lee el siguiente byte de datos disponible. D evuelve un en
tero (int) correspondiente al valor A S C I I del carácter leído, al núm ero de bytes
leídos si se lee una m atriz, o bien -1 cuando en un intento de leer datos se alcanza
el final del flujo (esto es, no hay m ás datos).
P o r ejem plo, suponiendo que tenem os definido un objeto flu jo E (flujo de en
trada) de alguna subclase de In p u tS tre a m . el siguiente código lee un byte del
origen vinculado con flu jo E :
i n t n:
n = f 1u j o E . r e a c K ):
La segunda versión del m étodo read lee un núm ero de bytes de un flujo de
entrada y los alm acena en una m atriz b (m ás adelante, dedicarem os un capítulo a
ex plicar las m atrices de datos). D evuelve un entero correspondiente al núm ero de
bytes leídos, o bien -1 si no hay bytes disponibles p ara leer porque se ha alcanza
do el final del flujo.
i n t n;
b y t e [ ] b = new b y t e [ 1 2 8 ] : // m a t r i z ’ b ' de 1 28 b y t e s
n = flujoE.read(b); // n e s e l nú m e r o de b y t e s leídos
La tercera versión del m étodo read lee un m áxim o de len bytes a partir de la
posición o f f de un flujo de entrada y los alm acena en una m atriz b.
C ada uno de estos m étodos ha sido escrito para que bloquee la ejecución del
program a que los invoque hasta que toda la entrada solicitada esté disponible.
Flujos de salida
La clase O u tp u tS tre a m es una clase abstracta que es superclase de todas las cla
ses que representan un flujo en el que un origen escribe bytes en un destino.
C uando una aplicación define un flujo de salida, la aplicación es origen de ese
flujo de bytes (es la q ue envía los bytes), y es todo lo que se necesita saber.
public v o i d w r i t e ( i n t b) t h r o w s I O E x c e p t i o n
public v o i d w r i t e ( b y t e [ ] b) t h r o w s I O E x c e p t i o n
public v o i d w r i t e ( b y t e [ ] b, i n t off, i n t len) t h r o w s IOException
La prim era versión de w rite sim plem ente escribe el byte especificado en un
flujo de salida. Puesto que su parám etro es de tipo int, lo que se escribe es el valor
correspondiente a los 8 bits m enos significativos, el resto son ignorados.
Por ejem plo, suponiendo que tenem os definido un objeto Jlu jo S (flujo de sali
da) de alguna subclase de O u tp u tS tre a m , el siguiente código escribe el byte es
pecificado en el destino vinculado con JlujoS:
int n;
II...
flujoS.w ri t e (n ) ;
La segunda versión del m étodo w rite escribe los bytes alm acenados en la
m atriz b en un flujo de salida (m ás adelante, dedicarem os un capítulo a explicar
las m atrices de datos).
La tercera versión del m étodo w rite escribe un m áxim o de len bytes de una
m atriz b a partir de su posición off. en un flujo de salida.
C ada uno de estos m étodos ha sido escrito para que bloquee la ejecución del
program a que los invoque hasta que toda la salida solicitada haya sido escrita.
Excepciones
Cuando durante la ejecución de un program a ocurre un error que im pide su conti
nuación, por ejem plo, una entrada incorrecta de datos o una división por cero. Ja
va lanza una excepción, que cuando no se captura da lugar a un m ensaje acerca de
lo ocurrido y detiene su ejecución (las excepciones se lanzan, no ocurren). Ahora,
si lo q ue deseam os es que la ejecución del program a no se detenga, habrá que
capturarla y m anejarla adecuadam ente en un intento de reanudar la ejecución.
Puesto que en Java hay m uchas clases de excepciones, un m étodo puede indi
car los tipos de excepciones que posiblem ente puede lanzar. Por ejem plo, puede
observar que los m étodos read y w rite que acabam os de exponer lanzan excep
ciones del tipo IO E x c e p tio n . Entonces, cuando utilicem os alguno de esos m éto
dos hay que escribir el código necesario para capturar las posibles excepciones
que pueden lanzar. E sto es algo a lo que nos obliga el com pilador Java, del m ism o
m odo que él verifica si una variable ha sido iniciada antes de ser utilizada, o si el
núm ero y tipo de argum entos utilizados con un m étodo son correctos, con la única
intención de m inim izar los posibles errores que puedan ocurrir.
Para capturar una excepción hay que hacer dos cosas: una, poner a prueba el
código que puede lanzar excepciones dentro de un bloque try; y dos, m anejar la
excepción cuando se lance, en un bloque catch. Por ejem plo:
try
1
// C ó d i g o que p u e d e l a n z a r una e x c e p c i ó n
n = f 1u j o E . r e a d ( ) : II p u e d e l a n z a r una e x c e p c i ó n IOException
I
c a t c h ( I O E x c e p t i o n e)
I
// M a n e j a r una e x c e p c i ó n d e l a c l a s e I O E x c e p t i o n
S y s t e m . o u t . p r in t 1n ( " E r r o r : " + e . g e t M e s s a g e ( ));
1
Esto es todo lo que necesita saber por ahora para poder utilizar los métodos
involucrados en la E/S que lancen excepciones. M ás adelante, dedicarem os un ca
pítulo al estudio de excepciones.
• System .out. R eferencia a la salida estándar del sistem a, que norm alm ente es
el m onitor. Se utiliza para m ostrar datos al usuario.
• System .err. R eferencia a la salida estándar de error del sistem a, que normal
m ente es el m onitor. Se utiliza para m ostrar m ensajes de error al usuario.
i n t n:
n = S y s t e m . i n . r e a d t ); // e n t r a d a p o r t e c l a d o : A
System .out.p rintln (n): // s a l i d a p o r m o n i t o r : 65
b y t e b:
b = ( b y t e ) S y s t e m . i n . r e a d í ) : // e n t r a d a p o r t e c l a d o : A
System .out.p rin tln (b): // s a l i d a p o r m o n i t o r : 65
El valor devuelto por el m étodo read tam bién puede ser convertido explíci
tam ente al tipo c h a r para m anipular caracteres:
c h a r c;
c = ( c h a r ) S y s t e m . i n . r e a d ( ) ; // e n t r a d a p o r t e c l a d o : A
System .out.p rin tln (c): // s a l i d a p o r m o n i t o r : A
¿Q ué otros m étodos podem os utilizar con estos flujos? Para dar respuesta a
esta pregunta prim ero tendrem os que investigar de qué clases son estos objetos y
después, analizar esas clases. A veriguar de qué clases son estos objetos es una ta
rea sim ple; basta con revisar la inform ación de la biblioteca de Java, o bien utili
zar un o bjeto C lass com o se indica en el apartado siguiente.
CA PÍTU LO 5: C L A SES DE USO COM ÚN 9 7
De los m étodos a los que nos hem os referido, nos interesa ahora ge tC lass. Pa
ra invocar este m étodo puede hacerlo así:
Class ObjetoClass - c u a l q u i e r O b j e t o . g e t C l a s s ( );
import j a v a . i o . * :
class ClaseDeUnObj
I
public static void m a in (S trin g [] args)
I
int n:
try
I
S y s t e m . o u t . p r i n t ( " D a t o : ” );
n = S y s t e m . i n . r e a d ( ) ; // l e e r un c a r á c t e r d e s d e e l t e c l a d o
S y s t e m . o u t . p r i n t l n t ( c h a r ) n ) ; // v i s u a l i z a r e l c a r á c t e r
// I n v e s t i g a m o s
C l a s s O b j e t o C l a s s ; // o b j e t o C l a s s
O b j e t o C l a s s = S y s t e m . i n . g e t C l a s s ( ):
S y s t e m . o u t . p r i n t l n ( " C l a s e de i n : " + O b j e t o C l a s s .getNamet)):
O b j e t o C l a s s = S y s t e m . o u t . g e t C l a s s t ):
S y s t e m . o u t . p r i n t 1n ( ”C 1 a s e d e o u t : " + O b j e t o C l a s s . g e t N a m e ( ) ) :
O b j e t o C l a s s = S y s t e m . e r r . g e t C l a s s í );
S y s t e m . o u t . p r i n t l n ( ’ C l a s e de e r r : " + O b j e t o C l a s s . g e t N a m e ( )) :
I
c a t c h ( I O E x c e p t i o n e)
I
S y s t e m . e r r . p r i n t l n t “E r r o r : " + e .g e t M e s sa g e í));
98 JA V A: C U R SO DE PROGRAM ACIÓN
Dato: 1
1
C l a s e de i n : c la s s java. io.BufferedlnputStream
C l a s e de o u t : c la ss j a v a . io.PrintStream
C l a s e de e r r : class java.io.PrintStream
BufferedlnputStream
L a clase análoga a B u ffe re d ln p u tS tre a m , pero que perm ite trabajar con ca
racteres es B u ffe re d R e a d e r. clase derivada de R e ad e r.
BufferedReader
} ( Flujo \ r x
__________ I F |ui ° | L O rig en
s u b y a c e n te r
V J V J
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r C i s r ):
El código anterior indica que el flu jo E dirigirá todas las invocaciones de sus
m étodos al flujo subyacente isr, este flujo, en el caso de que el origen sea el tecla
d o (dispositivo vinculado con Syste m .in ), deberá convertir los bytes leídos del te
clado en caracteres. D e esta form a flu jo E podrá sum inistrar un flujo de caracteres
al program a destino de los datos. Para ello hay que definir el flujo que hem os de
nom inado isr así:
InputStreamReader i s r = new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) :
c a ra c te re s bytes
C om o ejem plo, vam os a realizar una aplicación que lea una línea de texto in
troducida a través del teclado y la visualice en la pantalla.
import j a v a . i o . * ;
public c la ss LeerUnaCadena
I
public static void main( S t r i n g [ ] a rg s)
I
// D e f i n i r un f l u j o de c a r a c t e r e s d e e n t r a d a : f l u j o E
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r C S y s t e m . i n ) :
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r t i s r ) :
// D e f i n i r una r e f e r e n c i a a l f l u j o e s t á n d a r de s a l i d a : f l u j o S
PrintStream f lu j o S = System.out;
S t r i n g s d a t o ; // v a r i a b l e p a r a a l m a c e n a r una l i n e a de t e x t o
try
I
f l u j o S . p r i n t ( " I n t r o d u z c a un t e x t o : " > :
s d a t o = f 1u j o E . r e a d L i n e ( ) : // l e e r u n a l i n e a de t e x t o
flu joS.println(sdato); // e s c r i b i r l a l i n e a l e í d a
I
catch (IOException ignorada) I I
)
I
PrintStream
p r i n t ( t i p o a r g u m e n t o ):
p r i n t l n ([tipo a r g u m e n t o ] ) ;
Los m étodos p rin t y p r in tln son esencialm ente los m ism os; am bos escriben
su argum ento en el flujo de salida. La única diferencia entre ellos es que println
añade un carácter 'W (avance a la línea siguiente) al final de su salida, y print
no. En otras palabras, la siguiente sentencia:
Los argum entos para p r in t y p rin tln pueden ser de cualquier tipo prim itivo o
referenciado: O bject, S trin g . char[], int, long. float, double, y boolean. En adi
ción, hay u na versión extra de p rin tln que no tiene argum entos y lo que hace es
escrib ir un carácter ‘Vi’, lo que se traduce en un avance a la línea siguiente.
C om o ejem plo, la siguiente aplicación utiliza p rin tln para escribir datos de
varios tipos en la salida estándar.
public c la s s TestTiposDatos
I
// T i p o s de d a t o s
pu b lic s t a t ic void m a in (S trin g [] args)
I
S t r i n g sCadena = " L e n g u a j e J a v a ” ;
c h a r [ ] c M a t r i z C a r s = I ' a ' , ' b ' , ’ c ’ I : // m a t r i z d e c a r a c t e r e s
i n t d a t o _ i n t = 4;
long dato_lon g = Long.MIN_VALUE: // m í n i m o v a l o r l o n g
f l o a t d a t o _ f l o a t = F l o a t . M A X _ V A L U E ; // máxi mo v a l o r f l o a t
double dato_double = M ath.Pl; // 3 . 1 4 1 5 9 2 6
boolean dato_boolean = true:
System .out.println(sCadena);
S y s t e m . o u t . p r i n t l n ( c M a t r i z C a r s ):
S y s t e m . o u t . p r i n t l n ( d a t o _ i n t ):
System .out.println(dato_long):
S y s t e m . o u t . p r i n t l n ( d a t o _ f 1o a t ):
System .out.println(dato_double);
S y s t e m . o u t . p r i n t l n ( d a t o _ b o o 1e a n );
L enguaje Java
abe
4
-9223372036854775808
3 . 4028235E38
3.141592653589793
true
102 JA V A: C U R SO D E PROGRAM A CIÓN
P r i n t W r i t e r f l u j o S = new P r i n t W r i t e r ( S y s t e m . o u t ) :
i n t d a t o _ i n t = 4;
f 1 u j o S . p r i n t l n ( d a t o _ i n t ) : f 1u j o S . f 1 u s h ( ) ;
En la figura anterior se pueden observar en color gris las clases com entadas
en este capítulo; las coloreadas en gris m ás oscuro son clases abstractas (una línea
discontinua indica que esa clase no se deriva directam ente de O b ject; esto es, en
tre O b je c t y la clase hay otras clases que no tienen interés para el tem a que esta
mos tratando).
D espués de analizar la jerarq u ía de clases para, entre otras cosas, llegar a ver
la procedencia de los flujos in y out, se deduce que para leer del flujo in sólo se
dispone de m étodos que proporcionan un carácter, o bien una m atriz de caracte
res; para leer una cadena de caracteres del flujo in y alm acenarla en un objeto
S t r in g lo tenem os que hacer desde un flujo de la clase B u ffe re d R e a d e r; y para
escribir en el flujo o u t tenem os los m étodos proporcionados por la clase P r in t S -
tream , o bien P rin tW r ite r. que perm iten escribir cualquier valor de cualquier ti
po prim itivo o referenciado.
Evidentem ente, cualquier operación aritm ética requiere de valores num éricos;
pero, según lo expuesto, en el m ejor de los casos sólo se puede obtener una cade
na de bytes. El código siguiente pertenece a la aplicación LeerU naC adena reali
zado anteriorm ente:
f l u j o S . p r i n t ( " I n t r o d u z c a un t e x t o : "):
s d a t o = f l u j o E . r e a d L i n e ( ) : // l e e r una l í n e a de t e x t o
El código anterior perm ite leer del flujo in una cadena de caracteres que será
alm acenada en el objeto sdato de tipo S trin g . Por ejem plo, si cuando se ejecute el
m étodo re a d L in e se teclea el dato 45 6 , estos dígitos serán alm acenados en sdato
com o una cadena de caracteres. A hora bien, para que esa cadena de tres caracteres
pueda ser utilizada en una expresión aritm ética, tiene que adquirir la categoría de
valor num érico, lo que im plica convertirla a un valor de alguno de los tipos pri
m itivos. Esto puede hacerse utilizando los m étodos proporcionados por las clases
que encapsulan los tipos prim itivos.
A tributo Descripción
M IN J V A L U E V alor m ás pequeño de tipo int.
M AXJVALUE V alor m ás grande de tipo int.
M étodo Descripción
d o u b lc V a lu e O D evuelve el objeto In te g e r com o un valor double.
flo a tV a lu e O D evuelve el objeto In te ge r com o un valor float.
in tV a lu e O D evuelve el objeto In te ge r com o un valor int.
lo n g V a lu e O D evuelve el objeto In te ge r com o un valor long.
p a rse ln t(S trin g ) C onvierte una cadena a un valor int.
to Strin g(in t) C onvierte un valor int en una cadena (objeto Strin g).
v a lu e O f(S trin g ) C rea un objeto In te ge r a partir de una cadena.
Atributo D escripción
M IN J V A L U E V alor m ás pequeño de tipo float.
M AXJVALUE V alor m ás grande de tipo float.
NaN N o es un N úm ero; de tipo float.
N E G A T IV E _ IN F IN IT Y V alor infinito negativo de tipo float.
P O S IT IV E J N F IN IT Y V alor infinito positivo de tipo float.
De acuerdo con lo expuesto, para obtener, por ejem plo, un entero a partir de
una cadena de caracteres proporcionada por re a d L in e habrá que ejecutar los si
guientes pasos:
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) ;
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r ( i s r ) ;
C A PÍTU LO 5: CL A SES DE USO COM ÚN 10 5
S tr in g sdato; // v a r i a b l e p a r a a l m a c e n a r una c a d e n a
int dato_int; // v a r i a b l e p a r a a l m a c e n a r un e n t e r o
try
I
s d a t o = f l u j o E . r e a d L i n e ( ) : // l e e r una c a d e n a de c a r a c t e r e s
d a t o _ i n t = I n t e g e r . p a r s e l n t ( s d a t o ) : // c o n v e r t i r a e n t e r o
I
catch (IOException ignorada) I I
En el ejem plo anterior se observa que una vez leída la cadena sd a to , que se
supone es una cadena válida para ser convertida en un entero, se invoca al m étodo
estático p a rse ln t para convertir el objeto S t r in g en un dato de tipo int.
A nálogam ente, para convertir una cadena de bytes que representa un núm ero
con punto decim al, en un valor de tipo float, el código sería el siguiente:
s d a t o = f 1u j o E . r e a d L i n e ( ) ; // l e e r una c a d e n a de c a r a c t e r e s
F l o a t f = new F 1 o a t ( s d a t o ) ; II c r e a r un o b j e t o F l o a t
f l o a t d a t o _ f l o a t = f . f l o a t V a l u e ( ): // o b t e n e r e l v a l o r f l o a t
Según lo expuesto, podem os escribir un m étodo dato que lea una cadena de
caracteres desde el teclado, la alm acene en un objeto S t r in g y devuelva com o re
sultado dicho objeto.
S t r i ng d a t . o ( )
I
S t r in g sdato = " " :
try
I
// D e f i n i r un f l u j o de c a r a c t e r e s d e e n t r a d a : f l u j o E
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) ;
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r í i s r );
// L e e r . La e n t r a d a f i n a l i z a al p u l s a r l a t e c l a E n t r a r
s d a t o = f l u j o E . r e a d L i n e ( ):
I
catchíIOException e)
I
S y ste m .e rr.p rin tln C 'E rro r: " + e.getM essage()):
1
return sdato; // d e v o l v e r e l dato tecleado
1 0 6 JA V A: C U R S O D E PROGRAM A CIÓN
int datolnt()
I
String sdato = dato (): // i n v o c a al mé t o d o d a t o
return I n t e g e r . p a r s e l n t ( s d a t o ) ; // c o n v i e r t e s d a t o en un i n t
I
int datolntt)
I
try
I
return I n t e g e r . p a r s e l n t ( d a t o ( )):
1
c a t c h ( N u m b e r F o r m a t E x c e p t i o n e)
I
return Integer.MIN_VALUE; II v a l o r más p e q u e ñ o de t i p o int
O bserve que el argum ento del m étodo p a rse ln t es la cadena de caracteres de
vuelta p o r el m étodo dato. Si ocurre un error, por ejem plo, porque se introduce
una cadena que no es convertible a un núm ero entero, el sistem a lanzará una ex
cepción de tipo N u m b e rF o rm a tE x c e p tio n que será atrapada por el bloque catcli
lo que dará lugar a que el m étodo d a to ln t devuelva el valor M IN _V A L U E , defini
do com o una constante de la clase.
A nálogam ente, podem os escribir otros m étodos para convertir una cadena
válida, devuelta por el m étodo dato, en otros tipos de datos prim itivos. Agrupe
mos todos estos m étodos en una clase denom inada Leer.
Clase Leer
El objetivo es escribir una clase L eer que incluya com o m iem bros, además de los
m étodos que hem os venido im plem entando anteriorm ente, otros m étodos, de ma
CA PÍTU LO 5: C L A S E S DE U SO C O M Ú N 107
ñera que todos ju n to s proporcionen una interfaz que cualquier program a puede
utilizar para obtener del teclado datos de cualquier tipo prim itivo. El código que
define esta clase se m uestra a continuación.
import j a v a . i o . * :
En la clase Leer, se puede observar que todos los m étodos, adem ás de públi
cos, se han declarado static con el fin de que puedan ser invocados allí donde se
necesiten, sin necesidad de que exista un objeto de la clase. R ecuerde que la sin
taxis para invocar a un m étodo de una clase es:
Una vez escrita la clase Leer, podem os utilizarla com o soporte para otras
aplicaciones. C om o ejem plo, vam os a escribir una aplicación que lea un dato de
cada uno de los tipos contem plados en L eer y m uestre después los valores leídos.
C A PÍTU LO 5: CL A SES DE USO C O M Ú N 109
R ecuerde que para que la clase aplicación que vam os a escribir pueda utilizar la
clase Leer, deben estar am bas en la m ism a carpeta de trabajo.
// U t i l i z a l a c l a s e L e e r q u e d e b e d e e s t a r al macenada
// en l a mi s ma c a r p e t a
S y s t e m . o u t . p r i n t ( " D a t o s h o r t : ” ):
d a t o _ s h o r t = L e e r . d a t o S h o r t t ):
S y s t e m . o u t . p r i n t ( " D a t o i n t : ” );
d a t o _ i n t = L e e r . d a t o l n t t ):
S y s t e m . o u t . p r i n t C D a t o l ong : “ ):
d a t o _ l o n g = L e e r . d a t o L o n g í );
System.out.printí"Dato float: "):
d a t o _ f l o a t = L e e r . d a t o F l o a t t ):
S y s t e m . o u t . p r i n t í " D a t o double: " ) :
d a t o _ d o u b l e - L e e r . d a t o D o u b l e ( ):
System.out.println(dato_short):
System.out.println(dato_int):
System.out.println(dato _ lo ng);
System.out.println(dato_float);
System.out.println(dato_double):
D espués del trabajo realizado, ya tenem os una form a de leer datos num éricos
introducidos a través del teclado. Esto nos perm itirá escribir diversas aplicaciones
que requieren de este proceso. A dem ás, sabem os tam bién cóm o convertir núm e
ros a cadenas de caracteres y viceversa.
Variable CLASSPATH
C uando en el código fuente de un program a se hace referencia a una clase que no
pertenece a un paquete que se pueda im portar, com o ocurre con la clase Leer, Ja
va busca por ella en el directorio actual si la variable C LA SSP A TH no ha sido es
tablecida. En o tro caso busca en las rutas especificadas p o r esta variable.
CLASSPATH=c: . ; \ j d k 1 . 3 \ m i s C l a s e s
El ejem plo anterior indica a Java que busque las clases a las que haga referen
cia un determ inado program a, adem ás de en los paquetes im portados, en la car
peta actual de trabajo (.) o en la carpeta jdkl.S^m isC lases.
Recuerde que cuando el m étodo read intenta leer y se encuentra con el final
del flujo, retorna la constante -1 . A nálogam ente, cuando el m étodo re a d L in e in
tenta leer del flujo y se encuentra con el final del m ism o, retom a la constante nuil.
Para aclarar lo expuesto, el siguiente ejem plo solicita del teclado un dato precio.
Entonces, si al m ensaje “P recio:” respondem os escribiendo una cantidad, la varia
ble precio alm acenará ese valor, pero si respondem os pulsando las teclas C trl+Z
(carácter fin de fichero), deberá alm acenar el valor N a N de tipo float.
i mpor t j a v a . i o . * ;
S t r i n g sdato:
f l o a t pre cio = 0.0F:
try
I
f l u j o S . p r i n t ( " P r e c i o : ” ):
s d a t o - f l u j o E . r e a d l i n e ( ):
precio - (sdato != n u i l )
? ( new F 1 o a t ( s d a t o ) ) . f l o a t V a l u e ( )
: Float.NaN:
I
catch (lOException ignorada)! 1
flujoS.println(precio):
f l u j o S . p r i n t l n ( " C o n t i n u a la a p l i c a c ió n " ) :
C uando ejecute esta aplicación puede proceder de cualquiera de las dos for
m as siguientes:
P re c i o: 123.45
123.45
Continua la a p l i c a c i ó n
1 1 2 JA V A: C U R SO DE PRO G R A M A CIÓ N
F l o a t f - new F l o a t ( s d a t o ) :
precio - f.flo a tV a lu e O :
CARACTERES \r\n
C uando se están introduciendo datos a través del teclado y pulsam os la tecla En
tra r se introducen tam bién los caracteres W t . correspondientes a los caracteres
ASCII CR L F (C R es el A SCII 13 y L F es el A SC II 10). M ientras que en la salida
Vi produce un C R+ LF, en la entrada se corresponde con un L F \ esto es. una ex
presión Java com o “V i' = = 10 daría com o resultado true.
Por ejem plo, suponiendo definidos los flujos flu jo E y flu jo S igual que en el
ejem plo anterior, el código siguiente lee un carácter:
char opción:
try
I
f 1u j o S . p r i n t ( " O p c i ó n (a, b o c) : ” ):
o p c i ó n - ( c h a r ) f 1u j o E . r e a d ( ) :
1
catch (IOException ignorada)! 1
b [ E n tr a r ]
CA PÍTU LO 5: CLASES DE USO C O M Ú N 1 13
| b 1 \ r | \n | | | | | |
y después de la lectura:
1 \ r | \n |
ya que read lee un solo carácter. Estos caracteres sobrantes pueden ocasionarnos
problem as si a continuación se ejecuta otra sentencia de entrada que adm ita datos
que sean caracteres. Por ejem plo:
char opción;
S t r i n g sdato:
try
I
f l u j o S . p r i n t ( "Opci ón (a, b o c ) : "):
o p c i ó n = ( c h a r ) f l u j o E . r e a d ( );
Si ejecutam os esta aplicación y tecleam os, por ejem plo, com o opción b segui
da de la pulsación de la tecla Entrar, se producirá el siguiente resultado:
El m étodo sk ip perm ite saltar n caracteres en el flujo de entrada para que no estén
presentes en la próxim a operación de lectura; y el m étodo a v a ila b le devuelve el
núm ero de caracteres que hay disponibles en el flujo de entrada. Por ejem plo:
char opción:
int ncars;
S t r i n g sdato:
try
I
f l u j o S . p r i n t ( " O p c i ó n (a. b o c ) : ");
o p c i ó n = ( c h a r ) f l u j o E . r e a d ( ):
n c a r s - f 1 u j o E . a v a i 1
a b l e ( ) : / / c a r a c t e r e s d i s p o n i b l e s
f l u j o E . s k i p ( n c a r s ) : / / s a l t a r l o s c a r a c t e r e s C R L F
Un buffer se lim pia autom áticam ente cuando está lleno, cuando se cierra el
flujo, o bien cuando el program a finaliza norm alm ente.
MÉTODOS MATEMÁTICOS
La biblioteca de clases de Java incluye una clase llam ada M a t h en su paquete ja-
va.lang, la cual define un conjunto de operaciones m atem áticas de uso com ún que
pueden ser utilizadas por cualquier program a.
La tabla siguiente resum e los m iem bros de la clase M a t h . T odos los miem
bros de esta clase son static para que puedan ser invocados sin necesidad de defi
nir un objeto de la clase.
M étodo Descripción
static double E V alor del núm ero e (base del logaritm o nepe-
riano o natural).
CA PÍTU LO 5: CLASES DE USO C O M Ú N 1 15
EJERCICIOS RESUELTOS
1. R ealizar una aplicación que dé com o resultado los intereses producidos y el capi
tal total acum ulado de una cantidad c, invertida a un interés r durante t días.
c* r* t
7 = 360*100
siendo:
/ = Total de intereses producidos.
c = C apital.
r = T asa de interés nom inal en tanto por ciento.
/ = Período de cálculo en días.
• Prim ero definim os las variables que vam os a u tilizar en los cálculos.
• C onocidos los datos, realizam os los cálculos. N os piden los intereses produci
dos y el capital acum ulado. Los intereses producidos los obtenem os aplicando
directam ente la fórm ula. El capital acum ulado es el capital inicial m ás los in
tereses producidos.
in te re se s = c * r * t / (360 * 100):
capital = c + intereses:
ENTRADA PROCESO S A L ID A |
// La c l a s e L e e r d e b e e s t a r en a l g u n a c a r p e t a de l a s especificadas
// p o r l a v a r i a b l e de e n t o r n o C L A S S P A T H .
public class CIntereses
I
public static void m a in ( S trin g [] args)
I
d o u b l e c. i n t e r e s e s , c a p i t a l :
f l o a t r;
i n t t:
System.out.print("Capital invertido: "):
c = L e e r . d a t o D o u b l e í );
S y s t e m . o u t . p r i n t í ” \ n A un % a n u a l d e l : ” );
r = L e e r . d a t o F l o a t í );
S y s t e m . o u t . p r i n t í "\nDurante cuántos d ías : ");
t = L e e r . d a t o l n t í );
i n te re s e s = c * r * t / (360 * 100):
capi ta l = c + i n t e r e s e s :
ax2 + bx + c = O
- b ± y l b : —4 a c
1 18 JAVA: C U R SO DE PROGRAM A CIÓN
d o u b l e a. b, c. d. xl, x2:
System.out.print("Coeficiente a: ” ) : a - L e e r , d a t o D o u b l e ( );
System.out.printí"Coeficiente b: " ) : b = L e e r . d a t o D o u b l e ( ):
System.out.print("Coeficiente c: " ) : c - L e e r . d a t o D o u b l e ( );
• N os piden calcular las raíces reales. Para que existan raíces reales tiene que
cum plirse que b2-4 a c > 0; si no, las raíces son com plejas conjugadas. E nton
ces, si hay raíces reales las calculam os; en otro caso, salim os de la aplicación.
Para salir de una aplicación, en general para salir de un proceso sin hacer na
da m ás. Java proporciona la sentencia re tu rn .
d = b * b - 4 * a * c :
i f (d < 0)
I
// S i d e s menor q u e 0
System.out.printlní"Las raíces son c o m p l e j a s . " ) ;
r e t u r n : // s a l i r
I
II S i d e s ma y o r o i g u a l que 0
System.out.printlní"Las raíces reales son:"):
d = Math.sqrt(d):
x l = ( - b + d) / ( 2 * a ) :
x2 = ( - b - d) / ( 2 * a ) :
/ / ■ L a c l a s e L e e r d e b e e s t a r en a l g u n a c a r p e t a de l a s e s p e c i f i c a d a s
// p o r l a v a r i a b l e d e e n t o r n o C L AS S P A T H.
pu b li c c l a s s CEcuacion
I
public s t a t i c void m a in (S trin g [] args)
i
d o u b l e a. b. c, d, x l . x2:
System.out.print("Coeficiente a: " ) : a = L e e r . d a t o D o u b l e ( ):
System, out . p r i n t C C o e f i c i e n t e b: ” ): b = L e e r . d a t o O o u b l e ( ):
S y s t e m . o u t .p r i n t ( " C o e f i c i e n t e c : “ ) ; c - L e e r , d a t o O o u b l e ( ):
d - b * b - 4 * a * c ;
i f ( d < 0)
í
// S i d e s me n o r que 0
System.out.printlní"Las raíces son co m pl ej a s . ") ;
r e t u r n : // s a l i r
I
// S i d e s ma y o r o i g u a l q u e 0
S y s t e m . . o u t . p r i n t l n ( “ L a s r a í c e s r e a l e s s o n : ” );
d - Math.sqrt(d);
x l - ( - b + d) / ( 2 * a ) ;
x 2 - ( - b - d) / ( 2 * a ) ;
S y s t e m . o u t . p r i n t l n C ' x l = " + x l + " . x2 = " + x 2 ) ;
EJERCICIOS PROPUESTOS
1. Realizar una aplicación que calcule el volum en de una esfera, que viene dado por
la fórmula:
4 3
v =-n r
2. R ealizar una aplicación que pregunte el nom bre y el año de nacim iento y dé com o
resultado:
H o l a n o mb r e , en e l año 2030 t e n d r á s n años
char c a r i = ’A ’ , c a r 2 - 65, c a r 3 = 0:
SENTENCIAS DE CONTROL
C ada m étodo de las aplicaciones que hem os hecho hasta ahora, era un conjunto de
sentencias q ue se ejecutaban en el orden en el que se habían escrito, entendiendo
por sentencia una secuencia de expresiones que especifica una o varias operacio
nes. Pero esto no es siem pre así; seguro que en algún m om ento nos ha surgido la
necesidad de ejecutar unas sentencias u otras en función de unos criterios especi
ficados p o r nosotros. Por ejem plo, en el capítulo anterior, cuando calculábam os
las raíces de una ecuación de segundo grado, vim os que en función del valor del
discrim inante las raíces podían ser reales o com plejas. En un caso com o éste, sur
ge la necesidad de que sea el propio program a el que tom e la decisión, en función
del valor del discrim inante, de si lo que tiene que calcular son dos raíces reales o
dos raíces com plejas conjugadas.
En este capítulo aprenderá a escribir código para que un program a tom e deci
siones y para que sea capaz de ejecutar bloques de sentencias repetidas veces.
SENTENCIA if
La sentencia if perm ite a un program a tom ar una decisión para ejecutar una ac
ción u otra, basándose en el resultado verdadero o falso de una expresión. La sin
taxis para utilizar esta sentencia es la siguiente:
122 JA VA : C U R SO D E PROGRAM A CIÓN
if ( condición)
s e nt e n c i a 1;
[ e l se
s e nt e n c i a 2];
1. Se evalúa la condición.
A continuación se exponen algunos ejem plos para que vea de una form a sen
cilla cóm o se utiliza la sentencia if.
if ( x ! = 0)
b = a / x;
b = b + 1:
i f ( a < b ) c = c + 1;
// s i g u i e n t e l i n e a d e l p r o g r a ma
En este otro ejem plo, la condición viene im puesta por una expresión a < b. Si
al evaluar la condición se cum ple que a es m enor que b, entonces se ejecuta la
sentencia c = c + I. En otro caso, esto es, si a es m ayor o igual que b, se continúa
en la línea siguiente, ignorándose la sentencia c = c + 1.
C A PÍTU LO 6: SEN TEN CIA S D E C O N TR O L 12 3
if (a ! - 0 U b ! - 0)
x « 1;
// s i g u i e n t e l i n e a d e l p r o g r a ma
En el ejem plo siguiente, si se cum ple que a es igual a b*5, se ejecutan las
sentencias x = 4 y a = a + x . En otro caso, se ejecuta la sentencia b = 0. En am
bos casos, la ejecución continúa en la siguiente línea de program a.
if (a — b * 5)
I
x “ 4;
a - a + x;
I
else
b = 0;
// s i g u i e n t e línea del p r o g r a ma
Un error típico es escribir, en lugar de la condición del ejem plo anterior, la si
guiente:
if (a - b * 5)
// ...
a - b * 5:
i f (a)
// ...
donde se observa que a no puede dar un resultado b o o lean . Sí sería correcto lo si
guiente:
a = b * 5:
i f ( a ! “ 0)
II...
que equivale a:
if ((a - b * 5) ! - 0)
II ...
1 2 4 JA V A : C U R SO D li PROGRAM A CIÓN
ANIDAMIENTO DE SENTENCIAS if
O bservando el form ato general de la sentencia if cabe una pregunta: ¿cóm o sen
tencia / o sentencia 2 se puede escribir otra sentencia if? La respuesta es sí. Esto
es, las sentencias i f ... else pueden estar anidadas. Por ejem plo:
if ( c o n d i c i ó n 1)
I
if ( c o n d i c i ó n 2)
s en t e n c i a ¡:
I
else
s e nt e n c i a 2:
En el ejem plo anterior las llaves definen perfectam ente que la cláusula else
está em parejada con el p rim er if. ¿Q ué sucede si quitam os las llaves?
if (c o n d i c i ó n 1)
i f (c o n d i c i ó n 2)
s e n t e nc i a 1;
else
s en t e n c i a 2;
C om o ejem plo se puede observar el siguiente segm ento de program a que es
cribe un m ensaje indicando cóm o es un núm ero a con respecto a otro b (m ayor,
m enor o igual):
i f (a > b)
flujoS.printlnía + " e s mayor que " + b ) ;
el s e i f ( a < b)
flujoS.printlnía + “ e s me n o r que " + b ):
el se
flujoS.printlnía + " es i g u a l a " + b):
// s i g u i e n t e l i n e a de l programa
Es im portante observar que una vez que se ejecuta una acción com o resultado
de haber evaluado las condiciones im puestas, la ejecución del program a continúa
en la siguiente línea a la estructura a que dan lugar las sentencias i f ... else anida
das. En el ejem plo anterior si se cum ple que a es m ayor que b, se escribe el m en
saje correspondiente y se continúa en la siguiente línea del program a.
i f (a - » 0)
i f ib != 0)
s ** s + b:
el se
s = s + a:
// s i g u i e n t e l i n e a del programa
i f (a — 0)
1 2 6 JA V A: CU R SO DE PRO G R A M A CIÓ N
if ( b ! - 0)
s = s + b;
I
el se
s = s + a;
// s i g u i e n t e l í n e a del programa
// La c l a s e L e e r d e b e e s t a r en a l g u n a c a r p e t a de l a s especificadas
// p o r l a v a r i a b l e de e n t o r n o C L A S S PA TH .
//
public c la ss C Menor
I
// M e n o r d e t r e s n ú m e r o s a. b y c
// L e e r l o s v a l o r e s de a .b y c
S y s t e m . o u t . p r i n t ( "a : "); a = L e e r . d a t o F l o a t ( );
System .out.print("b : ') ; b = L e e r . d a t o F l o a t ( );
System .out,print("c : "); c - L e e r . d a t o F l o a t ( );
// O b t e n e r e l menor
i f ( a < b)
i f (a < c)
me n o r = a;
el se
menor = c ;
el se
i f (b < c)
me n o r = b;
el se
menor = c;
S y s t e m . o u t . p r i n t l n í " M e n o r = " + menor):
ESTRUCTURA else if
La estructura presentada a continuación, aparece con bastante frecuencia y es por
lo que se le da un tratam iento por separado. E sta estructura es consecuencia de las
sentencias if anidadas. Su form ato general es:
CA PÍTU LO 6: SEN TEN CIA S DE CO N TRO L 127
if ( c on d i c i ó n 1 )
s en t e n c i a 1:
e l s e i f ( c o n d i c i ó n 2)
s en t e n c i a 2:
e l s e i f ( c o n d i c i ó n 3)
s en t e n c i a 3;
el se
s e n t e n c i a n;
P o r ejem plo, al efectuar una com pra en un cierto alm acén, si adquirim os más
de 100 unidades de un m ism o artículo, nos hacen un descuento de un 4 0 %; entre
25 y 100 un 2 0 %; entre 10 y 24 un 10 % ; y no hay descuento para una adquisi
ción de m enos de 10 unidades. Se pide calcular el im porte a pagar. La solución se
presentará de la siguiente forma:
Código a r t í c u l o 111
Cantidad comprada 100
Precio u n it a r i o 1 00
D e s c u e n t o ....................... 2 0 . 0 %
T o t a l .............................. 8 0 0 0 . 0
En la solución presentada com o ejem plo, se puede observar que com o la can
tidad com prada está entre 25 y 100, el descuento aplicado es de un 20% .
• Prim ero definim os las variables que vam os a utilizar en los cálculos.
i n t a r , cc:
f l o a t pu. d e s c ;
a r = L e e r . d a t o l n t í ):
S y s t e m . o u t . p r i n t ( " C a n t i d a d comprada "):
c c = L e e r , d a t o I n t ( ):
S y s t e m . o u t . p r i n t ( " P r e c i o u n i t a r i o ............ ");
pu - L e e r . d a t o F l o a t í ):
if ( c c > 100)
desc - 4 0 F ; I I descuento 40%
e l s e i f ( c c > - 25)
desc = 2 0 F ; // d e s c u e n t o 20%
e l s e i f ( c c > - 10)
desc = 10F : // d e s c u e n t o 10%
el se
desc = 0.0F; // d e s c u e n t o 0%
S y s t e m , o u t . p r i n t l n( “ D e s c u e n t o ........................ ” + d e s c + " % " ) :
S y s t e m . o u t . p r i n t l n( " T o t a l ................................ " +
cc * pu * (1 - d e s c / 1 0 0 ) ) :
Se puede observar que las condiciones se han establecido según los descuen
tos de m ayor a m enor. C om o ejercicio, piense o pruebe que ocurriría si establece
las condiciones según los descuentos de m enor a m ayor. La aplicación com pleta
se m uestra a continuación.
// La c l a s e L e e r d e b e e s t a r en a l g u n a c a r p e t a de l a s especificadas
// p o r l a v a r i a b l e de e n t o r n o C L A S S P A T H .
//
public class CDescuento
(
public static void m a in (S trin g [] args)
I
i n t a r . cc:
f l o a t pu. d e s c :
if (cc >1 0 0 )
desc = 40 F ; // d e s c u e n t o 40%
el s e i f ( c c > - 25)
desc = 20F; // d e s c u e n t o 20%
else i f ( c c > - 10)
desc = 10F ; // d e s c u e n t o 10%
CA PÍTU LO 6: SEN TEN CIA S DE C O N TR O L 129
el se
desc - 0.0F; // d e s c u e n t o 0%
S y s t e m , o u t . p r i n t l n( " D e s c u e n t o •............. " + d e s c +
S y s t e m . o u t . p r i n t l n t " T o t a l ................................ " +
c c * pu * (1 - d e s c / 1 0 0 ) ) :
SENTENCIA switch
La sentencia sw itch perm ite ejecutar una de varias acciones, en función del valor
de una expresión. Es una sentencia especial para decisiones m últiples. La sintaxis
para utilizar esta sentencia es:
switch (e x p r e s i ó n )
¡
c a se e x p r e s i ó n - c o n s t a n t e 1:
[ s e n t e n c i a 1;]
[case e x p r e s i ó n - c o n s t a n t e 2 : ]
[ s e n t e n c ia 2:]
[case e x p r e s i ó n - c o n s t a n t e 3 :]
[ s e n t e n c i a 3;]
[default: ]
[sentencia n:]
1
switch (m)
i n t n = 0, k = 2; // d e c l a r a c i ó n no p e r m i t i d a
c a s e 7:
/ aración permitida
while ( i < m )
n += ( k + i ) * 3:
i++:
break;
c a s e 13:
// . . .
break:
// . . .
i n t n - 0, k “ 2 ; ~
s w i t c h (m)
1
I I ...
I
Para ilustrar la sentencia sw itch , vam os a realizar un program a que lea una
fecha representada por dos enteros, m es y año, y dé com o resultado los días co
rrespondientes al m es. Esto es:
I n t r o d u c i r mes ( # # ) y a ñ o ( # # # # ) : 5 2 0 0 2
El mes 5 d e l a ñ o 2 0 0 2 t i e n e 31 d i a s
H ay que tener en cuenta que febrero puede tener 28 días, o bien 29 si el año
es bisiesto. Un año es bisiesto cuando es m últiplo de 4 y no de 100 o cuando es
m últiplo de 400. Por ejem plo, el año 2000 p o r las dos prim eras condiciones no se
ría bisiesto, pero sí lo es porque es m últiplo de 400; el año 2100 no es bisiesto
porque aunque sea m últiplo de 4, tam bién lo es de 100 y no es m últiplo de 400.
• Prim ero definim os las variables que vam os a utilizar en los cálculos.
i n t d í a s = 0. mes - 0. a ñ o = 0:
CA PÍTU LO 6: SEN TEN CIA S D E C O N TR O L 131
S y s t e m , o u t . p r i n t C M e s ( # # ) : " ) : mes - L e e r . d a t o l n t í );
S y s t e m , o u t . p r i n U " A ñ o (////////): " ) : a ñ o - L e e r . d a t o I n t ( ):
switch ( mes )
1
c a s e 1: c a s e 3: c a s e 5: c a s e 7: c a s e 8 : c a s e 1 0 : c a s e 12:
d í a s - 31:
break;
c a s e 4: c a s e 6: c a s e 9: c a s e 11:
d í a s - 30:
break:
c a s e 2:
// ¿ E s e l a ñ o b i s i e s t o ?
i f ( ( a ñ o % 4 — 0 ) && ( a ñ o % 100 ! - 0 ) || ( a ñ o % 4 0 0 - - 0))
d i a s - 29:
else
d í a s - 28:
break;
default:
S y s t e m . o u t . p r i n t l n ( " \ n E l mes no e s v á l i d o ’ ):
break;
I
C uando una constante coincida con el valor de mes, se ejecutan las sentencias
especificadas a continuación de la m ism a, siguiendo la ejecución del progra
ma por los bloques de las siguientes cláusulas case, a no ser que se tom e una
acción explícita para abandonar el bloque de la sentencia sw itch. Ésta es pre
cisam ente la función de la sentencia b re a k al final de cada bloque case.
// La c l a s e L e e r d e b e e s t a r en a l g u n a c a r p e t a d e las especificadas
// p o r l a v a r i a b l e de e n t o r n o C L A S S P A T H .
//
public class CDiasMes
I
// D i a s correspondientes a un mes de un a ñ o da do
switch ( mes )
i
c a s e 1: // e n e r o
c a s e 3: II ma r z o
c a s e 5: II mayo
c a s e 7: II j u l i o
c a s e 8: // a g o s t o
c a s e 10: // o c t u b r e
c a s e 12: 1/ d i c i embre
d í a s = 31:
break:
c a s e 4: II a b r i 1
c a s e 6: II j u n i o
c a s e 9: II s e p t i e m b r e
c a s e 11: II n o v i e m b r e
d i a s = 30:
break:
c a s e 2: II f e b r e r o
// ¿ E s e l añ o b i s i e s t o ?
i f ( ( a ñ o % 4 — 0 ) && ( a ñ o % 1 00 ! = 0 ) || ( a ñ o % TOO — 0))
d í a s = 29;
el se
d i a s - 28:
break;
default:
S y s t e m . o u t . p r i n t l n ( “ \ n E l mes no e s v á l i d o " ) :
break:
1
if ( mes > = 1 && mes < = 12)
S y s t e m . o u t . p r i n t l n í " \ n E l mes " + mes + " d e l a ñ o " + añ o +
” tie n e “ + d ias + " días"):
E l que las cláusulas case estén una a continuación de otra o una debajo de
otra no es m ás que una cuestión de estilo, ya que Java interpreta cada carácter
CA PÍTU LO 6: SEN TEN CIA S DE CO N TRO L 133
nueva línea com o un espacio en blanco; esto es, el código al que llega el com pila
dor es el m ism o en cualquier caso.
SENTENCIA while
La sentencia w hile ejecuta una sentencia, sim ple o com puesta, cero o m ás veces,
dependiendo del valor de una expresión booleana. Su sintaxis es:
while ( condición)
sentencia:
Por ejem plo, el siguiente código, que podrá ser incluido en cualquier aplica
ción, solicita obligatoriam ente una de las dos respuestas posibles: s/n (sí o no).
char car = 1\ 0 ’ :
try
I
S y s t e m . o u t . p r i n t t " V n D e s e a c o n t i n u a r s / n ( s í o n o ) “ );
w h i l e ( ( c a r - ( c h a r ) S y s t e m . i n . r e a d ( ) ) ! - ’ s ' && c a r ! = ’n ’ )
i
// S a l t a r l o s c a r a c t e r e s d i s p o n i b l e s en e l f l u j o de e n t r a d a
S y s t e m . i n . s k i p ( S y s t e m . i n . a va i 1 a b l e ( ) ) ;
S y s t e m . o u t . p r i n t t " \ n D e s e a c o n t i n u a r s / n ( s í o no) " ) ;
catch(IOException ignorada) (I
La diferencia de este ejem plo con respecto al anterior es que ahora la condi
ción incluye la lectura de la variable ca r, que se ejecuta prim ero p o r estar entre
paréntesis. A continuación se com para ca r con los caracteres V y V .
El siguiente ejem plo, que visualiza el código A SCII de cada uno de los ca
racteres de una cadena de texto introducida por el teclado, da lugar a un bucle in
finito, porque la condición es siem pre cierta (valor true). Para salir del bucle infi
nito tiene que pulsar las teclas Ctrl+C.
i mpor t j a v a . i o . *:
try
I
S y s t e m . o u t . p r i n t t " I n t r o d u z c a una c a d e n a de t e x t o : "):
w h i l e ( t r u e ) // c o n d i c i ó n s i e m p r e c i e r t a
C A PÍTU LO 6: SEN TEN CIA S DE CO N TRO L 135
c a r - ( c h a r ) S y s t e m . i n . r e a d ( ) : // l e e r e l s i g u i e n t e c a r á c t e r
i f ( c a r ! = ’ \ r * && c a r !=* ' \ n ' )
S y s t e m . o u t . p r i n t l n ( " E l c ó d i g o A S C I I de " + c a r +
" es " + ( i n t l c a r ) :
// S i no h a y d a t o s d i s p o n i b l e s , s o l i c i t a r l o s
i f ( S y s t e m . i n . a v a i 1a b l e ( ) - - 0 )
S y s t e m . o u t . p r in t t " Introduzca texto: "):
I
I
catch(IOException ignorada) II
I n t r o d u z c a una c a d e n a de t e x t o : afEntrar]
El c ó d i g o A S C I I de a e s 97
I n t r o d u z c a una c a d e n a d e t e x t o :
El resultado obtenido perm ite observar que el bucle w hile se está ejecutando
sin pausa m ientras hay caracteres en el flujo de entrada. C uando dicho flujo queda
vacío y se ejecuta el m étodo read de nuevo, la ejecución se detiene a la espera de
nuevos datos.
M odifiquem os ahora el ejem plo anterior con el objetivo de elim inar el bucle
infinito. Esto se puede hacer incluyendo en el w hile una condición de term ina
ción; por ejem plo, leer datos hasta alcanzar la m arca de fin de fichero. Recuerde
que para el flujo estándar de entrada, esta m arca se produce cuando se pulsan las
teclas C trl+ D en U N IX , o bien C trl+ Z en aplicaciones W indow s de consola, y
que cuando read lee una m arca de fin de fichero, devuelve el valor -1.
1 3 6 JA V A: C U R SO DE PROGRAM A CIÓN
i mport j a v a . i o . * ;
catch(IOException ignorada) II
I n t r o d u z c a una c a d e n a de t e x t o .
Para t e r m i n a r p u l s e C t r l + z
hola[Entrar]
El c ó d i go A S C I I de h es 104
El c ó d i go A S C I I de 0 es 111
El c ó d i go A S C I I de 1 es 1 08
El c ó d i go A S C I I de a es 97
ad i ó s [ E n t r a r ]
El c ó d i g o A S C I I de 3 es 97
El c ó d i go A S C I I de d e s 100
El c ó d i go A S C I I de i e s 105
El c ó d i go A S C I I de ó e s 162
El c ó d i go A S C I I de s es 115
[ C t r l ] [z]
Bucles anidados
C uando se incluye una sentencia w h ile dentro de otra sentencia while, en general
una sentencia while, do, o f o r dentro de otra de ellas, estam os en el caso de bu
cles anidados. Por ejem plo:
CA PITU LO 6: SEN TEN CIA S DE CONT ROL 137
Para i = 1: j = 1. j - 2, j = 3. j = 4.
Para i - 2: j = 1. j - 2. j = 3. j - 4.
Para i — 3: j = 1. j — 2. j =3. j =4.
E ste resultado dem uestra que el bucle exterior se ejecute tres veces, y por ca
da una de éstas, el bucle interior se ejecuta a su vez cuatro veces. Es así com o se
ejecutan los bucles anidados: por cada iteración del bucle externo, el interno se
ejecuta hasta finalizar todas sus iteraciones.
O bserve tam bién que cada vez que finaliza la ejecución de la sentencia w hile
interior, avanzam os a una nueva línea, increm entam os el valor de i en una unidad
e iniciam os de nuevo j al valor 1.
r = x2 + y 2
l X Y
5 3 4
13 5 12
10 6 8
50 30 40
138 JA V A: C U R SO D E PROGRAM A CIÓN
• Prim ero definim os las variables que vam os a utilizar en los cálculos.
1 n t x - l . y - l . z - 0 :
z = ( i n t ) M a t h . s q r t ( x * x + y * y ) ; // z e s una v a r i a b l e e n t e r a
i f (z * z == x * x + y * y) // ¿ l a r a i z c u a d r a d a f u e e x a c t a ?
System.out. p r i n t l n t z + " \ t " + x + " \ t " + y ) :
A dem ás, siem pre que obtengam os un valor z m ayor que 50 lo desecharem os y
continuarem os con un nuevo v alor de a y los correspondientes valores de y.
while (x <= 5 0 )
1
// C a l c u l a r z . Como z e s un e n t e r o , a l m a c e n a
// l a p a r t e e n t e r a de l a r a í z c u a d r a d a
z = (int)M ath.sqrt(x * x + y * y):
w h i l e ( y < = 5 0 && z < = 5 0 )
I
// S i l a r a i z c u a d r a d a a n t e r i o r f u e e x a c t a .
// e s c r i b i r z , x e y
i f ( z * z = = x * x + y * y )
System.out.printlníz + " \ t " + x + " \ t " + y):
CA PÍTU LO 6: SEN TEN CIA S DE C O N TR O L 139
■ y = y + 1:
z = (int)Math.sqrt(x * x + y * y);
I
x = x + l ; y = x :
)
do
s e n te n c ia -,
wh i l e ( c o n d i c i ó n );
Por ejem plo, el siguiente código obliga al usuario a introducir un valor positivo:
d o u b l e n:
do // e j e c u t a r las sentencias siguientes
[
S y s t e m . o u t . p r i n t ( " Nú me r o : ” ):
11 = L e e r . d a t o D o u b l e í ):
I
while ( n < 0 ): // m i e n t r a s n s ea me n o r q u e 0
ejecuta una estructura w h ile puede suceder que el bloque de sentencias no se eje
cute, lo que ocurrirá siem pre que la condición sea inicialm ente falsa.
rM =
abs(r¡ ■ ru , ) < £
• Prim ero definim os las variables que vam os a utilizar en los cálculos.
double n; // númer o
double aprox: // aproxi maci ón a la r a í z cuadrada
double antaprox: // a n t e r io r aproximación a la r a iz cuadrada
double epsilon: // c o e f i c i e n t e de e r r o r
System.out.printt"Número: "):
n = L e e r . d a t o D o u b l e ( );
S y s t e m . o u t . p r i n t C R a i z cuadrada aproximada: ");
a p r o x = L e e r . d a t o D o u b l e ( );
S y s t e m . o u t . p r i n t í " C o e f i c i e n t e de e r r o r : " ) ;
e p s i l o n - L e e r , d a t o D o u b l e ( ):
do
1
ant aprox = aprox:
aprox = (n/antaprox + antaprox) / 2;
1
while (Math.abstaprox - antaprox) >= e p s i l o n ) :
CA PÍTU LO 6: SEN TEN CIA S DE CO N TRO L 141
Al aplicar la fórm ula p o r prim era vez, la variable antaprox contiene el valor
aproxim ado a la raíz cuadrada que hem os introducido a través del teclado. Pa
ra sucesivas veces, antaprox contendrá la últim a aproxim ación calculada.
do
I
System.out.printí"Número: ” ):
n = L e e r . d a t o D o u b l e t ):
I
w h i 1e ( n < = 0 ):
do
I
S y s t e m . o u t . p r i n t í " R a í z cuadrada aproximada: ” ):
a p r o x = L e e r . d a t o D o u b l e í ):
I
w h i 1e ( a p r o x < = 0 ):
do
I
S y s t e m . o u t . p r i n t ( " C o e f i c i e n t e de e r r o r : ");
e p s i l o n = L e e r . d a t o D o u b l e ( ):
I
while ( epsilon < = 0 );
142 JA V A: C U R SO DE PRO G R A M A CIÓ N
do
(
antaprox - aprox:
aprox = (n/antaprox + antaprox) / 2:
I
'w hile (M a t h . a b s ía p ro x - a nta p ro x) >= e p s i l o n ) :
S y s t e m . o u t . p r i n t l n í " L a r a í z c u a d r a d a de " + n + " es ” + aprox):
Númer o: 10
Raíz cuadrada aproximada: 1
C o e f i c i e n t e de e r r o r : l e - 4
La r a i z c u a d r a d a de 1 0 . 0 e s 3 . 1 6
SENTENCIA for
La sentencia fo r perm ite ejecutar una sentencia sim ple o com puesta, repetida
m ente un núm ero de veces conocido. Su sintaxis es la siguiente:
fo r ( [ v l= e l [ , v2=*e2]. . . ] ; [ c o n d i c i ó n ] ; [ p r o g r e s i ó n - c o n d i c i ó n ' ] )
sentencia;
• v i , v2, .... representan variables de control que serán iniciadas con los valores
de las expresiones e l , e2, ...;
• condición es una expresión booleana que si se om ite, se supone verdadera;
• progresión-condición es una o m ás expresiones separadas p o r com as cuyos
valores evolucionan en el sentido de que se cum pla la condición para finalizar
la ejecución de la sentencia for;
• sentencia es una sentencia sim ple o com puesta.
2. Se evalúa la condición:
a) Si el resultado es true (verdadero), se ejecuta el bloque de sentencias, se
evalúa la expresión que da lugar a la progresión de la condición y se vuel
ve al punto 2.
b) Si el resultado es false (falso), la ejecución de la sentencia fo r se da por fi
nalizada y se p asa el control a la siguiente sentencia del program a.
C A PÍTU LO 6: SEN TEN CIA S DE CO N TRO L 143
P o r ejem plo, la siguiente sentencia fo r im prim e los núm eros del 1 al ¡00. Li
teralm ente dice: desde i igual a / , m ientras i sea m enor o igual que 100, incre
m entado la i de uno en uno, escribir el v alor d e i.
in t i :
f o r ( i - 1 ; i < - 100 ; i + + )
S y s t e m . o u t . p r i n t ( i + " “ ):
El siguiente ejem plo im prim e los m últiplos de 7 que hay entre 7 y 112. Se
puede observar que, en este caso, la variable se ha declarado e iniciado en la pro
pia sentencia f o r (esto no se puede hacer en una sentencia w hile; las variables que
intervienen en la condición de una sentencia w hile deben hab er sido declaradas e
iniciadas antes de que se procese la condición p o r prim era vez).
f o r ( i n t k - 7 ; k < = 112 : k + - 7)
System .out.print(k + ’ ') :
int f , c :
f o r ( f = 3 . c - 6 : f + c < 40 : f + + . c + = 2)
System .out.p rin tln t"f = " + f + "\t c - " + c);
E ste otro ejem plo que ve a continuación, im prim e los valores desde 1 hasta 10
con increm entos de 0.5.
f o r ( f l o a t i - 1 : i < = 10 : i + - 0 . 5)
System .out.print(i + " ");
El siguiente ejem plo im prim e las letras del abecedario en orden inverso.
char car:
f o r ( c a r = ' z ' ; c a r >= ' a ' : c a r - - )
S y s t e m . o u t . p r i n t ( c a r + " ’ ):
El ejem plo siguiente indica cóm o realizar un bucle infinito. Para salir de un
bucle infinito tiene que pulsar las teclas Ctrl+C.
for (::)
I
s e n t e n c i as;
I
1 4 4 JA V A : C U R SO D E PRO G R A M A CIÓ N
P o s i c i ó n del a lfil:
fila 3
c o l u mn a 4
B * B N B * B N
N B * B * B N B
B N B * B N B N
N B * B * B N B
B * B N B * B N
* B N B N B * B
B N B N B N B *
N B N B N B N B
• Prim ero definim os las variables que vam os a utilizar en los cálculos.
int f a l f i l , c a l f i l ; // p o s i c i ó n i n i c i a l del a l f i l
int f i l a , c o l u m n a : II p o s i c i ó n a c t u a l del a l f i l
// P i n t a r e l t a b l e r o de a j e d r e z
i f ( ( f i l a + c o l u mn a — f a l f i l + c a l f i l ) ||
( f i l a - c o l u mn a = - f a l f i l - c a l f i l ) )
System .out.print{"* "):
e l s e i f ( ( f i l a + col umna) X 2 — 0)
System.out.print("B "):
el se
S y s t e m . o u t . p r i n t C N ” ):
// P i n t a r e l t a b l e r o de a j e d r e z
f o r ( f i l a = 1: f i l a < = 8 ; f i l a + + )
I
for ( c o l u m n a = 1; c o l u mn a < = 8 ; columna++)
1
if ( ( f i l a + c o l u mn a = = f a l f i l + c a l f i l ) ||
( f i l a - col umna = = f a l f i l - c a l f i l ) )
System.out.p r i n t ( " * ");
e l s e i f ( ( f i l a + col umna) % 2 = 0)
System.out.printí"B ");
el se
System.out.print("N ");
I
S y s t e m . o u t . p r i n t l n í ); II c a m b i a r de f i l a
1 4 6 JA V A: C U R SO DE PRO G R A M A CIÓ N
SENTENCIA break
while ( y <= 5 0 )
I
// S i l a r a i z c u a d r a d a anterior fue exacta.
// e s c r i b i r z . x e y
if ( z * z = = x * x + y * y )
System.out.printlnCz + "\t" + x + "\t" + y):
y - y + 1:
z - ( int)Math.sqrt(x * x + y * y):
del b u c l e « ■ ■ ■ ■ ■ ■ ■ ■ ■
SENTENCIA continué
La sentencia con tinué obliga a ejecutar la siguiente iteración del bucle while, do,
0 for, en el que está contenida. Su sintaxis es:
continué:
C om o ejem plo, vea la siguiente aplicación que im prim e todos los núm eros
entre 1 y 100 q ue son m últiplos de 5.
Ejecute este program a y observe que cada vez que se ejecuta la sentencia co n
tinué, se inicia la ejecución del bloque de sentencias de fo r para un nuevo valor
de n.
ETIQUETAS
C on las sentencias b re a k y con tin u é se puede tam bién utilizar una etiqueta para
indicar dónde se debe reanudar la ejecución (quiero advertir que el uso de etique
tas es una m ala práctica en program ación, p o r lo que debe reducirse a casos ex
cepcionales). Según lo explicado anteriorm ente, cuando se utiliza b re a k en bucles
anidados, perm ite finalizar la ejecución del bucle donde está incluida, continuan
do la ejecución en el bucle exterior m ás cercano; y continué, inicia una nueva ite
ración del bucle donde está incluida. P ues bien, utilizando una etiqueta con b re a k
o con con tin u é se puede reanudar la ejecución en un bucle m ás externo. La eti
queta, finalizada con dos puntos, d eb e escribirse ju sto antes de la sentencia while,
do, o for. P o r ejem plo:
salir:
f o r ( x - 1 : x < - 5 ; x + + )
I
f o r ( y = 1 : y < - 5 ; y + + )
I
f o r ( z = 1 ; z < - 5 : z + + )
I
i f ( ( x * y + z ) % 11 = 0 )
1
S y s t e m . o u t . p r i n t l n t x + " * " + y + " + " + z +
" e s m ú l t i p l o d e 11 “ ) ;
b r e a k salir:
I
I
I
I
S y s t e m . o u t . p r i n t l n t " C o n t i n ú a l a e j e c u c i ó n " ) ;
2 3+5
* e s m ú l t i p l o d e 11
C o n t i n ú a l a e j e c u c i ó n
2 * 3 + 5 es múl t i p i o de 11
3 * 2 + 5 es múl t i pl 0 de 11
4 * 2 + 3 es múl t i pl 0 de 11
5 * 2 + 1 es múl t i p i o de 11
C o n t i l l úa 1a ejecución
Los resultados m ostrados indican que ahora, cada vez que se cum ple la condi
ción. con tinué hace que se reanude la ejecución para la siguiente iteración del bu
cle m ás externo (para el siguiente valor de x).
La inform ación dada por el m ensaje anterior, adem ás del tipo de excepción,
especifica que ha ocurrido una división por cero en la línea 9 del m étodo m a in de
la clase Test.
S y s t e m . o u t . p r i n t l n C S e i n i c i a l a a p l i c a c i ó n ” ):
datol++;
d a t o 3 = d a t o l / d a t o 2:
dato2++:
II O t r a s s e n t e n c ia s
S y s t e m . o u t .p rin t ln ( d a t o l + ” ” + dato2 + ” ” + dato3);
I
CA PÍTU LO 6: SEN TEN CIA S DE C O N TR O L 149
Se i n i c i a l a a p l i c a c i ó n
E r r o r : / by z e r o
1 0 1
EJERCICIOS RESUELTOS
1. R ealizar un program a que calcule las raíces de la ecuación:
ax2 + bx + c = O
1 5 0 JA V A: C U R S O DE PROGRAM A CIÓN
- b ± j b 2 - 4 ac
x + yj, * - yj
Indicar con literales apropiados los datos a introducir, así com o los resultados
obtenidos.
• Prim ero definim os las variables que vam os a utilizar en los cálculos.
double a. b. c : // c o e f i c i e n t e s de l a e c u a c i ó n
double d; II discriminante
double r e . i m; // p a r t e r e a l e i m a g i n a r i a de l a r a í z
System.out.printí"a =" ) ; a = L e e r . d a t o D o u b l e ( );
System.out.printí"b - "): b = L e e r . d a t o O o u b l e í ):
System.out.printí"c = "): c = L e e r . d a t o D o u b l e í ):
If (a — 0 && b — 0)
System.out.printlnCLa e c u a c i ó n e s d e g e n e r a d a " );
el se i f (a - - 0)
System.out.printlnCLa única raíz es: " + -c/b):
else
I
// E v a l u a r l a fórmula. C á l c u l o de d. r e e im
C A PÍTU LO 6: SEN TEN CIA S DE C O N TR O L 151
if (d > - 0)
I
// Imprimir las raíces reales
I
el s e
I
// I mprimir las raíces complejas conjugadas
—b -Jb : - 4 a c
• C álculo d e —— ± --------
2a la
^ —re im
re - -b / (2 * a ) :
d - b * b - 4 * a * c :
im - M a t h . s q r t ( M a t h . a b s ( d ) ) / ( 2 * a);
S y s t e m . o u t . p r i n t l n í " R a í c e s r e a l e s : “ ):
S y s t e m . o u t . p r i n t l n ( ( r e + i m ) + ” , " + ( r e - i m) ) :
S y s t e m . o u t . p r i n t l n í " C o e f i c i e n t e s a, b y c de l a e c u a c i ó n : ’ ):
System.out.printí’a = ’ ) : a = L e e r . d a t o D o u b l e ( ):
System.out.p r i n t í ’ b = ’ ) ; b = L e e r . d a t o D o u b l e ( ):
System.out.printí"c = " ) ; c = L e e r . d a t o D o u b l e ( ):
152 JA V A: C U R SO DF. PROGRAM A CIÓN
S y s t e m . o u t . p r i n t l n í );
if ( a - » 0 && b — 0)
S y s t e m . o u t . p r i n t l n ( " L a e c u a c i ó n es d e g e n e r a d a " ) :
e l s e i f ( a = = 0)
S y s t e m . o u t . p r i n t l n í “ La ú n i c a r a i z e s : " + - c / b ):
el s e
I
re - -b / ( 2 * a) ;
d - b * b - 4 * a * c ;
im = M a t h . s q r t ( M a t h . a b s í d ) ) / (2 * a):
if (d > = 0 )
I
System.out.printlni"Raíces rea les:");
System .out.p rin tln ((re +im ) + ". " + (re-im)):
1
el se
1
S y s t e m . o u t . p r i n t l n i " R a l c e s c o m p l e j a s : ” );
S y s t e m . o u t .p r i n t l n i re + " + " + M a t h . a b s ( i m ) + " j “ );
System .out.p rin tln íre + " - " + Math.absíim) + “ j"):
I
I
2. E scrib ir un program a para que lea un texto y dé com o resultado el núm ero de
palabras con al m enos cuatro vocales diferentes. S uponem os que una palabra está
separada de otra por uno o m ás espacios ( ‘ ’), tabuladores (\t) o caracteres ‘\n ’. La
entrada de datos finalizará cuando se detecte la m arca de fin de fichero. La ejecu
ción será de la form a siguiente:
Númer o d e p a l a b r a s con 4 v o c a l e s d i s t i n t a s : 3
i n t np = 0 : // n ú me r o de p a l a b r a s c o n 4 v o c a l e s distintas
i n t a = 0 . e = 0 . i = 0. o = 0. u — 0:
char car:
fi n a l char eof = ( c h a r )-1:
C A P ÍT U L O 6: SEN TEN CIA S DE C O N T R O L 15 3
S y s t e m . o u t . p r i n t l n C I n t r o d u c i r texto. " +
"Para f i n a l i z a r puls ar C t r l + z . \ n " ) :
while ( ( c a r = ( c h a r ) S y s t e m . i n . r e a d ( )) ! - eof)
I
/*
S i e l c a r á c t e r l e í d o e s una ' a ' h a c e r a - 1
S i e l c a r á c t e r l e í d o e s una ' e ' h a c e r e - 1
S i e l c a r á c t e r l e í d o e s una ' i ' h a c e r i - 1
S i e l c a r á c t e r l e í d o e s una ‘ o ’ h a c e r o - 1
S i e l c a r á c t e r l e i d o e s una ’ u ' h a c e r u - 1
S i e l c a r á c t e r l e i d o e s un e s p a c i o en b l a n c o .
un \ t o un \ n , a c a b a mo s d e l e e r una p a l a b r a . E n t o n c e s .
s i a + e + i + o + u > = 4 . i n c r e m e n t a r e l c o n t a d o r de p a l a b r a s
de c u a t r o v o c a l e s d i f e r e n t e s y p o n e r a. e. i . o y u de
nuevo a c e r o .
*/
I // fin del while
if ( ( a + e + i + o + u ) >-4) np++;
i mpor t j a v a . i o . * :
// L e e r . c l a s s d e b e e s t a r en l a c a r p e t a especificada p o r CLASSPATH
//
public class CPalabras
I
// C o n t a r el n ú me r o de p a l a b r a s en un t e x t o
II c o n 4 o más v o c a l e s d i f e r e n t e s
public s t a t i c void m a i n ( S t r i n g [] args)
I
i n t np - 0 : // n ú me r o d e p a l a b r a s c o n 4 v o c a l e s d i s t i n t a s
i n t a = 0. e - 0. i = 0. o = 0 , u = 0:
char car;
fi nal char eof = ( c h a r ) - l:
1 5 4 JA V A: C U R S O D E PRO G R A M A CIÓ N
try
I
S y s te m . o u t .p rin tln ( " I n tr o d u c ir texto. " +
" P a r a f i n a l i z a r p u l s a r C t r l + z . \ n " );
w h i l e ( ( c a r = ( c h a r ) S y s t e m . i n . r e a d ( )) != eo f)
switch (car)
ii
c a s e ‘A ’ : case ' a ‘ : c a s e ’á ' :
a = 1:
break:
case ' E ' : case ’e' : case ' é ' :
e = 1:
break;
case ' I ' : case ’ i ' : case ' i ':
i = 1:
break:
c a s e ’O ’ : c a s e ' o ' : c a s e ’ ó ' :
o = l:
break:
c a s e ’ U ’ : c a s e •u' : c a s e ’ ú ’ :
u = 1:
break:
default:
i f ( c a r — ' •)
1
i f ( ( a + e + i + o + u) >
a - e = i - 0
D
II
o
c
r
if (car — ’\ n ' )
i f ( ( a + e + i + 0 + u) >
a = e = i = 0 - u = 0:
I // f i n d e l s w i t c h
) // f i n d e l w h i l e
i f ( (a + e + i + o + u) >= 4 ) np++:
S y s t e m . o u t . p r i n t l n í " \ n \ n N ú m e r o de p a l a b r a s c o n ” +
"4 v o c a l e s d i s t i n t a s : " + np);
I
catch(lOException ignorada) I)
1
I
3. E scribir un program a para que lea un texto y dé com o resultado el núm ero de
caracteres, palabras y líneas del m ism o. Suponem os que una palabra está separad
de otra por uno o m ás espacios caracteres lab (\t) o caracteres ‘\ n ’. L a ejecu
ción será de la form a siguiente:
CA PÍTU LO 6: SEN TEN CIA S D E C O N T R O L 155
i mport j a v a . i o . * ;
// L e e r . c l a s s debe e s t a r en l a c a r p e t a especificada p o r CLASSPATH
II
public cla ss CContarPalabras
I
// C o n t a r c a r a c t e r e s , p a l a b r a s y l í n e a s en un t e x t o
public s t a t ic void m ain(S tring [] args)
I
final char eof = ( c h a r ) - l;
char car:
bo o l e a n pal abr a = f a l s e :
i n t n c a r a c t e r e s = 0, n p a l a b r a s = 0. n l i n e a s = 0:
try
I
S y s te m . o u t .p rin tln ( " Intr o d u cir texto. ” +
" P u l s e [ E n t r a r ] d e s p u é s de c a d a l i n e a . " ) :
System.out.printlní"Para f i n a li z a r pulsar C t r l + z . \ n "):
while ( ( c a r = ( c h a r ) S y s t e m . i n .r e a d ( )) != eof)
I
// [ E n t r a r ] = C R L F = \ r \ n
i f ( c a r = = * \ r ’ ) c o n t i n u é : // l e s i g u e un \ n
ncaracteres++; // c o n t a d o r de c a r a c t e r e s
II E l i m i n a r b l a n c o s , t a b u l a d o r e s y f i n a l e s de l i n e a
II e n t r e p a l a b r a s
i f ( c a r = « ' ’ || c a r — ’ \ n ’ || c a r = = ’ \ t ’ )
palabra = false:
e l s e i f ( ¡ p a l a b r a ) // c o m i e n z a una p a l a b r a
I
npalabras++; // c o n t a d o r de p a l a b r a s
palabra = true:
I
if (car == ' \ n ' ) II f i n a l i z a una l i n e a
nlineas++: // c o n t a d o r de l i n e a s
I
S y s t e m . o u t . p r i n t l n ! ):
1 5 6 JA V A: C U R SO DE PRO G R A M A CIÓ N
n i i n e a s ) ;
I
catchíIOException ignorada) II
4. R ealizar un program a que a través de un m enú perm ita realizar las operaciones de
sum ar, restar, m ultiplicar, d ivid ir y salir. Las operaciones constarán solam ente de
dos operandos. El m enú será visualizado p o r un m étodo sin argum entos, que de
volverá com o resultado la opción elegida. La ejecución será de la form a siguiente:
1. s uma r
2. r e s t a r
3. m u l t i p l i c a r
4. d i v i d i r
5. s a l i r
S e l e c c i o n e la o pera ci ón deseada: 3
D a t o 1: 2 . S
D a t o 2: 10
Resultado - 25.0
P u l s e [ E n t r a r ] para c o n t i n u a r
• Prim ero definim os las variables y los prototipos de las funciones que van a
intervenir en el program a.
d o u b l e d a t o l = 0. d a t o 2 = 0 . resultado - 0:
i n t o p e r a c i ó n = 0:
o p e r a c i ón = m e n ú ( ):
static i n t menú()
I
i n t op:
do
I
S y s t e m . o u t . p r i n t l n ( " N t l . s u m a r ” ):
C A P Í TULO 6: SEN TEN CIA S DE C O N T R O L 157
if (operación ! = 5)
I
// L e e r d a t o s
System.out.printí"Dato 1: ” ); d a t o l = L e e r . d a t o D o u b l e ( ):
System.out.printCDato 2: "); d a t o 2 = L e e r . d a t o D o u b l e ( ):
// R e a l i z a r la operación
I
el s e
break; // s a l i r
switch (operación)
[
c a s e 1:
resultado = datol + dato2:
break;
c a s e 2:
r e s u l t a d o = d a t o l - dato2:
break:
c a s e 3:
r e s u l t a d o = d at o l * dato2;
break:
c a s e 4:
r e s u l t a d o = da to l / dato2;
break:
I
// E s c r i b i r e l r e s u l t a d o
System.out.printlni"Resultado = " + resultado):
// H a c e r una p a u s a
S y s t e m . o u t . p r i n t l n i " P u l se [ E n t r a r ] para c o n t i n u a r " ) ;
S y s t e m . i n . r e a d í ):
158 JA VA: C U R SO DE PRO G R A M A CIÓ N
• Las operaciones descritas form arán parte de un bucle infinito form ado p o r una
sentencia w h ile con el fin de poder encadenar distintas operaciones.
while (true)
I
// s e n t e n c i a s
i mpor t j a v a . i o . * ;
// L e e r . c l a s s d e b e e s t a r en l a carpeta especificada p o r CLASSPATH
//
public class CCalculadora
I
// S i m u l a c i ó n de una c a l c u l a d o r a
s t a t i c i n t menú()
1
int op:
do
I
S y s t e m . o u t . p r i n t l n ( " \ t l . s u m a r " );
S y s t e m . o u t . p r i n t l n í " \ t 2. r e s t a r " ) :
S y s t e m . o u t . p r i n t l n í " \ t 3 . m u í t i p l i c a r " );
S y s t e m . o u t . p r i n t l n í " \ t 4 . d i v i d i r ” ):
System.out.println("\t5. sa lir"):
S y s t e m . o u t .p r i n t í "\nSeleccione la operación deseada: "):
op - L e e r . d a t o l n t í ):
1
while ( o p < 1 || o p > 5 ) :
r e t u r n op:
try
I
while (true)
I
o p e r a c i ó n = m e n ú í );
i f ( o p e r a c i ó n ! - S)
I
// L e e r d a t o s
S y s t e m . o u t . p r i n t í " D a t o 1: '): d a t o l - L e e r . d a t o D o u b l e ( ):
S y s t e m . o u t . p r i n t í " D a t o 2: ” ); d a t o 2 = L e e r . d a t o D o u b l e ( ):
C A P ÍT U L O 6: SEN TEN CIA S D E C O N T R O L 159
/ / L i m p i a r e l b u f f e r d e l f l u j o d e e n t r a d a
S y s t e m . i n . s k i p ( S y s t e m . i n . a v a i 1 a b l e ( ) ) ;
// R e a l i z a r l a o p e r a c i ó n
switch (operación)
I
c a s e 1:
resultado = datol + dato2:
break;
c a s e 2:
resultado = datol - dato2;
break;
c a s e 3:
resultado = datol * dato2:
break;
c a s e 4:
resultado = datol / dato2;
break;
I
// E s c r i b i r e l r e s u l t a d o
System.out.printlní"Resultado - * + resultado):
// H a c e r una p a u s a
S y s t e m . o u t . p r i n t l n í " P u l se [ E n t r a r ] para c o n t i n u a r " ) ;
S y s t e m . i n . r e a d í );
// L i m p i a r e l b u f f e r d e l f l u j o de e n t r a d a
S y s t e m . i n . s k i p ( S y s t e m . i n . a va i 1 a b l e ( ) ) ;
I
el se
break;
catchíIOException ignorada) II
EJERCICIOS PROPUESTOS
1. R ealizar un program a que calcule e im prim a la sum a de los m últiplos de 5 com
prendidos entre dos valores a y b. El program a no perm itirá introducir valores ne
gativos para a y b, y verificará que a es m enor que b. Si a es m ayor que b, inter
cam biará estos valores.
3. Si quiere av eriguar su núm ero de Tarot, sum e los núm eros de su fecha de naci
m iento y a conlinuación redúzcalos a un único dígito; por ejem plo si su fecha de
nacim iento fuera 17 de O ctubre d e 1970, los cálculos a realizar serían:
día d e l m es de aíxo
donde día, m es y año son enteros, y dé com o resultado el núm ero de Tarot. El
program a verificará si la fecha es correcta, esto es, los valores están dentro de los
rangos perm itidos.
1
2 3 2
3 4 5 4 3
4 5 6 7 6 5 4
5 6 7 8 9 8 7 6 5
6 7 8 9 0 1 0 9 8 7 6
7 8 9 0 1 2 3 2 1 0 9 8 7
8 9 0 1 2 3 4 S 4 3 2 1 0 9 8
9 0 1 2 3 4 5 6 7 6 5 4 3 2 1 0 9
1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1
3 4 5 6 7 8 9 0 1 0 9 8 / 6 5 4 3
2 3 ...........................................................................
El núm ero de filas estará com prendido entre 11 y 20 y el resultado aparecerá cen
trado en la pantalla com o se indica en la figura.
6. Un centro num érico es un núm ero que separa una lista de núm eros enteros
(com enzando en 1) en dos grupos de núm eros, cuyas sum as son iguales. El prim er
centro num érico es el 6, el cual separa la lista (1 a 8) en los grupos: (1, 2, 3, 4, 5)
CA PÍTU LO 6: SEN TEN CIA S DE C O N TR O L 161
y (7, 8) cu y as sum as son am bas iguales a 15. El segundo centro num érico es el 35,
el cual separa la lista (1 a 49) en los grupos: (1 a 34) y (36 a 49) cuyas sum as son
am bas iguales a 595. E scribir un program a que calcule los centros num éricos en
tre 1 y n.
7. R ealizar un program a que solicite un texto (suponer que los caracteres que form an
el texto son sólo letras, espacios en blanco, com as y el punto com o final del texto)
y a continuación lo escriba m odificado de form a que, a la A le corresponda la K, a
la B la L, ... , a la O la Y, a la P la Z, a la Q la A , ... y a la Z la J, e igual para las
letras m inúsculas. S uponga que la entrada no excede de una línea y que finaliza
con un punto.
Al realizar este program a tenga en cuenta que el tipo c h a r es un tipo entero, por
lo tanto las afirm aciones en los ejem plos siguientes son correctas:
MATRICES
H asta ahora sólo hem os tenido que trabajar con algunas variables en cada uno de
los program as que hem os realizado. Sin em bargo, en m ás de una ocasión tendre
m os que m anipular conjuntos m ás grandes de valores. P or ejem plo, p ara calcular
la tem peratura m edia del m es de agosto necesitarem os conocer los 31 valores c o
rrespondientes a la tem peratura m edia de cada día. En este caso, podríam os utili
zar una variable para introducir los 31 valores, uno cada vez, y acum ular la suma
en otra variable. Pero ¿qué ocurrirá con los valores que vayam os introduciendo?
que cuando tecleem os el segundo valor, el prim ero se perderá; cuando tecleem os
el tercero, el segundo se perderá, y así sucesivam ente. C uando hayam os introdu
cido todos los valores podrem os calcular la m edia, pero las tem peraturas corres
pondientes a cada día se habrán perdido. ¿Q ué podríam os hacer para alm acenar
todos esos valores? Pues, podríam os utilizar 31 variables diferentes; pero ¿qué pa
saría si fueran 100 o m ás valores los que tuviéram os que registrar? A dem ás de ser
muy laborioso el definir cada una de las variables, el código se vería enorm e
m ente increm entado.
Si las m atrices son la form a de registrar conjuntos de valores, todos del m is
m o tipo (in t, flo at, d o u b le, c h a r. S trin g , etc.), ¿qué harem os p ara alm acenar un
conjunto de valores relacionados entre sí, pero de diferentes tipos? P or ejem plo,
alm acenar los datos relativos a una persona com o su nom bre, dirección, teléfono,
etc. Ya hem os visto que esto se hace definiendo una clase; en este caso, podría ser
la clase de objetos persona. Posteriorm ente podrem os crear tam bién m atrices de
objetos, cuestión que aprenderem os m ás adelante.
1 6 4 JA V A : C U R SO DE PRO G R A M A CIÓ N
matriz m
i i i i i i i i i ■
En general, la representación de las m atrices se hace m ediante variables sus
critas o de subíndices y pueden tener una o varias dim ensiones (subíndices). A las
m atrices de una dim ensión se les llam a tam bién listas y a los de dos dim ensiones,
tablas.
v = [a 0 , a , , a 2,
P o r ejem plo, supongam os que tenem os una m atriz unidim ensional de enteros
llam ada m, la cual contiene 10 elem entos. Estos elem entos se identificarán de la
siguiente forma:
matriz m
A
O bserve que los subíndices son enteros consecutivos, y que el p rim er subíndi
ce vale 0. Un subíndice puede ser cualquier expresión entera positiva.
CA PITU LO 7: M A TRICES I65
A sí m ism o, una m atriz de dos dim ensiones se representa m ediante una varia
ble con dos subíndices (filas, colum nas); una m atriz de tres dim ensiones se repre
senta m ediante una variable con tres subíndices etc. El núm ero m áxim o de
dim ensiones o el núm ero m áxim o de elem entos, dentro de los lím ites establecidos
por el com pilador, para una m atriz depende de la m em oria disponible.
En Java, cada elem ento de una m atriz unidim ensional es de un tipo prim itivo,
o bien una referencia a un objeto; y cada elem ento de una m atriz m ultidim ensio-
nal es, a su vez, una referencia a otra m atriz. A continuación se estudia todo esto
detalladam ente.
Para crear y utilizar una m atriz hay que realizar tres operaciones: declararla,
crearla e iniciarla.
t » ' p o [ ] nombre;
tipo n o m b r e l ] ;
donde tip o indica el tipo de los elem entos de la m atriz, que pueden ser de cual
quier tipo prim itivo o referenciado; y nom bre es un identificador que nom bra a la
m atriz. Los corchetes m odifican la definición norm al del identificador para que
sea interpretado por el com pilador com o una m atriz.
i n t [ ] m:
f l o a t [ ] temperatura:
COrdenador[] ordenador: // C O r d e n a d o r e s una c l a s e d e o b j e t o s
1 6 6 JA V A: C U R SO D E PROGRAM A CIÓN
L a prim era línea declara una m atriz de elem entos de tipo in t; la segunda, una
m atriz de elem entos de tipo flo at; y la tercera una m atriz de objetos C O rdenador.
Según se ha podido observar, los corchetes se pueden colocar tam bién d es
pués del nom bre de la m atriz. Por lo tanto, las declaraciones anteriores pueden es
cribirse tam bién así:
i n t m [ ] :
f l o a t t e mp e r a t u r a [ ] :
COrdenador o r d e n a d o r [ ] ¡ // C O r d e n a d o r e s una c l a s e de o b j e t o s
donde nom bre es el nom bre de la m atriz previam ente declarada; tipo es el tipo de
los elem entos de la m atriz; y tam año es una expresión entera positiva m enor o
igual que la precisión de un int, que especifica el núm ero de elem entos.
El hecho de utilizar el operador n ew significa que Java im plem ento las m atri
ces com o objetos, por lo tanto serán tratadas com o cualquier otro objeto.
Las siguientes líneas de código crean las m atrices declaradas en el ejem plo
anterior:
m - n e w i n t [ 1 0 ] :
t e m p e r a t u r a = n e w f 1 o a t [ 3 1 3 :
o r d e n a d o r = n e w C 0 r d e n a d o r [ 2 5 ] ;
La prim era línea crea una m atriz identificada p o r m con 10 elem entos de tipo
in t; es decir, puede alm acenar 10 valores enteros; el p rim er elem ento es m [0] (se
lee: m sub-cero), el segundo m i l ] , ..., y el últim o m [9]. L a segunda crea una m a
triz tem peratura de 31 elem entos de tipo flo at. Y la tercera crea una m atriz orde
n a d o r de 25 elem entos, cada uno de los cuales puede referenciar a un objeto
C O rdenador. U na m atriz de objetos es una m atriz de referencias a dichos objetos.
CA PÍTU LO 7: M A TRICES 167
Es bastante com ún declarar y crear la m atriz en una m ism a línea. E sto puede
hacerse así:
tipoli n o m b r e = new t i p o í t a m a ñ o ] ;
tipo n o m b r e n - new t i po [t a m a ñ o ] ;
i n t [ ] m = new i n t [ 1 0 ] :
f l o a t [ ] t e m p e r a t u r a - new f l o a t [ 3 1 ] ;
C O r d e n a d o r [ ] o r d e n a d o r = new C 0 r d e n a d o r [ 2 5 ] :
C uando se crea una m atriz, el tam año de la m ism a puede ser tam bién especi
ficado durante la ejecución a través de una variable a la que se asignará com o va
lor el núm ero de elem entos requeridos. Por ejem plo, la últim a línea de código del
ejem plo siguiente crea una m atriz con el núm ero de elem entos especificados por
la variable n E lem entos:
i n t nElementos:
S y s t e m . o u t . p r i n t í " N ú m e r o d e e l e m e n t o s de l a m a t r i z : "):
n E l e m e n t o s = L e e r . d a t o l n t í );
i n t [ ] m = new i n t [ n E l e m e n t o s ] ;
Si deseam os iniciar una m atriz con otros valores diferentes a los predeterm i
nados, podem os hacerlo de la siguiente form a:
E l ejem plo anterior crea una m atriz tem peratura de tipo float con tantos ele
m entos com o valores se hayan especificado entre llaves.
Para acceder al valor de un elem ento de una m atriz se utiliza el nom bre de la m a
triz, seguido de un subíndice entre corchetes. Esto es, un elem ento de una m atriz
168 JA VA : C U R S O DE PROGRAM A CIÓN
i n t [ ] m = new i n t [ 1 0 0 ] :
int k = 0 . a = 0 :
11...
a - m [ 1] + m [ 9 9 ] ;
k = 50:
m[ k]++;
m [k+l] = m [k];
O bserve que para referenciar un elem ento de una m atriz se puede em plear
com o subíndice una constante, una variable o una expresión de tipo entero. El
subíndice especifica la posición del elem ento dentro de la m atriz. La prim era po
sición es la 0.
Si se intenta acceder a un elem ento con un subíndice m enor que cero o m ayor
que el núm ero de elem entos de la m atriz m enos uno, Java lanzará una excepción
de tipo A r r a y ln d e x O u tO fB o u n d s E x c e p t io n . indicando que el subíndice está
fuera de los lím ites establecidos cuando se creó la m atriz. Por ejem plo, cuando se
ejecute la últim a línea de código del ejem plo siguiente Java lanzará una excep
ción, puesto que intenta asignar el valor del elem ento de subíndice 99 al elem ento
de subíndice 100, que está fuera del rango 0 a 99 válido.
i n t [ ] m = new i n t [ 1 0 0 ] ;
i n t k = 0 . a = 0 :
11...
k - 99:
m [k + l] = m [k ]:
int n = m.length; // nú me r o de e l e m e n t o s de l a m a t r i z n
Por ejem plo, el código expuesto a continuación crea una m atriz m 2 que es una
copia de o tra m atriz existente m i. D espués pregunta si m i es igual a m 2\ el resul
tado será false puesto que m i y m 2 se refieren a m atrices diferentes.
i n t [ ] mi = 110. 2 0 , 3 0 . 4 0 . 5 0 1 :
i n t [ ] m2 = ( i n t [ ] ) m l . e l o n e ( ) ; // m2 e s un a c o p i a de mi
i f ( m i . e q u a l s í m 2 ) ) // e q u i v a l e a: i f (mi =— m2)
S y s t e m . o u t . p r i n t l n ( " m i y m2 s e r e f i e r e n a l a misma m a t r i z " ) :
el se
S y s t e m . o u t . p r i n t l n í " m i y m2 s e r e f i e r e n a m a t r i c e s d i f e r e n t e s " ) :
Número de e l e m e n t o s d e l a m a t r i z : 10
I n t r o d u c i r l o s v a l o r e s de l a m a t r i z .
m [0]= 1
m[1 ] - 2
m [2]- 3
1 23 . . .
F i n del proceso.
Para ello, en prim er lugar definim os la variable nE lem entos para fijar el nú
m ero de elem entos de la m atriz, cream os la m atriz m con ese núm ero de elem en
tos y definim os el subíndice i para acceder a los elem entos de dicha matriz.
in t nElementos:
n E l e m e n t o s = L e e r . d a t o l n t í ):
i n t [ ] m = new i n t f n E l e m e n t o s ] : // c r e a r la m atriz m
i n t i - 0 : // s u b í n d i c e
// L e e r . c l a s s d e b e e s t a r en l a c a r p e t a especificada p o r C LA S S P A T H
p u b lic c la s s CMatrizUnidim ensional
I
// C r e a c i ó n d e una m a t r i z u n i d i m e n s i o n a l
public s t a t ic void m a in (S trin g [] args)
(
i n t nElementos:
S y s t e m . o u t . p r i n t ( " N ú m e r o de e l e m e n t o s de l a m a t r i z : " ) :
n E l e m e n t o s - L e e r , d a t o I n t ( );
i n t [ ] m = new i n t [ n E l e m e n t o s ] ; // c r e a r l a m a t r i z m
in t i = 0 ; / / subíndice
// V i s u a l i z a r l o s e l e m e n t o s de l a m atriz
S y s t e m . o u t . p r i n t l n t );
f o r (i = 0: i < nElementos: i++)
System.out.p r i nt(m[i ] + " ");
System .out.p r i n t l n ( " \ n \ n F in del proceso.'
El ejercicio anterior nos enseña cóm o leer una m atriz y cóm o escribirla. El
paso siguiente es aprender a trabajar con los valores alm acenados en la m atriz.
Por ejem plo, pensem os en un program a que lea la nota m edia obtenida p o r cada
alum no de un determ inado curso, las alm acene en una m atriz y dé com o resultado
la nota m edia del curso.
int nAlumnos; // nú me r o de a l u m n o s
do
I
S y s t e m . o u t . p r i n t í " N ú m e r o de a l u m n o s : "):
n A l u m n o s = L e e r . d a t o l n t í );
I
w h i le (nAlumnos < 1);
f 1o a t C ] n o t a = new f 1o a t t n A l u m n o s ] : // c r e a r l a m a t r i z nota
i n t i - 0: // s u b í n d i c e
f l o a t suma = 0 F ; // suma t o t a l de l a s n o t a s m e d i a s
El paso siguiente será alm acenar en la m atriz las notas introducidas a través
del teclado.
f o r ( i - 0; i < n o t a . l e n g t h : i++)
suma + - n o t a [ i ] :
S y s te m .o u t .p r in tln ("\n\nNota media de l curso: " + suma / n A l u m n o s ) :
// L e e r . c l a s s d e b e e s t a r en l a c a r p e t a especificada p o r C LA S S P A T H
pu blic c la s s CMatrizUnidim ensional
I
// T r a b a j a r c o n una m a t r i z u n i d i m e n s i o n a l
pu b lic s t a t ic void m a in (S trin g [] args)
I
int nAlumnos: // nú me r o de a l u m n o s ( v a l o r no n e g a t i v o )
do
1
S y s t e m . o u t . p r i n t í " N ú m e r o de a l u m n o s : "):
n A l u m n o s = L e e r . d a t o l n t ( ):
I
w hile (nAlumnos < 1);
f 1o a t [ ] n o t a = new f l o a t [ n A l u m n o s ] ; // c r e a r l a m a t r i z n o t a
int i = 0 ; // s u b í n d i c e
f l o a t suma = 0 F : // suma t o t a l d e l a s n o t a s m e d i a s
S y s t e m . o u t . p r i n t í n ( " I n t r o d u c i r l a s n o t a s medias del c u r s o . " ) :
17 2 JA V A: C U R S O DE PROGRAM A CIÓN
// S u m a r l a s n o t a s m e d i a s
f o r ( i = 0; i < n o t a . l e n g t h : i++)
suma + = n o t a [ i ] :
// V i s u a l i z a r l a n o t a m e d i a d e l c u r s o
S y s t e m . o u t . p r i n t l n í " \ n \ n N o t a media del c u r s o : ” + suma / nA l umn o s ) :
1
I
Matrices asociativas
C uando el índice de una m atriz se corresponde con un dato, se dice que la m atriz
es asociativa (por ejem plo, una m atriz d(asM es[13) que alm acene en el elem ento
de índice 1 los días del m es 1, en el de índice 2 los días del m es 2 y así sucesiva
m ente; ignoram os el elem ento de índice 0). En estos casos, la solución del pro
blem a resultará m ás fácil si utilizam os esa coincidencia. P or ejem plo, vam os a
realizar un program a que cuente el núm ero de veces que aparece cada una de las
letras de un texto introducido por el teclado y a continuación im prim a el resulta
do. Para hacer el ejem plo sencillo, vam os a suponer que el texto sólo contiene le
tras m inúsculas del alfabeto inglés (no hay ni letras acentuadas, ni la 11, ni la ñ).
La solución podría ser de la form a siguiente:
I n t r o d u c i r un t e x t o .
Para f i n a l i z a r p u l s a r [Ctrl]fz]
a b c d e f g h i j k l m n o p q r s t u v w x y z
9 1 1 3 5 0 0 0 9 0 0 6 4 6 3 0 0 1 II 2 2 0 0 0 1 1
C A PÍTU LO 7: MATRICES 173
da com o resultado 26. R ecuerde que cada carácter tiene asociado un valor entero
(código A SC II) que es el que utiliza la m áquina internam ente para m anipularlo.
A sí p o r ejem plo la ‘z ’ tiene asociado el entero 122. la V el 97. etc. Según esto, la
evaluación de la expresión a n te rio re s: 122 - 97 + 1 = 2 6 .
i n t [ ] c - new i n t [ 2 5 6 ] : // l a t a b l a A S C I I t i e n e 2 5 6 c a r a c t e r e s
char car - ' a ' : // c a r t i e n e a s i g n a d o e l e n t e r o 9 7
c [ * a *] - 10:
c[car] = 10:
c a r = ( c h a r ) S y s t e m . i n . r e a d ( ):
c[car]++:
Pero ¿qué pasa con los elem entos c¡0] a c [ 9 6 ] l Según hem os planteado el
problem a inicial quedarían sin utilizar (el enunciado decía: con qué frecuencia
aparecen los caracteres de la 'a ' a la 'z')- Esto, aunque no presenta ningún pro
blem a. se puede evitar así:
Para ca r igual a ‘a ' se trataría del elem ento c[0] y para ca r igual a 'z ' se trata
ría del elem ento c[25J. D e esta form a podem os definir una m atriz de enteros ju s
tam ente con un núm ero de elem entos igual al núm ero de caracteres de la 'a ' a la
Y (26 caracteres según la tabla ASCII). El prim er elem ento será el contador de
'a ', el segundo el de ‘b \ y a sí sucesivam ente.
Un contador es una variable que inicialm ente vale cero (suponiendo que la
cuenta em pieza desde uno) y que después se increm enta en una unidad cada vez
que ocurre el suceso que se desea contar.
i mpor t j a v a . i o . * ;
// L e e r . c l a s s d e b e e s t a r en l a c a r p e t a especificada p o r CLASSPATH
public class CMatrizAsociativa
I
// F r e c u e n c i a con la que a p a r e c e n l a s l e t r a s en un t e x t o ,
public static void m a in(S tring [] args)
I
// C r e a r l a m a t r i z c c o n ’ z ' - ' a ' + l e l e m e n t o s .
// J a v a i n i c i a l o s e l e m e n t o s de l a m a t r i z a c e r o ,
i n t [ ] c = new i n t [ ' z ’ - ’ a ’+ l ] ;
c h a r c a r : // s u b í n d i c e
f i n a l char eof = ( c h a r ) - l ;
// E n t r a d a de d a t o s y c á l c u l o de l a t a b l a de f r e c u e n c i a s
S y s t e m . o u t . p r i n t l n ( " I n t r o d u c i r un t e x t o . ” ):
S y s t e m . o u t . p r i n t l n ( “ P a r a f i n a l i z a r p u l s a r [ C t r l ] [ z ] \ n " );
try
I
// L e e r e l s i g u i e n t e c a r á c t e r d e l t e x t o y c o n t a b i l i z a r l o
while (( c a r = ( c h a r ) S y s t e m . i n . r e a d ( )) != eof)
I
// S i el c a r á c t e r l e í d o e s t á e n t r e l a ’ a ’ y la ’z '
// i n c r e m e n t a r e l c o n t a d o r c o r r e s p o n d i e n t e
i f ( c a r >= ' a ’ && c a r < = ’z' )
c[car - 'a']++:
c a t c h ( I O E x c e p t i o n i g n o r a d a ) I)
// M o s t r a r l a t a b l a de f r e c u e n c i a s
S y s t e m . o u t . p r i n t l n í " \ n " );
// V i s u a l i z a r una c a b e c e r a " a b e . . . "
f o r ( c a r = ' a ' : c a r < = ’z ' : c a r + + )
System .out.printí" " + car):
S y s t e m . o u t . p r i n t l n í " \ n ................................................................... ” +
" "):
CA PÍTU LO 7: M A TRICES 175
CADENAS DE CARACTERES
L as cadenas de caracteres en Java son objetos de la clase S tr in g . C uando expusi
mos los literales en el capítulo 3 vim os que cada vez que en un program a se utili
za un literal de caracteres, Java crea de form a autom ática un objeto S t r in g con el
v alor del literal. Por ejem plo, la línea de código siguiente visualiza el literal “Fin
del proceso.”, para lo cual. Java previam ente lo convierte en un objeto S trin g :
B ásicam ente, una cadena de caracteres se alm acena com o una m atriz unidi
m ensional de elem entos de tipo c h a r:
char[] c a d e n a = new c h a r f l O ] ;
Igual q ue sucedía con las m atrices num éricas, una m atriz unidim ensional de
caracteres puede ser iniciada en el m om ento de su definición. Por ejem plo:
Este ejem plo define cadena com o una m atriz de caracteres con cuatro ele
m entos (cadenafO J a cadena[3]) y asigna al prim er elem ento el carácter ‘a ’, al
segundo el carácter ‘b \ al tercero el carácter ‘c ’ y al cuarto el carácter ‘d \
Puesto que cada carácter es un entero, el ejem plo anterior podría escribirse
tam bién así:
C ada carácter tiene asociado un entero entre 0 y 65535 (código U nicode). Por
ejem plo, a la 'a ' le corresponde el valor 97, a la 7 / el valor 98, etc. (recuerde que
los prim eros 128 códigos U nicode coinciden con los prim eros 128 códigos ASCII
y A NSI: capítulo 3, tipo c h a r).
La llam ada a p r in tln perm ite visualizar la cadena. Se visualizan todos los c a
racteres hasta finalizar la m atriz, incluidos los nulos ( ‘\0 ’)-
c h a r [ ] c a d e n a = n e w c h a r [ 4 0 ] : // m a t r i z d e 4 0 c a r a c t e r e s
int i = 0 . car:
try
I
S y s t e m . o u t .p r i n t í " I n t r o d u c i r un texto: "):
while ( ( c a r - S y s t e m . i n .r e a d ( )) !- ' \ r ‘ && i< c a d e n a . l e n g t h )
I
cadenafi] - (charlear;
i++;
I
S y s t e m . o u t .p r i n t l n ( " T e x t o i n t r o d u c i d o : " + cadena);
S y s t e m . o u t . p r i n t l n ( " L o n g i t u d del t e x t o : " + i):
S y s t e m . o u t . p r i n t l n ( " D i m e n s i ó n d e la m a t r i z : " + c a d e n a .l e n g t h ) :
I
catch(IOException ignorada) II
El ejem plo anterior define la variable cadena com o una m atriz de caracteres
de longitud 40. Después establece un bucle para leer los caracteres que se tecleen
hasta que se pulse la tecla Entrar. C ada carácter leído se alm acena en la siguiente
posición libre de la m atriz cadena. Finalm ente se escribe el contenido de cadena,
el núm ero de caracteres alm acenados, y la dim ensión de la m atriz. Se puede o b
servar que el valor dado por el atributo length no es el núm ero de caractere
m acenado en cadena, sino la dim ensión de la m atriz.
O bserve que el bucle utilizado para leer los caracteres tecleados, podría ha
berse escrito tam bién así:
CA PÍTU LO 7: M A TRICES 177
En el capítulo 5 vim os tam bién otra form a de leer una cadena de caracteres.
C onsiste en leer una línea de texto de un flujo de la clase B u ffe re d R e a d e r. co
nectado al flujo in. utilizando el m étodo re a d L in e , y alm acenarla en un objeto
S trin g. El m étodo re a d L in e lee hasta encontrar el carácter V \ ‘Vi’ o los caracte
res W i ’ introducidos al pulsar la tecla E n tra n estos caracteres son leídos pero no
alm acenados, sim plem ente son interpretados com o delim itadores. Por ejem plo:
// D e f i n i r un f l u j o de c a r a c t e r e s de e n t r a d a : f l u j o E
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ):
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r ( i s r ) ;
El ejem plo anterior d efine en prim er lugar un flujo de entrada, flu jo E , del cual
se podrán leer líneas de texto. D espués, define una referencia, flu jo S , al flujo de
salida estándar; esto perm itirá utilizar la referencia flu jo S en lugar de System .out.
Finalm ente lee una línea de texto introducida a través del teclado. C on esa infor
m ación, el m étodo r e a d L in e crea un objeto y devuelve una referencia al m ism o
que es alm acenada en cadena. F inalm ente, la llam ada a p rin tln perm ite visualizar
el objeto Strin g.
char[] c a d e n a = new c h a r [ 4 0 ] : // m a t r i z de 4 0 c a r a c t e r e s
// . . .
String scadena - new S t r i n g ( c a d e n a ) ;
178 JA V A: CU R SO DE PRO G R A M A CIÓ N
E s c r i b a una c a d e n a d e c a r a c t e r e s :
Hola ¿qué t a l ?
C a r á c t e r - ' H \ c ó d i g o A S C I I = 72
C a r á c t e r = ' o ' . c ó d i g o A S C I I = 111
O bservar que. el m étodo p rin tln visualiza un elem ento de tipo c h a r com o un
carácter; p o r lo tanto, para visualizar su valor A SC II es necesario convertirlo ex
plícitam ente a in t. El program a com pleto se m uestra a continuación.
import j a v a . i o . * :
public c la ss CValorAscii
I
// E x a m i n a r una c a d e n a de c a r a c t e r e s a l m a c e n a d a en un a m a t r i z
p u b lic s t a t ic void m a in (S trin g [] args)
1
c h a r [ ] cadena - new c h a r [ 8 0 ] ¡ // m a t r i z de c a r a c t e r e s
i n t c a r , i - 0; // un c a r á c t e r y e l s u b í n d i c e p a r a l a matriz
try
1
S y s t e m . o u t . p r i n t í n ( " E s c r i b a una c a d e n a d e c a r a c t e r e s : ” ):
w h i l e ( ( c a r - S y s t e m . i n . r e a d ( ) ) ! = ' \ r ' && i < c a d e n a . l e n g t h )
cadena[i++] = (char)car;
// E x a m i n a r l a m a t r i z de c a r a c t e r e s
i - 0;
do
1________________________________________________________________
System.out.println("Carácter - * + cadena[i] +
", c ó d i g o ASCII - " + ( i n t ) c a d e n a [11):
i++:
I
while (i < c a d e n a . 1 e n g t h && c a d e n a [ i ] != '\0 ') ¡
I
catch(IOException ignorada) (I
CA PÍTU LO 7: M A TRICES 179
cadena H H | o 1 I | a | 1 i. | q 1 u | é 1 t | a | I | ? \0 \ÓTT
O bservar que el bucle utilizado para exam inar la cadena, para i igual a 0 ac
cede al prim er elem ento de la m atriz, p ara i igual a 1 al segundo, y así hasta lle
gar al final de la m atriz o hasta encontrar un carácter nulo ( ‘\0 ’) que indica el final
de los caracteres tecleados.
En el siguiente ejem plo se trata de escribir un program a que lea una línea de
la entrada estándar y la alm acene en una m atriz de caracteres. A continuación,
utilizando un m étodo, deseam os convertir los caracteres escritos en m inúsculas, a
m ayúsculas.
Si observa la tabla ASCII en los apéndices de este libro, com probará que los
caracteres 'A '...... * Z \ ‘a ’ V están consecutivos y en orden ascendente de su
código (valores 65 a 122). Entonces, pasar un carácter de m inúsculas a m ayúscu
las supone restar al v alor entero (código A SC II) asociado con el carácter, la dife
rencia entre los códigos de ese carácter en m inúscula y el m ism o en m ayúscula.
Por ejem plo, la diferencia ’a ’- 'A ’ es 9 7 - 32 = 65, y es la m ism a que 'b '- 'B ', que
’c ’- 'C ', etc. C om o ayuda relacionada con lo expuesto, puede repasar los concep
tos que se expusieron en el apartado "M atrices asociativas” expuesto anterior
m ente en este m ism o capítulo.
El m étodo que realice esta operación recibirá com o parám etro la m atriz de ca
racteres que contiene el texto a convertir. Si el m étodo se llam a M inusculasM a-
yusculas y la m atriz cadena, la llam ada será así:
import j a v a , i o . * ;
p u b 1 i c e l a s s CCadenas
I
// C o n v e r t i r una c a d e n a a M a y ú s c u l a s
s t a t i c void M in u sc u la sM a y u sc u la s(c h a r[] str)
I
i nt i = 0. desp = ' a ' - ' A ' :
f o r ( i = 0 ; i < s t r . 1e n g t h && s t r [ i ] ! = ’ \ 0 ’ : 1+ + )
i f ( s t r [ i ] > = ’ a ’ && s t r f i ] < = ’ z ’ )
s t r [ i ] = ( c h a r ) { s t r t i ] - desp);
try
I
S y s t e m . o u t . p r i n t l n ( " E s c r i b a una c a d e n a de c a r a c t e r e s : ” );
w h i l e ( ( c a r = S y s t e m . i n . r e a d ( ) ) ! = ’ \ r ' && i < c a d e n a . 1 e n g t h )
cadena[i++] = (char)car;
// C o n v e r t i r m i n ú s c u l a s a m a y ú s c u l a s
M i n u s c u l a s M a y u s c u l a s t c a d e n a ) ; // l l a m a r a l m ét o d o
S y s t e m . o u t . p r i n t l n ( c a d e n a );
I
catch(IOException ignorada) II
Clase String
String(String valor)
En el capítulo 4 hicim os un breve com entario acerca de que toda clase tiene al
m enos un m étodo predeterm inado especial denom inado igual que ella, que es ne
cesario invocar para crear un objeto; se trata del constructor de la clase, del cual
aprenderem os m ás en un capítulo posterior. Según esto, S t r in g es el constructor
de la clase S tr in g . A nteriorm ente, trabajando con cadenas de caracteres, vim os
cóm o u tilizar este constructor para crear un objeto S t r in g a partir de una matriz
de caracteres. Pero en la m ayoría de los casos lo utilizarem os para crear un objeto
S t r in g a partir de un literal o a partir de otro S trin g . P or ejem plo, cada una de las
líneas siguientes crea un S trin g . D ejam os para un próxim o capítulo las diferen
cias que hay entre utilizar u na u otra forma, puesto que no repercuten en el código
que escribim os debido a que los S t r in g son objetos no m odificables.
18 2 JA V A: C U R SO DE PROGRAM A CIÓN
String toString()
Este m étodo devuelve el propio objeto S t r in g que recibe el m ensaje to S trin g. Por
ejem plo, el siguiente código copia la referencia s tr l en s t r l (no crea un objeto
nuevo referenciado p o r str2, a partir de s tr l) . El resultado es que las dos variables,
s tr l y str2, perm iten acceder al m ism o o bjeto S trin g.
S tr in g s t r l = "abe". str2;
s t r 2 = s t r l . t o S t r i n g ( ) ; // e q u i v a l e a str2 = strl
S t r in g s t r l = "abe", s t r 2 - "Abe":
i f ( s t r l . c o m p a r e T o ( s t r 2 ) < 0)
S y s t e m . o u t . p r i n t l n ( s t r l ):
S t r i n g strtemp;
in t resultado:
if ( resultado > 0 )
s t r t e m p = " m a y o r que " ;
else i f ( resultado < 0 )
s t r t e m p = " m e n o r que
el se
strtemp = "ig u a l a ";
Sy ste m .o u t.p rin tln t s t r l + " es " + strtemp + s t r 2 ):
1
184 JA V A: C U R SO DE PROGRAM A CIÓN
int iength()
Este m étodo devuelve la longitud o núm ero de caracteres U nicode (tipo c h a r) del
objeto S t r in g que recibe el m ensaje length.
String toLowerCase()
Este m étodo convierte a m inúsculas las letras m ayúsculas del objeto S t r in g que
recibe el m ensaje to L o w e rC a se . El resultado es un nuevo objeto S t r in g en mi
núsculas.
String tollpperCase()
E ste m étodo convierte a m ayúsculas las letras m inúsculas del objeto S t r in g que
recibe el m ensaje to U p p e rC a se . El resultado es un nuevo objeto S t r in g en ma
yúsculas.
String trim()
Este m étodo devuelve un valor true si el su fijo especificado coincide con el final
del objeto S t r in g que recibe el m ensaje e n d sW ith . Un poco m ás adelante se
m uestra un ejem plo.
CA PÍTU LO 7: M A TRICES 185
El siguiente ejem plo, elim ina los espacios en blanco que haya al principio y al
final de s t r l , verifica si s tr I finaliza con “gh” y en caso afirm ativo obtiene de s tr l
una subcadena str2 igual a s tr l m enos el sufijo “g h ” .
S t r i n g s t r l - " abcdefgh ” , s t r 2 =
s t r l = s t r l . t r i m t ):
i f ( s t r l . e n d s W i t h ( " g h ” ))
s t r 2 = s t r l . s u b s t r i n g ( 0 . s t r l .1e n g th ( ) - " g h " .1e n g t h ( )):
Este m étodo devuelve el índice de la prim era ocurrencia del carácter especificado
por c a r en el objeto S t r in g que recibe el m ensaje in d ex O f. Si ca r no existe el
m étodo in d e x O f devuelve el valor -1. Puede com enzar la búsqueda por el final en
lugar de hacerlo p o r el principio utilizando el m étodo la stln d e x O f.
E ste m étodo devuelve un nuevo S t r in g resultado de reem plazar todas las ocurren
cias ca r p o r n u evoC ar en el objeto S t r in g que recibe el m ensaje replace. Si el ca
rácter ca r no existiera, entonces se devuelve el objeto S t r in g original.
186 JA VA : C U R S O DE PROGRAM A CIÓN
E ste m étodo devuelve un nuevo S t r in g creado a partir del dato pasado com o ar
gum ento. P uesto que el m étodo es static no necesita ser invocado para un objeto
S trin g. El argum ento puede ser de los tipos boolean, ch ar. char[], int, long,
float, d ouble y Object.
d o u b le pi = M a t h . P I :
S t r i n g s t r l = S t r i n g . v a l u e O f ( p i );
char[] toCharArray()
Este m étodo devuelve una m atriz de caracteres creada a partir del objeto S t r in g
que recibe el m ensaje to C h a r A r r a y .
S t r i ng s t r = " a b c d e " :
c h a r [ ] mear = s t r . t o C h a r A r r a y t ) ;
byteQ getBytes()
E ste m étodo devuelve una m atriz de bytes creada a partir del objeto S t r in g que
recibe el m ensaje getBytes.
Clase StringBuffer
D el estudio de la clase S t r in g sabem os que un objeto de esta clase no es m odifi-
cable. Se puede observar y com probar que los m étodos que actúan sobre un ob
je to S t r in g con la intención de m odificarlo, no lo m odifican, sino que devuelven
un objeto nuevo con las m odificaciones solicitadas. En cam bio, un objeto S t r in g
B u ffe r es un objeto m odificable tanto en contenido com o en tamaño.
StringBuffer(/arg/)
Este m étodo perm ite crear un objeto de la clase S tr in g B u ffe r . El siguiente ejem
plo m uestra las tres form as posibles de invocar a este método:
S t r i n g B u f f e r s t r b l = new S t r in g B u f f e r ();
S t r i n g B u f f e r s t r b 2 = new S t r in g B u f f e r (80);
S t r i n g B u f f e r s t r b 3 = new S t r i n g B u f f e r ( ' ' a b c d e ” );
System.out.pri n t l n ( s t r b l + ” ” + s t r b l . 1e n g t h ( ) + ” “ + s t r b l . c a p a c i t y ( ) ) :
System.out.println(strb2 + ” ” + s t r b 2 . i e n g t h ( )+ ” " + s t r b 2 . c a p a c i t y ( )):
C A P ÍT U L O 7: M A TRICES 187
0 16
0 80
a b c d e 5 21
int length()
Este m étodo devuelve la longitud o núm ero de caracteres U nicode (tipo c h a r) del
objeto S t r in g B u f fe r que recibe el m ensaje length. E sta longitud puede ser m odi
ficada por el m étodo se tL e n g th cuando sea necesario.
int capacity()
StringBuffer append(tipo x)
E ste m étodo perm ite añadir la cadena de caracteres resultante de convertir el ar
gum ento x en un objeto S trin g , al final del objeto S t r in g B u f f e r que recibe el
m ensaje append. El tipo del argum ento x puede ser boolean, char, char[], int,
long. float, double, S t r in g y O bject. La longitud del objeto S t r in g B u f f e r se in
crem enta en la longitud correspondiente al S t r in g añadido.
E ste m étodo perm ite insertar la cadena de caracteres resultante de convertir el ar
gum ento x en un objeto S tr in g , en el objeto S t r in g B u f f e r que recibe el m ensaje
inserí. Los caracteres serán añadidos a partir de la posición especificada por el
argum ento índice. El tipo del argum ento x puede ser boolean. char. char| 1. int.
long. float, double. S t r in g y O bject. La longitud del objeto S t r in g B u f f e r se in
crem enta en la longitud correspondiente al S t r in g insertado.
188 JA V A: C U R S O DE PROGRAM A CIÓN
Este m étodo elim ina los caracteres que hay entre las posiciones p l y p 2 - 1 del
objeto S t r in g B u ff e r que recibe el m ensaje delete. El valor p 2 debe ser m ayor que
p l . Si p l es igual que p 2 . no se efectuará ningún cam bio y si es m ayor Java lanza
rá una excepción.
Partiendo del ejem plo anterior, el siguiente ejem plo elim ina la subcadena
“A bril ” del objeto strb y añade en su m ism a posición la cadena “M ayo ” , El re
sultado será “ M es de M ayo del año 2002” .
S t r i n g B u f f e r s t r b = new S t r i n g B u f f e r ( " M e s de d e l a ñ o ” ):
s t r b . i n s e r t t 7. " A b r i l " ) ;
strb.append(2002):
s t r b . d e l e t e t 7. 1 3 ) :
s t r b . i n s e r t ( 7 . " M a y o ” ):
Este m étodo reem plaza los caracteres que hay entre las posiciones p l y p 2 - 1 del
objeto S t r in g B u f fe r que recibe el m ensaje replace, por los caracteres especifica
dos por str. La longitud y la capacidad del objeto resultante serán ajustadas auto
m áticam ente al valor requerido. El valor p 2 debe ser m ayor que p l . Si p l es igual
que p 2 , la operación se convierte en una inserción, y si es m ayor Java lanzará una
excepción. Según lo expuesto, el ejem plo anterior, puede escribirse tam bién así:
StringBuffer reverse()
Este m étodo reem plaza la cadena alm acenada en el objeto S t r in g B u f f e r que reci
be el m ensaje reverse, por la m ism a cadena pero invertida.
CA PÍTU LO 7: M A TRICES 189
Este m étodo reem plaza el carácter que está en la posición especificada en el ob
je to S t r in g B u ff e r que recibe el m ensaje s e tC h a rA t, por el nuevo carácter espe
cificado. El índice del prim er carácter es el 0. P or lo tanto, el parám etro índice
tiene que estar entre los valores 0 y le n gth O - 1.
String toString()
Clase StringTokenizer
E sta clase, perteneciente al paquete java.u til, perm ite dividir una cadena de ca
racteres en una serie de elem entos delim itados por unos determ inados caracteres.
De form a predeterm inada los delim itadores son: el espacio en blanco, el tabulador
horizontal (\t), el carácter nueva línea (\n), el retorno de carro (\r) y el avance de
página (\f).
S t r i n g T o k e n i z e r cadena:
c a d e n a = new S t r i n g T o k e n i z e r í " u n o , dos. tres y cuatro"):
1 9 0 JA V A: CU R SO DE PROGRAM A CIÓN
Para obtener los elem entos de la cadena separados por los delim itadores, en
este caso predeterm inados, utilizarem os los m étodos h a s M o r e T o k e n s para saber
si hay m ás elem entos en la cadena, y n e x tT o ke n para obtener el siguiente ele
m ento. Por ejem plo:
w h i 1e ( c a d e n a . h a s M o r e T o k e n s ( ) )
System .out.printlntcadena.nextTokenO);
un o,
dos.
tres
y
cuatro
En este caso, el resultado que se obtendrá a partir del objeto cadena es el si
guiente:
uno
dos
tres
y
cuatro
A hora el resultado será el siguiente (las líneas som breadas corresponden a los
delim itadores):
CA PITU LO 7: M A TRICES 191
tres
cuatro
U na m atriz m ultidim ensional, com o su nom bre indica, es una m atriz de dos o
m ás dim ensiones. Java no soporta m atrices m ultidim ensionales, pero se puede lo
g rar la m ism a funcionalidad declarando m atrices de m atrices; las cuales, a su vez,
192 JA V A: C U R SO D E PROGRAM A CIÓN
pueden tam bién contener m atrices, y así sucesivam ente, hasta llegar a obtener el
núm ero de dim ensiones deseadas.
Por ejem plo, en el caso de las tem peraturas podríam os definir una m atriz de
12 elem entos para que cada uno de ellos alm acenara una referencia a una matriz
unidim ensional de 31 elem entos; y en el caso de los nom bres podríam os definir
u na m atriz de n elem entos para que cada uno d e ellos alm acenara una referencia a
una m atriz unidim ensional de m caracteres (un nom bre). L as figuras m ostradas en
los siguientes apartados le ayudarán a com prender lo expuesto.
t/po[][]... n o m b r e _ m a t r i z = new t i p o í e x p r - ] ] [ e x p r - 2 ] . . . ;
donde tipo es un tipo prim itivo entero o real. El núm ero de elem entos de una ma
triz m ultidim ensional es el producto de las dim ensiones indicadas por expr-1,
e x p r -2 ,... Por ejem plo, la línea de código siguiente crea una m atriz de dos dim en
siones con 2x3 = 6 elem entos de tipo int:
i n t [ ] [ ] m = n e w i n t [ 2 ] [ 3 ] ;
A partir de la línea de código anterior. Java crea una m atriz unidim ensional m
con 2 elem entos m /0 ] y m [ l] que son referencias a o tras dos m atrices unidim en
sionales de 3 elem entos. G ráficam ente podem os im aginarlo así:
m a triz m — mo m,
mío mu m t2 fila 1
i n t [ ) [ ] m = new i n t [ 2 ] [ 3 ] ;
System .out.println(m .length); // r e s u l t a d o : 2
S y s t e m . o u t . p r i n t l n ( m [ 0 ] . 1 e n g t h ); // r e s u l t a d o : 3
S y s t e m . o u t . p r i n t l n ( m [ 1 ] . 1 e n g t h ); // r e s u l t a d o : 3
CA PÍTU LO 7: MATRICES 193
D esde nuestro punto de vista, cuando se trate de m atrices de dos dim ensiones,
es m ás fácil pensar en ellas com o si de una tabla d e / filas p o r c colum nas se trata
ra. Por ejem plo:
matriz m — col 0 col 1 col 2
fila 0 moo mot mo2
fila 1 mío mu mi2
Para acceder a los elem entos de la m atriz m, puesto que se trata de una matriz
de dos dim ensiones, utilizarem os dos subíndices, el prim ero indicará la fila y el
segundo la colum na donde se localiza el elem ento, según se puede observar en la
figura anterior. Por ejem plo, la prim era sentencia del ejem plo siguiente asigna el
valor x al elem ento que está en la fila 1, colum na 2; y la segunda, asigna el valor
de este elem ento al elem ento m[OJ[ 1 ¡.
m [l][2] - x:
m [0 ][1 ] - m [1 ] [ 2 ] :
Número de f i l a s de la m a t r i z : 2
Número de c o lu m n a s de l a m a t r i z : 2
Introducir l o s v a l o r e s de l a m a t r i z
m[ 0 D[ 0 ] - 2
m [ 0 ] [ 1] = S
m[1] [ 0 ] - 3
m[1 ] [ 1] = 6
Suma de l a f i l a 0: 7 . 0
Suma de l a f i l a 1: 9 . 0
F i n del proceso
En prim er lugar definim os las variables que alm acenarán el núm ero de filas y
de colum nas de la m atriz, y a continuación leem os esos valores del teclado, dese
chando cualquier valor negativo.
do
I
S y s t e m . o u t . p r i n t ( " N ú m e r o de c o l u m n a s de l a m a t r i z : ");
n c o l s = L e e r , d a t o I n t ( );
I
while (ncols < 1): // no p e r m i t i r un v a l o r negativo
D espués, cream os la m atriz m con el núm ero de filas y colum nas especifica
do, definim os las variables fila y col que utilizarem os para m anipular los subíndi
ces correspondientes a la fila y a la colum na, y la variable sum afila para
alm acenar la sum a de los elem entos de una fila:
f l o a t [ ] [ ] m = new f l o a t [ n f i 1 a s ] [ n c o l s ] : // c r e a r l a m a t r i z m
i n t f i l a = 0 , c o l = 0: // s u b í n d i c e s
f l o a t s u m a f i l a = 0: // suma de l o s e l e m e n t o s de una f i l a
// L e e r . c l a s s d e b e e s t a r en l a c a r p e t a especificada p o r C LA S S P A T H
p u b lic c la s s CM atrizM ultidim ensional
I
// C r e a c i ó n de una m a t r i z m u l t i d i m e n s i o n a l .
// Suma de l a s f i l a s d e una m a t r i z de d o s d i m e n s i o n e s ,
pu blic s t a t i c void main( S t r i n g [ ] args)
I
int n f ila s . ncols: // f i l a s y c o l u m n a s de l a m atriz
CA PÍTU LO 7: M A TRICES 195
do
I
S y s t e m . o u t . p r i n t ( " N ú m e r o de f i l a s de l a m atriz: "):
n f i l a s - L e e r . d a t o l n t t ):
I
w hile (nfilas < 1): // no p e r m i t i r un v a l o r negativo
do
I
S y s t e m . o u t . p r i n t t " N ú m e r o de c o l u m n a s de l a m atriz: "):
n c o l s = L e e r . d a t o l n t í );
I
w hile (ncols < 1): // no p e r m i t i r un v a l o r negativo
f l o a t [ ] [ ] m = new f l o a t [ n f i 1 a s ] [ n c o l s ] ; II c r e a r l a m a t r i z m
i n t f i l a = 0. c o l = 0 : // s u b í n d i c e s
f l o a t s u m a f i l a = 0: // suma de l o s e l e m e n t o s de una f i l a
// V i s u a l i z a r l a suma de c a d a fila de l a m a t r i z
S y s t e m . o u t . p r i n t l n ( );
f o r ( f i l a = 0: f i l a < n f i l a s ; fila++)
I
s u m a f i l a - 0;
f o r ( c o l - 0; c o l < n c o l s ; c o l + + )
s u m a f i l a + - m [ f i 1 a ] [ c o l 3:
S eguram ente habrá pensado que la sum a de cada fila se podía haber hecho
sim ultáneam ente a la lectura tal com o se indica a continuación.
m C f 11 a ] [ c o l ] = L e e r . d a t o F l o a t ( );
s u m a fila += m [ f i l a ] [ c o l ];
I
S y s t e m . o u t . p r i n t l n í " S u m a de l a fila " + fila + " + suma f i l a ) :
I
Número de f i l a s de l a m a t r i z : 2
Número de c o l u m n a s de l a m a t r i z : 2
Introducir l o s v a l o r e s de l a m a t r i z .
m[0][0) - 2
m [ 0 ] [ 1) - 5
Suma de l a f i l a 0: 7.0
m [l][0] - 3
m[1 ] [ 1] - 6
Suma de l a f i l a 1: 9 . 0
F in del proceso.
Con este últim o planteam iento, una solución para escribir los resultados al fi
nal sería alm acenarlos en una m atriz unidim ensional y m ostrar posteriorm ente la
m atriz. E ste trabajo se deja com o ejercicio para el lector.
H aciendo un estudio análogo al realizado para las m atrices num éricas m ulti
dim ensionales, la definición de una m atriz de cadenas de caracteres puede hacerse
de la form a siguiente:
Por ejem plo, la línea de código siguiente crea una m atriz de cadenas de ca
racteres de F filas por C caracteres m áxim o por cada fila.
char[][] m - new c h a r [ F ] [ C ] :
A partir de la línea de código anterior, Java crea una m atriz unidim ensional m
con los elem entos, m [0], m [ l ] m [F -IJ, que son referencias a otras tantas m a
C A PÍTU LO 7: M A TRICES 197
mp
mi
m2
m3
Para acceder a los elem entos de la m atriz m , puesto que se trata de una m atriz
de cadenas de caracteres, utilizarem os sólo el prim er subíndice, el que indica la
fila. Sólo utilizarem os dos subíndices cuando sea necesario acceder a un carácter
individual. P o r ejem plo, la prim era sentencia del ejem plo siguiente crea una m a
triz de cadenas de caracteres. La segunda asigna una cadena de caracteres a m [0]
desde el teclado; la cadena tendrá nC arsP orF ila caracteres com o m áxim o y será
alm acenada a p artir de la posición 0 de m [0]. Y la tercera sentencia, reem plaza el
últim o carácter leído en m [0] p o r ‘\ 0 \ puesto que re a d devuelve el núm ero de ca
racteres leídos.
c h a r [ ] [ ] nombre = new c h a r [ n F i 1 a s ] [ n C a r s P o r F i 1 a ] :
n C a r s L e i d o s = f l u j o E . r e a d ( m [ 0 ] . 0 , n C a r s P o r F i 1 a );
nom bre[0][nCarsLeidos-l] = ’\ 0 ’ :
Es im portante que asim ile que m [0J, m [l], etc. son cadenas de caracteres y
que, por ejem plo, m [l][3 ] es un carácter; el que está en la fila 1, colum na 3.
Número de f i l a s d e l a m a t r i z : 10
Número de c a r a c t e r e s p o r f i l a : 40
19 8 JA V A: CU R SO DE PROGRAM ACIÓN
E s c r i b a l o s n o m b r e s que d e s e a i n t r o d u c i r .
Puede f i n a l i z a r p u l s a n d o l a s t e c l a s [ C t r l ] [ Z ] .
N o m b r e [ 0 ] : Ma d e l Carmen
Nombre[l]: F ra n cisco
Nombre[2]: J a v i e r
Nombre[3]: [C trlD [Z ]
¿ D e s e a v i s u a l i z a r el c o n t e n i d o d e l a m a t r i z ? ( s / n ) : S
Ms d e l Carmen
F ra n c i seo
Javier
import j a v a . i o . * :
// U t i l i z a L e e r . c l a s s q u e e s t á en C L A S S P A T H = c : \ j d k l . 3 \ m i s C l a s e s
pu blic c la s s CMatrizlCadenas
I
public static void m a i n ( S t r i n g [ ] arg s)
I
try
I
// D e f i n i r un f l u j o de c a r a c t e r e s d e e n t r a d a : f l u j o E
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) ;
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r í i s r ):
// D e f i n i r una r e f e r e n c i a a l f l u j o e s t á n d a r de s a l i d a : f l u j o S
PrintStream flu jo S = System.out;
i n t nF i l a s = 0 . n C a r s P o r F i l a = 0;
i n t f i l a = 0, n C a r s L e i d o s - 0. e o f = - 1 ;
do
I
S y s t e m . o u t . p r i n t ( " N ú m e r o de f i l a s de l a m a t r i z : ");
n F i l a s = L e e r , d a t o I n t ( ):
1
while (nFilas < 1); // no p e r m i t i r un v a l o r negativo
CA PÍTU LO 7: M A TRICES 199
do , ,»
• q f,} \.j,y j , i l / . t í l i f V , :i I
S y s t e m . o u t . p r i n t í " N ú m e r o de c a r a c t e r e s por f i l a : ");
nCarsPorFila = Lee r.d atoln tí);
j
w hile (nCarsPorFila < 1); // no p e r m i t i r un v a l o r n e g a t i v o
// M a t r i z de c a d e n a s de c a r a c t e r e s
e h a r [ ] [ ] nombre - new c h a r [ n F i l a s ] [ n C a r s P o r F i 1 a ] :
El identificador nom bre hace referencia a una m atriz de caracteres de dos di
m ensiones. U na fila de esta m atriz es una cadena de caracteres (una m atriz de ca
racteres unidim ensional) y la biblioteca de Java provee el m étodo re a d para leer
m atrices unidim ensionales de caracteres. Por eso. para leer una fila (una cadena
de caracteres) utilizam os sólo un índice. Esto no es aplicable a las m atrices num é
200 JA V A: C U R SO DE PROGRAM A CIÓN
ricas de dos dim ensiones, ya que la biblioteca de Java no proporciona m étodos pa
ra leer filas com pletas, lo cual es lógico.
O bserve la sentencia:
r e s p u e s t a = ( ( f 1u j o E . r e a d L i n e ( ) ) . t o L o w e r C a s e ( ) ) . c h a r A t í 0 ):
Es equivalente a:
S t r i n g s = f 1u j o E . r e a d L i n e ( ) ; II l e e r una l i n e a de t e x t o
s = s . t o L o w e r C a s e ( ): // c o n v e r t i r e l t e x t o a m a y ú s c u l a s
respuesta = s.ch a rA t(O ); // o b t e n e r e l p r i m e r c a r á c t e r l e í d o
¿D e qué longitud son las cadenas de caracteres nom bre/0¡, n o m b re/1], etc.?
Independientem ente del núm ero de caracteres leídos para cada uno de los nom
b res solicitados, todas son de la m ism a longitud: nC arsP orF ila caracteres
(recuerde que las m atrices de caracteres son iniciadas con nulos); para verificarlo
puede recurrir al atributo length de las m atrices. E videntem ente, esta form a de
proceder supone un derroche de espacio de m em oria, que se puede evitar hacien
do que cadena fila de la m atriz nom bre tenga una longitud igual al núm ero de ca
racteres del nom bre que alm acena. A pliquem os esta teoría al program a anterior.
L eem os las cadenas de caracteres. Para poder leer una cadena, necesitam os
definir una m atriz de caracteres que vam os a denom inar unN om bre. Esta será
una m atriz unidim ensional de longitud 81 caracteres, por ejem plo.
// A ñ a d i r e l nombre l e í d o a l a m a t r i z nombre
n o m b r e f f i l a ] = new c h a r [ n C a r s L e i d o s - 2 ] : // men os CR LF
f o r ( i n t i = 0 : i < n C a r s l e i d o s - 2: i + + )
n o m b r e t f i 1a ] [ i ] = u n N o m b r e f i ] : // c o p i a r
unNombre J \r \n
L a sentencia nom bre ¡fila I = new char[nC arsLeidos-2] asigna para cada valor
de fila un espacio de m em oria de nC arsLeidos-2 caracteres (en la figura: fila
O .fila / . f i l a 2, etc.), para copiar la cadena leída a través de unN om bre. Re
cuerde que el m étodo re a d devuelve el núm ero de caracteres leídos.
import j a v a . i o . * ;
// U t i l i z a L e e r . c l a s s que e s t á en C l A S S P A T H = c : \ j d k l . 3 \ m i s C l a s e s
p u b lic c l a s s CMatriz2Cadenas
1
public static void m a in (S trin g [] args)
(
try
I
// D e f i n i r un f l u j o de c a r a c t e r e s de e n t r a d a : f l u j o E
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) ;
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r ( i s r );
// D e f i n i r una r e f e r e n c i a al f l u j o e s t á n d a r de s a l i d a : flujoS
PrintStream f l u j o S = System.out;
i n t n F i l a s = 0. n C a r s P o r F i l a = 8 1 ;
i n t f i l a = 0, n C a r s L e i d o s = 0 , e o f = - 1 ;
c h a r [ ] unNombre « new c h a r í n C a r s P o r F i 1 a ] ;
do
[
S y s t e m . o u t . p r i n t t " N ú m e r o de f i l a s de l a m atriz: ");
n F i l a s = L e e r . d a t o l n t í );
I
w hile (nFilas < 1): // no p e r m i t i r un v a l o r n e g a t i v o
// M a t r i z de c a d e n a s de c a r a c t e r e s
c h a r n n nombre = new c h a r [ n F i l a s ] [ ] :
flujoS.print("\n.\n");
n F i l a s = f i l a ; , II nú mero de f i l a s l é i d a s
// . . .
// c o n t i n ú a i g u a l q u e en l a v e r s i ó n a n t e r i o r
I
catch (IQException ignorada) I I
C A P ÍT U L O 7: M A TRICES 2 0 3
Para com probar lo expuesto, vam os a resolver el program a anterior p ero utili
zando una m atriz de objetos S tr in g . El proceso que seguirem os es el siguiente:
C ada elem ento de esta m atriz será iniciado p o r Java con el v alor nuil, indi
cando así que la m atriz inicialm ente no referencia a ningún objeto S trin g;
esto es, la m atriz está vacía.
• L eem os las cadenas de caracteres. Para poder leer una cadena, utilizarem os el
m étodo re ad Lin e . R ecuerde que este m étodo perm ite leer una cadena de ca
racteres hasta encontrar un carácter V ’, ‘\w* o 'W ¡ ' (estos caracteres son leí
dos pero no alm acenados) y devuelve una referencia a un objeto S t r in g que
alm acena la inform ación leída; referencia que asignarem os al siguiente ele
m ento vacío de la m atriz nom bre. E ste proceso lo repetirem os para cada uno
de los nom bres que leam os. R ecuerde tam bién que si el m étodo re a d L in e in
tenta leer del flujo y se encuentra con el final del m ism o, retom ará la cons
tante nuil.
• Una vez leídos todos los nom bres deseados los visualizam os si la respuesta a
la petición de realizar este proceso es afirm ativa.
import j a v a . i o . * :
// U t i l i z a L e e r . c l a s s que e s t á en C L A S S P A T H - c : \ j d k l . 3 \ m i s C l a s e s
public c la ss CMatriz3Cadenas
I
public static void m a in (S trin g [] args)
I
try
1
// D e f i n i r un f l u j o de c a r a c t e r e s de e n t r a d a : f l u j o E
I n p u t S t r e a m R e a d e r i s r - new I n p u t S t r e a m R e a d e r ( S y s t e m , i n ) ;
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r ( i s r ):
// D e f i n i r una r e f e r e n c i a a l f l u j o e s t á n d a r de s a l i d a : flujoS
PrintStream f lu j o S - System.out:
i n t nFi l a s = . fila - 0:
do
I
S y s t e m . o u t . p r i n t ( " N ú m e r o de f i l a s de l a m a t r i z : ” );
n F i l a s = L e e r . d a t o l n t í ):
I
w hile (nFilas < 1): // no p e r m i t i r un v a l o r n e g a t i v o
// M a t r i z de c a d e n a s de c a r a c t e r e s
S t r i n g E ] n o mb r e - new S t r i n g [ n F i 1 a s ] ;
S t r i n g B u f f e r f ] no mb r e - new S t r i n g B u f f e r [ n F i l a s ] ;
S t r i n g s N o mb r e ;
EJERCICIOS RESUELTOS
1. R ealizar un program a que lea una lista de valores introducida por el teclado. A
continuación, y sobre la lista, buscar los valores m áxim o y m ínim o, y escribirlos.
in t nElementos; II nú m e r o de e l e m e n t o s ( v a l o r no n e g a t i v o )
do
í
S y s t e m . o u t . p r i n t ( " N ú m e r o de v a l o r e s que d es ea introducir: ” ):
n E l e m e n t o s = L e e r . d a t o l n t í );
I
w hile (nElementos < 1):
• A continuación leem os los valores que form an la lista. La entrada de datos fi
nalizará cuando se tecleen todos los valores, o bien cuando se teclee un valor
no num érico; por ejem plo, pulsar sim plem ente la tecla Entrar. Hagam os un
breve repaso de la clase L eer que im plem entam os en el capítulo 5. Los méto-
206 JA V A"'CURSO D E PROGRAM A CIÓN
dos de esta clase devuelven el núm ero entero o decim al introducido a través
del teclado. A hora bien, cuando el v alo r tecleado no se corresponde con un
núm ero, los m étodos im plem entados para leer un entero devuelven el valor
M I N _ V A L U E (valor m ínim o) y los im plem entados para leer un decim al, de
vuelven el v alo r N a N (no es un núm ero).
El código anterior establece un bucle p ara leer datos hasta com pletar la m a
triz. Si p o r cualquier circunstancia se decide term inar la entrada d e datos antes
que se com plete la m atriz, pulsando, p o r ejem plo, la tecla E ntrar, el m étodo
datoF loat devolverá el valor N a N . Para detectar si esto ha ocurrido debem os
utilizar el m étodo is N a N de la clase Flo at. Se trata d e un m étodo static que
devuelve tru e si su argum ento se corresponde con un dato no num érico, y
false en otro caso.
¡vi f'j ion fionohoT im «aioli.v ou alan i.«•
max = mi n = d a t o [ 0 ] :
f o r ( i = 0; i < nE lem entos: i++)
1
if ( d a t o [ i ] > max)
max = d a t o [ i ] :
i f < d a t o [ i ] < min)
mi n = d a t o [ i ] ;
)
S y s t e m . o u t . p r i n t l n t " \ n V a l o r má xi mo : " + ma x ) ;
Syste,m,,out. p r i n t l n t " V a l o r m í n i m o : ” + m i n ) ;
// L e e r . c l a s s d e b e e s t a r en l a carpeta especificada p o r CL AS S PA TH
p u b lic c l a s s CValoresMaxMin
I •
// O b t e n e r e l má xi mo y e l m í n i m o de un c o n j u n t o de v a l o r e s
pu b lic s t a t ic void m a in ( S t r in g [ ] args)
I
int nElementos: // nú me r o de e l e m e n t o s (valor no n e g a t i v o )
do
[
S y s t e m . o u t . p r i n t t " N ú m e r o de v a l o r e s que d e s e a introducir: ");
n E l e m e n t o s - L e e r . d a t o l n t t ):
1
while (nElem entos < 1):
f l o a t [ ] d a t o = new f l o a t [ n E l e m e n t o s ] ; // c r e a r l a m a t r i z d a t o
i n t i = 0: // s u b í n d i c e
f l o a t max. m i n ; // v a l o r má xi mo y v a l o r m í n i m o
// O b t e n e r l o s v a l o r e s má xi mo y m í n i m o
i f (nE lem en to s > 0)
I
max = mi n = d a t o [ 0 ] ;
f o r ( i = 0: i < n E l e m e n t o s : i++)
I
if ( d a t o [ i ] > max)
max - da t OC i
i f ( d a t o [ i ] < min)
min - d a t o í i ] ; : :.n ¡
. 1 . , -¡ “ o'-'.’, ; , , ’:' ;.! ,1):) ’ W-í-.f,,
// E s c r i b i r l o s r e s u l t a d o s
S y s t e m . o u t . p r i :r t t l n ( " \ n V a l o r má xi mo : " + ma x ) :
S y s t e m . o u t . p r i n t l n ( " V a l o r minimo: " + min ):
1
el se
S y s t e m . o u t . p r i n t l n í ” \ n N o h a y d a t o s . ” ):
208 JAVA: C U R SO D E PROGRAM A CIÓN
2. Escribir un program a que dé com o resultado la frecuencia con la que aparece cada
una de las parejas de letras adyacentes de un texto introducido por el teclado. No
se hará diferencia entre m ayúsculas y m inúsculas. El resultado se presentará en
form a de tabla, de la m anera siguiente:
a b c d e f ... z
a 0 4 0 2 1 0 ... 1
b 8 0 0 0 3 1 ... 0
c
d
e
f
Por ejem plo, la tabla anterior dice que la pareja de letras a b ha aparecido 4
veces. La tabla resultante contem pla todas las parejas posibles de letras, desde la
a a hasta la zz.
L as parejas de letras adyacentes de “hola que tal" son: ho, ol, la, a blanco no
se contabiliza por estar el carácter espacio en blanco fuera del rango ‘a ’ - ‘z ’,
blanco q no se contabiliza por la m ism a razón, qu, etc.
if ( ( c a r a n t > = ' a ' && c a r a n t < = ' z ' ) && (c a r> = ‘a ' && c a r < = ' z ' ) )
t a b l a t c a r a n t - ’a ' ] [ c a r - ' a ' ] + + :
import j a v a . i o . * :
II L e e r . c l a s s d e b e e s t a r en l a carpeta especificada p o r C LA S S P A T H
p u b lic c l a s s CFrecuencia
I
// T a b l a de f r e c u e n c i a s de l e t r a s a d y a c e n t e s en un t e x t o ,
pu b lic s t a t ic void m a in (S trin g [] args)
I
// C r e a r l a m a t r i z t a b l a c o n ' z ’ - ' a ' + l p o r ’ z ' - ' a ' + l elementos.
// J a v a i n i c i a l o s e l e m e n t o s de l a m a t r i z a c e r o .
i n t [ ] [ ] t a b l a = new i n t [ ’ z ’ - ’ a ' + 1 ] [ ’ z ’ - ’ a ' + 1 ] :
c h a r f . c; // s u b í n d i c e s
char car; // c a r á c t e r a c t u a l
char carant = ’ // c a r á c t e r a n t e r i o r
f in a l char eof = ( c h a r ) - l:
// E n t r a d a d e d a t o s y c á l c u l o de l a t a b l a de f r e c u e n c i a s
S y s t e m . o u t . p r i n t l n ( " I n t r o d u c i r un t e x t o . " ) :
System .out.printlnt"Para f in a liz a r pulsar [ C t r l ] [ z ] \ n " ) :
try
1
// L e e r e l s i g u i e n t e c a r á c t e r d e l t e x t o
w h ile ( ( c a r = ( c h a r ) S y s t e m . i n .r e a d ( )) ! - eof)
I
// C o n v e r t i r el c a r á c t e r a m i n ú s c u l a s s i procede
if ( c a r > = ' A ' && c a r < = ’ Z ’ ) c a r + = ( ' a ' 'A ') :
// S i el c a r á c t e r l e í d o e s t á e n t r e la ’ a ‘ y la ’z '
// in cre m e n t a r el c o n t a d o r c o r r e s p o n d i e n t e
if ( ( c a r a n t > = ' a ’ && c a r a n t < = ' z ' ) && ( c a r > = ’ a ’ && c a r < = ’ z ’ ) )
ta bla[caran t - ’a ’ ][c a r - ’a ']+ + :
carant = car;
catch U O E x c e p t i o n ig n o ra d a ) II
// M o s t r a r l a t a b l a d e f r e c u e n c i a s
System .out.p rintln("\n"):
II V i s u a l i z a r una c a b e c e r a " a b e . . . ”
System .out.printí ” "):
f o r (c = ' a ’ : c <= ’ z ' : c++)
System .out.p r i n U " " + c);
S y s t e m . o u t . p r i n t 1 n ( );
// V i s u a l i z a r l a t a b l a de f r e c u e n c i a s
f o r ( f - ’a ’ : f <= ’z ’ : f + + )
I
S y s t e m . o u t . p r i n t ( f ):
f o r ( c = ’a ' ; c < = ’ z ' : c + + )
S y s t e m . o u t . p r i n t ( " " + t a b 1a [ f - ’a ' ] [ c - 'a '] ) :
S y s t e m . o u t . p r i n t l n ( );
210 JA VA : C U R SO D E PROGRAM A CIÓN
a b c d e f ... z
a 0 4 0 2 1 0 ... 1
b 8 0 0 0 3 1 ... 0
c
d
e
f
EJERCICIOS PROPUESTOS
1. Se desea realizar un histogram a con los pesos de los alum nos de un determ inado
curso.
Peso Número de a l u m n o s
21 **
22 ****
23 * * * * * * * * * * * * * * *
24 * * * * * *
El núm ero de asteriscos se corresponde con el núm ero de alum nos del peso espe
cificado.
3. Realizar un program a que lea una cadena de caracteres y la alm acene en una m a
triz. A continuación, utilizando un m étodo, deberá convertir los caracteres escritos
en m ayúsculas a m inúsculas. Finalm ente im prim irá el resultado.
4. La m ediana de una lista de n núm eros se define com o el valor que es m enor o
igual que los valores correspondientes a la m itad de los núm eros, y m ayor o igual
que los valores correspondientes a la o tra m itad. P or ejem plo, la m ediana de:
16 12 99 95 18 87 10
es 18, porque este valor es m enor q ue 99, 95 y 87 (m itad de los núm eros) y m ayor
que 16, 12 y 10 (otra m itad).
R ealizar un program a que lea un núm ero im par de valores y dé com o resultado la
m ediana. L a entrada de valores finalizará cuando se detecte la m arca de fin de fi
chero.
5. E scribir un program a que utilice un m étodo para leer una línea de la entrada y dé
com o resultado la línea leída y su longitud o núm ero de caracteres.
import j a v a . i o . * :
public c la ss Test
1 mi „ - i; n-> ,
public static void V isualizar(byte car)
I
int i = 0. bitr
f o r ( i = 7 : i >= 0 : i - • )
I
b i t = ( ( c a r & (1 << i ) ) ! = 0) ? 1 : 0:
System .out.p rin t(bit):
I !•! i v.-
S y s t e m . o u t . p r i n t l n ( ):
8. Para alm acenar una m atriz bidim ensional que generalm ente tiene m uchos ele
m entos nulos (m atriz sparse) se puede utilizar una m atriz unidim ensional en la
que sólo se guardarán los elem entos no nulos precedidos por sus índices, fila y
colum na, lo que redunda en un aprovecham iento de espacio. P or ejem plo, la m a
triz:
6 0 0 0 4
0 5 0 0 2
2 0 0 0 0
0 0 7 0 0
fila
0 0 0 8 0
■columna
|0 10 1 6 1 0 4 4 1 1 5 1 4 2 2 0 2 3 2 | 7 |4 3 8
Se pide:
a) Escribir un m étodo que lea una m atriz bidim ensional por filas y la alm acene
en una m atriz m unidim ensional. El prototipo de este m étodo será:
Los parám etros f i y co se corresponden con el núm ero de filas y de colum nas
de la supuesta m atriz bidim ensional.
b) Escribir un m étodo que perm ita representar en pantalla la m atriz bidim ensio
nal por filas y colum nas. El prototipo de este m étodo será:
Los parám etros / y c se corresponden con la fila y la colum na del elem ento
q ue se visualiza. El valor del elem ento que se visualiza se obtiene, lógica
m ente de la m atriz unidim ensional creada en el apartado a, así: buscam os pol
los índices / y c; si se encuentran, el m étodo Visualizar devuelve el valor al
m acenado justam ente a continuación; si no se encuentran, entonces devuelve
un cero.
E scrib ir un program a que, utilizando el m étodo C rearM atrizU ni, cree una
m atriz unidim ensional a partir de una supuesta m atriz sparse bidim ensional y
a continuación, utilizando el m étodo Visualizar, m uestre en pantalla la matriz
bidim ensional.
CA PÍTU LO 8
& F.J. Ceballos/RA-MA
METODOS
En los capítulos anteriores aprendim os lo que es un program a, cóm o escribirlo y
qué hacer para que el ordenador lo ejecute y m uestre los resultados perseguidos;
adquirim os conocim ientos generales acerca de la program ación orientada a obje
tos; aprendim os acerca de los elem entos que aporta Java; analizam os cóm o era la
estructura de una program a Java; aprendim os a leer datos desde el teclado y a vi
sualizar resultados sobre el m onitor; estudiam os las estructuras de control; y
aprendim os a trabajar con m atrices.
En este capítulo, utilizando los conocim ientos adquiridos hasta ahora, vamos
a centram os en cuestiones m ás específicas com o pasar argum entos a m étodos, es
cribir m étodos que devuelvan m atrices, copiar m atrices, pasar argum entos en la
línea de órdenes, im prim ir resultados con form ato, clasificar los elem entos de una
m atriz, o bien buscar un elem ento en una m atriz, entre otras cosas.
rior. A lgunos de los m étodos de la biblioteca Java tam bién tienen parám etros que
son m atrices de caracteres, p o r ejem plo re a d . P ero no hem os estudiado nada
análogo en el caso de m atrices num éricas, lo cual es lógico porque m ientras una
cadena de caracteres, por ejem plo “nom bre” , es un objeto que m anejam os habi
tualm ente com o tal, no sucede lo m ism o con una m atriz num érica, donde cual
quier operación p asa p o r el acceso individual a sus elem entos.
Para aclarar lo expuesto, el siguiente ejem plo im plem enta un m étodo con un
parám etro de tipo d o u b le[][], que perm ite m ultiplicar p o r 2 los elem entos de una
m atriz num érica de dos dim ensiones pasada com o argum ento.
Muí t i p l i c a r P o r D o s M a t r i z 2 D ( m ) :
II V i s u a l i z a r l a m a t r i z p o r f i l a s
f o r ( i n t f = 0: f < m . l e n g t h : f + + )
1
f o r ( i n t c = 0; c < m [ 0 ] . l e n g t h ; c++)
System .out.print(m [f][c] + " "):
S y s t e m . o u t . p r i n t l n í ):
main a c c e d e MultiplicarPorDosMatriz2D
a la m a triz a a c c e d e a la m atriz
tra v é s d e m a tra v é s d e x
m lx0 I m Ix, |
— *•! 10 | 20 | 30 |
— ► 40 | 50 60 fila 1
La aplicación siguiente im plem enta un m étodo que tiene un parám etro de tipo
d o u b le[][] y perm ite copiar una m atriz num érica bidim ensional pasada com o ar
gum ento, en otra m atriz. El m étodo devuelve com o resultado la copia realizada.
f o r ( i n t f = 0; f < x . l e n g t h ; f + + )
f o r ( i n t c = 0: c < x [ 0 ] . l e n g t h : c++)
z [ f ] [ c ] = x [ f ] [ c ]:
r e t u r n z;
I
// C o p i a r una m a t r i z u t i l i z a n d o un mét od o
d o u b l e [ ] [ ] m2 = C o p i a r M a t r i z 2 D ( m l );
m l [ 0 ] [ 0 ] = 77; // m o d i f i c a r un e l e m e n t o de l a m a t r i z original
// V i s u a l i z a r l a m a t r i z m2
f o r ( i n t f = 0: f < m 2 .le n g t h : f++)
21 8 JA V A: C U R SO DE PROGRAM A CIÓN
f o r ( i n t c = 0: c < m 2 [ 0 ] . 1 e n g t h : c++)
System .out.print(m 2[f][c] + " ") :
S y s t e m . o u t . p r i n t l n t );
O tra form a de realizar una copia de una m atriz es utilizando el m étodo clone
expuesto anteriorm ente. Q uizás esta form a de proceder resulte m ás difícil de
com prender cuando se m anipulan m atrices m ultidim ensionales. Por eso es reco
m endable volver a analizar detenidam ente la figura expuesta en el apartado
“M atrices num éricas m ultidim ensionales” del capítulo anterior. Si llega a la con
clusión de que una m atriz de dim ensiones f x c es una m atriz de una dim ensión de/
elem entos q ue son referencias a otras tantas m atrices de una dim ensión de c ele
m entos de un tipo especificado, le será fácil entender el código m ostrado a conti
nuación, el cual copia una m atriz de dos dim ensiones referenciada p o r m i en otra
m atriz referenciada por m2:
O tra form a más de realizar una copia de una m atriz es utilizando el método
a rra y c o p y de la clase System . Se trata de un m étodo público y estático cuya sin
taxis es la siguiente:
P o r ejem plo, el código m ostrado a continuación, copia una m atriz de dos di
m ensiones referenciada por n tl en otra m atriz referenciada p o r m2:
i n t [ ] [ ] mi = 1 1 1 0 . 2 0 . 3 0 1 . 14 0 . 5 0 . 6 0 1 1 ;
i n t [ ] [ ] m2 = new i n t [ m i . 1 e n g t h ] [ m i [ 0 ] . 1 e n g t h ] ;
S y s t e m . a r r a y c o p y ( m l . 0 . m2. 0 . m i . 1 e n g t h ) :
i nt a rg - 1234 ; ..
jm bhhhnhhhm hm m hhnbm m hhi
S y s t e m . o u t . p r i n t l n í a r g );
1
L a línea som breada del ejem plo anterior invoca al m étodo In c rem en ta rlo y
copia el valor del argum ento arg en el parám etro param del m étodo. Esto signifi
ca q ue el argum ento ha sido pasado p o r valor. P or lo tanto, cualquier m odifica
ción que haga el m étodo sobre param no afectará a la variable original. Según lo
expuesto el m étodo m a in m ostrará el resultado 1234, valor original de arg.
¿Q ué hay que hacer para que un m étodo pueda m odificar el valor original del
argum ento que se le pasa? Pasar dicho argum ento por referencia.
Tam bién hem os estudiado que un valor de un tipo prim itivo puede ser encap-
sulado en un objeto. Por ejem plo un valor de tipo in t puede ser encapsulado en un
objeto de la clase In te g e r. Entonces, si los objetos son pasados p o r referencia
¿puede un m étodo m odificar el valor original del argum ento que se le pasa cuan
do éste es un objeto? A nalicem os el siguiente ejem plo:
Com o las clases que encapsulan los tipos prim itivos no tienen m étodos análo
gos al descrito, esta form a de pasar un valor de un tipo prim itivo p o r referencia
con la intención de que sea m odificado, no sirve. ¿C óm o d ar solución al problem a
planteado? Según se ha expuesto anteriorm ente, una m atriz es un objeto, lo cual
C A P ÍT U L O 8: M ÉTO D O S 2 2 1
significa que cuando se pase com o argum ento a un m étodo, será pasado por refe
rencia. Por lo tanto, los cam bios que haga este m étodo sobre los elem entos de esa
m atriz afectarán a la original. V eam os el siguiente ejem plo:
C ada elem ento de la m atriz args referencia a un argum ento, de m anera que
args[0] contiene el p rim er argum ento de la línea de órdenes, a r g s [ l] el segundo,
etc. Por ejem plo, supongam os que tenem os una aplicación Java denom inada Test
que acepta los argum entos -n y -/. Entonces, podríam os invocar a esta aplicación
escribiendo en la línea de órdenes d el sistem a operativo la siguiente orden:
java Test -n -1
E sto hace que autom áticam ente la m atriz args d e objetos S t r in g se cree para
contener dos objetos S tr in g : uno con el prim er argum ento y otro con el segundo.
Puede im aginarla de cualquiera de las dos form as siguientes:
args
argsp - n - n
arg s i - 1 - 1
Para clarificar lo expuesto vam os a realizar una aplicación que sim plem ente
visualice los valores de los argum entos que se la han pasado en la línea de ó rd e
nes. E sto nos dará una idea de cóm o acceder desde un program a a esos argum en
tos. Supongam os que la aplicación se denom ina T est y que sólo adm ite los
argum entos -rt, -k y -/. Esto quiere decir que p o d rem o s especificar de cero a tres
argum entos. Los argum entos repetidos y no válidos se desecharán. P or ejem plo, la
siguiente línea invoca a lá aplicación T est pasándole los argum entos -n y -/:
java Test -n -1
// ¿ Q u é a r g u m e n t o s s e h a n p a s a d o ?
f o r ( i n t i = 0; i < a r g s . l e n g t h : i + + j
Argumentos:
-1
-n
224 JA V A: C U R S O DE PROGRAM A CIÓN
MÉTODOS RECURSIVOS
Se dice que un m étodo es recursivo, si se llam a a sí m ism o. El com pilador Java
perm ite cualquier núm ero de llam adas recursivas a un m étodo. C ada vez que el
m étodo es llam ado, sus parám etros y sus variables locales son iniciadas.
do
1
System .out.print("¿Núm ero? “ ):
nu mero = L e e r . d a t o l n t ( ):
)
while ( n u me r o < 0 || nu mero > 2 5 ) :
0 factorial(4) 24
4 * factorial(3) 4*6
2 3 * factoriai(2) 3*2
3 2 * factorial( 1) 2 * 1
4 1 * factorial(O) 1* 1
factorial(O) 1
C ada llam ada al m étodo factorial aum enta en una unidad el nivel de recur
sión. C uando se llega a n = 0. se obtiene com o resultado el valor / y se inicia la
vuelta hacia el punto de partida, reduciendo el nivel de recursión en una unidad
cada vez.
• con un núm ero determ inado de dígitos enteros com pletando con ceros por la
izquierda si fuera necesario,
• o bien una serie de cantidades decim ales, una debajo de otra, ajustadas por la
com a.
Para controlar los distintos form atos. Java proporciona un conjunto de clases
en el paquete java.text. La figura siguiente m uestra estas clases. L os rectángulos
som breados son clases abstractas.
La clase F o r m a t es una clase base abstracta para dar form ato a núm eros, fe
chas/horas y m ensajes. D e esta clase se derivan tres subclases especializadas en
cada una de las tareas m encionadas: N u m b e rF o rm a t, D a te F o r m a t y M c ssa g e -
F o rm a t.
Object
J
17 Formal
j
17 NumberFormat DateForm at M essageForm at
J
r
J T
DecimalFormat Sim pleDateFormat
ChoiceForm at
Para dar form ato a un núm ero, prim ero hay que crear un objeto form ateador basa
do en un form ato específico, y luego utilizar su m étodo fo rm a t para convertir el
núm ero en una cadena construida a partir del form ato elegido. L os sím bolos que
se pueden utilizar para especificar un determ inado form ato son:
El siguiente ejem plo crea un objeto fo rm a to que perm itirá obtener núm eros
form ateados con dos decim ales, si los hay, y con el punto de los miles.
D e c i m a l F o r m a t f o r m a t o = new D e c i m a l F o r m a t :
Strin g sa lid a = form ato.form at(dato);
S e puede utilizar tam bién un objeto form ateador basado en la localidad actual.
Por ejem plo, las siguientes líneas de código darán lugar a núm eros form ateados
así: 1 2 3 . 4 5 6 . 0 0 P t s .
Nu mb e r F o rm a t f o r m a t o = N u m b e r F o r m a t . g e t C u r r e n c y I n s t a h c e í ):
S t r in g s a lid a = form ato.form 'at(dato):
donde ( “e n ”, “U S ") significa inglés de E stados U nidos. O tros ejem plos de loca
lidades son: ( “e n ”, " G B ") que significa inglés del R eino Unido; ( “e s" , " E S ”)
que significa español de España; ( “f r ”, “F R ") que significa francés de Francia;
(" d e " , “D E ”) que significa alem án de A lem ania; etc. A continuación se explica
la clase Lócale.
O tros m étodos de interés son ge tN u m b e rln sta n c e que devuelve el form ato
num érico predeterm inado que se em plea en la localidad que se especifique, o bien
en la actual si no se especifica ninguna localidad; y ge tP e rcen tln stan ce que de
vuelve el form ato para especificar un v alor en tanto p o r ciento.
Localidad
Alineación
L os valores num éricos que escribim os, con o sin form ato, quedan alineados a u
tom áticam ente a la izquierda. Para alinear a la derecha una serie de valores num é
ricos form ateados, adem ás del objeto form ateador, hay que crear un objeto de la
clase F ie ld P o sitio n basado en la posición utilizada para realizar la alineación.
Ésta puede ser: IN T E G E R _F IE L D , alineación por el últim o dígito entero (por la
com a decim al), o bien E RAC TIÜ N _FIELD , alineación por el últim o dígito d eci
mal (1N TEG ER_F!ELD y F R A C T IO N _F IE L D son dos constantes pertenecientes
a la clase N u m b e r F o im a t). Por ejem plo:
S t r i n g p a t r ó n - new S t r i n g ( ”# # # . # # # . # # 0 . 0 0 " ) :
Deci mal For mat f o r ma t o = new De c i ma l F o r ma t ( p a t r ó n ) ;
F i e l d P o s i t i o n f p - new F i e l d P o s i t i o n ( Nu mb e r F o r ma t . FRACTI0N_FIELD);
S t r i n g B u f f e r s a l i d a = new S t r i n g B u f f e r ( ):
f o r m a t o . f o r m a t t d a t o . s a l i d a , f p );
f o r ( i n t i = 0; i < ( p a t r ó n . 1 e n g t h ( ) - f p . g e t E n d I n d e x ( ) ) ; i++)
sal i d a . i n s e r t ( 0 . ' ') ;
Una vez estudiado cóm o d ar form ato a un núm ero, el objetivo siguiente es es
cribir una clase O btener que proporcione varios m étodos que perm itan obtener
cualquier núm ero en alguno de los form atos que considerem os m ás com unes.
Posteriorm ente, podrem os utilizar esta clase com o soporte en otras aplicaciones.
Em pleando los conocim ientos expuestos hasta ahora podem os, com o ejercicio,
im plem entar una clase que incluya algunos m étodos que después podam os utilizar
para form atear núm eros. Estos m étodos los definirem os static para que puedan
ser invocados sin necesidad de crear un objeto de la clase. La clase la denom ina
rem os O btener y sus m étodos será los siguientes:
• F orm atoLocal. D evuelve la cadena resultante de form atear un núm ero utili
zando el form ato m onetario predefinido en el país actual.
• F orm atoPer. D evuelve la cadena resultante de form atear un núm ero con un
form ato personalizado. Los sím bolos de puntuación utilizados son los corres
pondientes al país actual.
• A linD er. R ealiza la m ism a operación que F orm atoP er y adem ás, alinea los
núm eros a la derecha del patrón utilizado.
• Form atoPaís. R ealiza la m ism a operación que Form atoP er pero utilizando
los sím bolos de puntuación del país especificado.
import j a v a . ú t i l
import j a v a . t e x t . * :
p u b l i c c l a s s Obtener
{
static public String Form atoLocal(double dato)
I
N u mb e r F o r m a t f o r m a t o = N u m b e r F o r m a t . g e t C u r r e n c y l n s t a n c e t ):
Strin g salida = form ato.form attdato):
return sa lid a:
1
import j a v a . i o . * ;
import j a v a .u t i 1 .*:
p u b l i c c l a s s CDemoFormatoNum „¡n/m
I . ,, , : •. 'i ü ' A i r m i M «i. ¡í ■i :
static public void m a in (S trin g [] args)
(
PrintStream f lu j o S = System.out;
f l u j o S . p r i n t l n í O b t e n e r . F o r m a t o L o e a l (1234 5 6 ) ) ;
f l u j o S . p r i n t l n( O b t e n e r . Form a t o L o c a l ( 1 2 . 3 4 5 6 . 7 8 9 ) ) :
f 1u j o S . p r i n t í n ( O b t e n e r . F o rm a to L o c a l( 1 2 3 .4 5 ) ) ;
f l u j o S . p r i n t l n í );
fl ujoS.p rintln(O bten er.fo rm atoP er( " # # . # # . # " . 123456)):
f 1u joS. p ri n t l n ( O b t e n e r . Form atoPerí " # # # # " . 123456));
f 1 u j o S . p r i n t l n ( O b t e n e r . F o r m a t o P e r ( “# # # . # # " . 1 2 3 4 5 6 . 7 8 9 ) ) ;
f 1u j o S . p r i n t í n ( O b t e n e r . F o rm a to P e r(" 0 0 0 0 0 0 . 0 0 0 " . 1 2 3 .4 5 ) ) :
f l u j o S . p r i n t l n( O b t e n e r . Form a t o P e r ( " I # # . # # . # # : " . 1 2 3 4 5 . 67 ) ) ;
f l u j o S . p r i n t l n ( 0 b t e n e r . FormatoPer( "###.###.'###". 1 2 .3 4 ));
flu joS.p rintlní);
S t r i n g p a t r ó n = new S t r i n g ( " # # # . # # # , # 0 . 0 0 " ) :
f l u j o S . p r i n t l n ( 0 b t e n e r .Al i n D e r ( p a t r ó n . 1 . 2 3 4 ) ) ;
flu jo S . p r i ntln(Obtener.Al i nD er(patrón. 12.345)):
fl u jo S.p rin tln (0 b te n e r.A l in D e r(p a tró n . -123.456));
CA PÍTU LO 8: M ÉTODOS 2 3 1
f 1u j o S . p r i n t l n ( O b t e n e r . A l inDerípatrón. 123.456));
flujoS.println(O btener.Al inDerípatrón. 1234.567));
f 1u j o S . p r i n t l n ( O b t e n e r .Al i nDer(patrón. 12345.678));
f 1u j o S . p r i n t l n ( O b t e n e r . A l i nDerí p a t r ó n , -12345)):
f l u j o S . p r i n t l n í );
Locale[] país =
I
new L o c a l e í " e s " . " E S ” ).
new L o c a l e í " e n " . " U S " ) .
I:
f o r ( i n t i = 0: i < p a í s . l e n g t h ; i + + )
f l u j o S . p r i n t l n i Obtener.FormatoPaí s ("# # # ,# # # .# # # ", 1 2 3 4 5 6 . 789,
paí s [ i ] ) ) ;
En las presentaciones num éricas un sím bolo da lugar al m ínim o núm ero de
dígitos. P o r ejem plo si yy yy d a lugar a 2002, y , yy, o y y y dan lugar a 02.
C ualquier otro carácter fuera de los rangos [‘A ’..‘Z ’] y [‘a \ . ‘z ’] será tratado
com o un separador.
El siguiente ejem plo alm acena en salida la fecha y la hora actuales según el
form ato especificado p o r patrón.
D a t e hoy - new D a t e ( ) ;
S t r i n g pa tró n = "EEEE dd-MMM-yyyy, HH:mm:ss” :
S i m p l e D a t e F o r m a t f o r m a t o - new S i m p l e D a t e F o r m a t ( p a t r ó n ):
S t r i n g s a l i d a = f o r m a t o . f o r m a t í h o y ):
Se puede utilizar tam bién un objeto form ateador basado en la localidad actual.
En este caso dicho objeto será creado y devuelto por alguno de los m étodos de la
clase D a te F o rm a t. Por ejem plo, las siguientes líneas de código alm acenarán en
sF echa y sH ora la fecha y la hora según el form ato local predeterm inado. Por
ejem plo: 1 6 - a g o - 0 2 y 2 1 : 0 6 : 34.
D a t e h o y = new D a t e ( ):
S t r i n g sFecha. sHora;
DateFormat formato:
f o r m a t o = D a t e F o r m a t . g e t D a t e l n s t a n c e t ):
sFecha = f o r m a t o . f o r m a t ( h o y ) :
f o r m a t o - D a t e F o r m a t . g e t T i m e l n s t a n c e í );
sHora = f o r m a t o . f o r m a t ( h o y ) :
El m étodo ge tD ate ln sta n ce sin argum entos devuelve el form ato utilizado pa
ra m ostrar la fecha en la localidad actual, y el m étodo ge tT im e ln sta n c e sin argu
m entos devuelve el form ato utilizado en la localidad actual para m ostrar la hora.
A m bos m étodos pueden ser invocados con un argum ento que especifique el estilo
con el que será form ateada la fecha o la hora; por ejem plo:
DateForm at.getDa te I n s t a n c e ( D a t e F o r m a t . M E D I U M ) ;
Da t e F o r m a t . g e t D a t e l n s t a n c e ( D a t e F o r m a t . D E F A U L T , p a 1 s );
LA CLASE Arrays
La clase A r r a y s del paquete ja v a .u til contiene varios m étodos static para m ani
pular m atrices. Estos m étodos son: b in a ry S e a rc h . equals. fill y sort.
binarySearch
Este m étodo perm ite buscar un valor en una m atriz que esté ordenada ascenden
tem ente utilizando el algoritm o de búsqueda binaria. Este algoritm o será explica
do m ás adelante en otro capítulo. A hora basta con saber que se trata de un
algoritm o m uy eficiente en cuanto a que el tiem po requerido para realizar una
búsqueda es m uy pequeño. L a sintaxis expresada de form a genérica para utilizar
este m étodo es la siguiente:
donde m representa la m atriz, clave es el valor que se desea buscar del m ism o tipo
que los elem entos de la m atriz, y tipo es cualquier tipo de datos de los siguientes:
byte, char, short, int, long, float, d o u b le y Object.
índice del elem ento de la m atriz donde debería encontrarse el v alo r buscado. La
expresión “-{ punto de inserción) - 1" garantiza que el índice devuelto será m ayor
o igual que cero sólo si el valor buscado es encontrado.
double[] a = 110,15.20,25.30.35.40.45,50.551;
nt i ;
= A r r a y s . b i n a r y S e a r c h ( a , 2 5 ) ; // i = 3
= A r r a y s . b i n a r y S e a r c h í a , 2 7 ) ; // i = -5
= Arrays.binarySearchía. 5); // i - -1
= A r r a y s . b i n a r y S e a r c h í a . 6 0 ) : // i = -11
equals
Este m étodo perm ite verificar si dos m atrices son iguales. D os m atrices se consi
deran iguales cuando am bas tienen el m ism o núm ero de elem entos y en el m ism o
orden. Asim ism o, dos m atrices tam bién son consideradas iguales si sus referen
cias valen nuil. La sintaxis para utilizar este m étodo expresada de form a genérica
es la siguiente:
donde m i y m 2 son m atrices del m ism o tipo y tipo es cualquier tipo de datos de
los siguientes: boolean. byte. char, short, int. long, float. d o u b le y Object.
El valor devuelto será true si am bas m atrices son iguales y false en caso con
trario.
Com o ejem plo, puede probar los resultados que produce el siguiente código:
double[] a = 1 1 0 .1 5 . 2 0 . 2 5 . 3 0 . 3 5 . 4 0 . 4 5 . 5 0 .5 5 1 :
double[] b = 1 1 0 . 1 5 . 2 0 . 2 5 . 3 0 , 3 5 . 4 0 . 4 5 . 5 0 .5 5 ) :
i f ( A r r a y s . e q u a l s ( a . b))
S y s t e m . o u t . p r i n t l n ( " S o n i g u a l e s " );
el se
S y s t e m . o u t . p r i n t l n ( " N o son i g u a l e s " ) :
fíll
Este m étodo perm ite asignar un valor a todos los elem entos de una m atriz, o bien
a cada elem ento de un rango especificado. La sintaxis expresada de form a genéri
ca para utilizar este m étodo es la siguiente:
CA PITU LO 8: M ÉTODOS 2 3 5
double[] a = 110.15.20,25.30,35.40.45.50.55):
A r r a y s . f i l K a . 0 ) : // p o n e r l o s e l e m e n t o s d e l a m a t r i z a cero
sort
E ste m étodo perm ite ordenar los elem entos de una m atriz en orden ascendente
utilizando el algoritm o quicksort. E ste algoritm o será explicado m ás adelante en
otro capítulo. A hora basta con saber que se trata de un algoritm o m uy eficiente en
cuanto a que el tiem po requerido para realizar la ordenación es m ínim o. La sinta
xis expresada de form a genérica para utilizar este m étodo es la siguiente:
void s o r t ( t i p o í] m)
void s o r t (tip oi'i m. int desdelnd. int hastalnd)
C om o ejem plo, puede probar los resultados que produce el siguiente código:
LA CLASE Object
L a clase O b je c t es la clase raíz de la jerarquía de clases de la biblioteca Java;
pertenece al paquete ja v a.lan g. A sim ism o, cualquier clase que im plem entem os en
nuestras aplicaciones pasará a ser autom áticam ente una subclase de esta clase.
Esto se traduce en que todos los m étodos de O b je c t son heredados por las clases
de la biblioteca Java y p o r cualquier o tra clase que incluyam os en un program a.
T res de estos m étodos (w ait, n otify y n o tify A II) soportan el control de hilos, por
lo tanto posponem os su estudio a un capítulo posterior; otros m étodos, com o
236 JA V A: C U R SO DE PROGRAM A CIÓN
En cam bio, la expresión strl.eq u a ls(str2 ) com para el contenido de los obje
tos; en este caso verifica si am bas cadenas contienen los m ism os caracteres. Esto
es así, porque el m étodo e q u a ls de O b je c t ha sido sobreescrito en la clase S t r in g
para que haga esta tarea m ás específica. T odas las subclases de O b je c t deberían
sobreescribir el m étodo e q u a ls para que realicen una com paración que sea útil.
String toStringO
El m étodo to S tr in g de la clase O b je c t retom a: un S t r in g que alm acena el nom bre
de la clase del objeto que recibe el m ensaje to S trin g , el sím bolo \ y la repre
sentación hexadecim al del código hash del objeto. Esto es, la cadena de caracteres
sería equivalente a la proporcionada p o r la siguiente expresión:
o b j . g e t C l a s s ( ) . g e t N a m e ( ) + ' @ ’+ I n t e g e r . t o H e x S t r i n g í o b j . h a s h C o d e í ) )
C uando se ejecute el ejem plo anterior la línea som breada dará lugar al si
guiente resultado:
Test@73bf4fel
void finalize()
E ste m étodo es invocado por el recolector de basura cuando Java determ ina que
no hay m ás referencias a un objeto.
Este ejem plo crea un objeto S t r in g con el contenido “abe” . D icho proceso
puede realizarse tam bién así:
No obstante, es im portante saber cuál es el com portam iento de Java ante las
dos form as expuestas de crear un objeto S trin g .
S t r i ng s t r l = " a b e " :
String str2 = "a b e ";
if (s trl.e q u a ls (str2 ))
I
// el resu ltad o es true siempre
1
if (strl = str2)
I
// e l r e s u l t a d o es true siempre
1
En este ejem plo, el resultado de strl.eq u a ls(str2 ) es siem pre true, lo cual es
lógico porque independientem ente de que se trate o no de objetos diferentes, los
contenidos son los m ism os (verem os que se trata de un único objeto).
C A PÍTU LO 8: M ÉTODOS 2 3 9
C uando se com pila la prim era línea, Java añade el objeto S t r in g “abe” al área
de m em oria destinada a tales objetos. C uando se com pila la segunda línea no se
añade un nuevo objeto por que ya existe uno con el m ism o literal.
D urante la ejecución, la prim era línea alm acena en s t r l una referencia al ob
jeto que ya existe en el área de m em oria destinada por el com pilador a objetos
S tr in g ; y la segunda línea alm acena en str2 una referencia al m ism o objeto. La fi
gura siguiente m uestra esto gráficam ente:
M em oria
“abe"
Area de memoria
para objetos String
Sin em bargo, la utilización del operador new hace que se asigne m em oria pa
ra un nuevo objeto. P or ejem plo:
String s t r l = new S t r i n g ( “a b e ” ) : II c r e a r un n u e v o o b j e t o
S t r i n g s t r 2 = “a b e " :
if (strl .equals(str2))
I
// e l r e s u l t a d o es t r u e siempre
I
if (strl == s t r 2 )
I
// e l r e s u l t a d o es false siempre
I
C uando se com pila la prim era línea del ejem plo anterior, Java añade el objeto
S t r in g “ abe” alárea de m em oria destinada a tales objetos. C uando se com pila la
segunda línea no se añade un nuevo objeto porque ya existe uno con el m ism o li
teral.
son equivalentes a:
Finalm ente, es im portante recordar que cuando un m étodo actúa sobre un ob
je to S t r in g el resultado es un nuevo objeto lo que m antiene intacto el objeto ori
ginal. Por ejem plo:
EJERCICIOS RESUELTOS
de un algoritm o com o éste. El algoritm o m ás com únm ente utilizado para generar
núm eros aleatorios es el de congruencia lineal que se enuncia de la form a si
guiente:
donde se observa que cada núm ero en la secuencia rk, es calculado a partir de su
predecesor rk., (% es el operador m ódulo o resto de una división entera). La se
cuencia, así generada, es llam ada m ás correctam ente secuencia seudoaleatoria, ya
que cada núm ero generado, depende del anteriorm ente generado.
El m étodo rnd anterior tiene un parám etro de tipo int[] que perm itirá pasar un
argum ento de tipo int por referencia. De esta form a, el m étodo podrá m odificar el
argum ento pasado con el valor del últim o núm ero seudoaleatorio calculado, lo
que perm itirá calcular el siguiente núm ero seudoaleatorio en función del anterior.
Se puede observar que, en realidad, el núm ero seudoaleatorio calculado es un va
lor entre 0 y 65535 y que para convertirlo a un v alor entre 0 y 1 lo dividim os por
65535; el cociente de tipo d o u b le es el valor devuelto p o r el m étodo.
El siguiente program a m uestra com o utilizar el m étodo rnd para generar nú
m eros seudoaleatorios entre 0 y 1:
import j a v a . u t i 1 . * :
// G e n e r a r n ú m e r o s s e u d o a l e a t o r i o s
d o u b l e n;
f o r ( i n t i = 10: i ! = 0: i - - )
I
n - rnd(random);
System .out.println(n);
I
I
I
El m étodo m a in del ejem plo anterior prim ero calcula un valor entre O y
65535 a partir del cual se generará el p rim er núm ero seudoaleatorio; este valor,
que es el resto de dividir el núm ero de m ilisegundos transcurridos desde el 1 de
enero de 1970 devuelto p o r el m étodo g e tT im e de la clase D a te entre 65536, se
alm acena en el prim er elem ento de una m atriz denom inada random . D espués, pa
ra calcular cada núm ero seudoaleatorio, invoca al m étodo rnd pasando el argu
m ento random por referencia; de esta form a, el m étodo m d podrá m odificarlo con
el núm ero seudoaleatorio que calcule, lo que garantizará calcular cada núm ero
seudoaleatorio en función del anterior.
• D efinim os el rango de los núm eros que deseam os obtener, así com o una m a
triz para alm acenar los 6 núm eros aleatorios.
int U m i t e S u p = 4 9 , l í m i t e l n f = 1;
int n [ ] = new i n t [ 6 ] , i . k;
• U na vez obtenidos todos los núm eros, ordenam os la m atriz y la visualizam os.
Arrays.sort(n);
f o r ( i = 0; i < n . l e n g t h ; i + + )
Sy ste m .o u t.p rin t(n [i] + ” "):
import j a v a .u t i 1 .*;
// C l a s i f i c a r l a m a t r i z
Arrays.sort(n);
// M o s t r a r l a m a t r i z
f o r ( i = 0 : i < n . l e n g t h ; i+ + )
S y s t e m . o u t . p r i n t { n [ i ] + " ” );
S y s t e m . o u t . p r i n t l n < );
El p rim er parám etro y el segundo del m étodo F usionar son las dos m atrices
de partida, y el tercero es la m atriz que alm acenará los elem entos de las dos ante
riores.
b) C om parar los dos elem entos (uno de cada m atriz) y alm acenar en la m atriz re
sultado el m enor.
c) T o m ar el siguiente elem ento la m atriz a la que pertenecía el elem ento alm ace
nado en la m atriz resultado, y volver al punto b).
int ind = 0, i n d A = 0. i n d B = 0. i n d C = 0:
if (listaA.length + 1i s t a B . 1ength = 0)
r e t u r n 0;
// F u s i o n a r l a s l i s t a s A y B en l a C
w h i l e ( i n d A < 1 i s t a A . 1 e n g t h && i n d B < 1 i s t a B . 1 e n g t h )
i f ( 1 i s t a A [ i n d A ] . c o m p a r e T o d i s t a B C i n d B ] ) < 0)
1i s t a C [ i n d C + + ] = 1i s t a A [ i n d A + + ] ;
el se
1i s t a C [ i n d C + + ] = 1 i s t a B [ i n d B + + ] ;
// L o s d o s b u c l e s s i g u i e n t e s s o n p a r a p r e v e r e l c a s o de que.
// l ó g i c a m e n t e una l i s t a f i n a l i z a r á a n t e s q u e l a o t r a ,
f o r ( i n d = i n d A ; i n d < 1 i s t a A . 1e n g t h : i n d + + )
l i s t a C [ i n d C + + ] = 1i s t a A [ i n d ] :
return 1;
// D e c l a r a r l a m a t r i z q u e va a a l m a c e n a r e l r e s u l t a d o de
// f u s i o n a r l a s d o s a n t e r i o r e s
S t r i n g C ] l i s t a 3 - new S t r i n g [1 i s t a l . 1 e n g t h + 1 i s t a 2 . 1 e n g t h ] ;
// F u s i o n a r l i s t a l y 1 i s t a 2 y a l m a c e n a r e l r e s u l t a d o en l i s t a 3 .
// El m ét o d o " F u s i o n a r " d e v o l v e r á un 0 c u a n d o no s e pueda
// r e a l i z a r l a f u s i ó n ,
i n t i n d . r;
r = F u s i o n a r ! 1 i s t a l . 1 i s t a 2. 1 i s t a 3 > ;
// E s c r i b i r la m atriz resultante
i f ( r != 0)
I
246 JA V A: C U R SO D E PROGRAM A CIÓN
f o r ( i nd = 0: i n d < 1 i s t a 3 . 1 e n g t h : i n d + + )
S y s t e m . o u t . p r i n t l n ( 1 i s t a 3 [ i n d ] );
I
el se
S y s t e m . o u t . p r i n t l n < " E r r o r " );
t x x1
e = x + v + v + F +'-
P ara un valor de x dado, se calcularán y sum arán térm inos sucesivos de la serie,
hasta que el últim o térm ino sum ado sea m enor o igual que una constante de error
predeterm inada (por ejem plo le-7). O bserve que cada térm ino es igual al anterior
p o r x/n para n = 1, 2 , 3, ... El p rim er térm ino es el 1. Para ello se pide:
import j a v a . i o . * :
p u b lic c l a s s Test
I
static d o u b l e e x p o n e n c i a l ( d o u b l e x)
(
int n - 1:
d o u b l e e x p . t é r m i n o = 1;
exp = t é r m i n o : // p r i m e r t é r m i n o
w hile (término > le -7 )
I
término *= x/n; // s i g u i e n t e término
CA PÍTU LO 8: M ÉTODOS 2 4 7
exp += té rm in o : // s u m a r o t r o t é r m i n o
n++:
I
return ex p ;
I
EJERCICIOS PROPUESTOS
1. R ealizar un program a que se com porte com o un diccionario Inglés-Español; esto
es, solicitará una palabra en inglés y escribirá la correspondiente palabra en espa
ñol. El núm ero de parejas de palabras es variable, pero lim itado a un m áxim o de
100. La longitud m áxim a de cada palabra será de 4 0 caracteres. Por ejem plo, su
poner que introducim os las siguientes parejas de palabras;
book libro
green verde
m ouse ratón
2. Un cuadrado m ágico se com pone de núm eros enteros com prendidos entre 1 y n 2,
donde n es un núm ero im par que indica el orden de la m atriz cuadrada que con
tiene los núm eros que form an dicho cuadrado m ágico. La m atriz que form a este
cuadrado m ágico, cum ple que la sum a de los valores que com ponen cada fila, ca
da colum na y cada diagonal es la misma. Por ejem plo, un cuadrado m ágico de or
den 3. im plica un valor de n = 3 lo que dará lugar a una m atriz de 3 por 3. Por lo
tanto, los valores de la m atriz estarán com prendidos entre 1 y 9 y dispuestos de la
form a siguiente:
248 JA V A: C U R SO DE PROGRAM A CIÓN
8 1 6
3 5 7
4 9 2
b) L lam e a un método:
c) Escriba la cadena que sea m enor según los n prim eros caracteres (esto es, la
que esté antes por orden alfabético).
4. R ealizar un program a que lea un conjunto de valores reales a través del teclado,
los alm acene en una m atriz de m filas p o r n colum nas y a continuación, visualice
la m atriz por filas.
C A PÍTU LO 8: M ÉTODOS 2 4 9
La estructura de! program a estará form ada, adem ás de por el m étodo m ain , por
los m étodos siguientes:
El parám etro m del m étodo leerM atr¡z2D es la m atriz cuyos elem entos deseam os
leer.
El m étodo sum aC olsM atriz2D devolverá una m atriz unidim ensional con la suma
de las colum nas de la m atriz m de dos dim ensiones pasada com o argum ento.
[k) k\ (n —k)\
/?! = /? * ( / ? - ! ) * ( » - 2 )*...*2 * 1
El m étodo fa cto ria l recibe com o parám etro un entero y devuelve el factorial
del m ism o,
El m étodo com binaciones recibe com o parám etros dos enteros n y k, y de-
/ \
n
vuelve com o resultado el valor de
El m étodo potencia recibe com o parám etros dos enteros, base y exponente, y
devuelve com o resultado el valor de baseexpo"e",e.
Programación avanzada
• Clases y paquetes
• Subclases e interfaces
• Excepciones
• T rabajar con ficheros
• Estructuras dinám icas
• A lgoritm os
• Hilos
CA PÍTU LO 9
@ F J. Ceballos/RA-MA
CLASES Y PAQUETES
S eguro que a estas alturas el térm ino clase y a le es fam iliar. En los capítulos ex
puestos hasta ahora se han desarrollado aplicaciones sencillas, para introducirle
m ás bien en el lenguaje y en el m anejo de la biblioteca de clases de Java que en el
d iseño de clases. No obstante, sí ha tenido que quedar claro que un program a
orientado a objetos sólo se com pone de objetos y que un objeto es la concreción
de una clase. Sirva co m o ejem plo las aplicaciones que hem os desarrollado: todas
están basadas en una clase aplicación. Es hora pues de entrar con detalle en la
program ación orientada a objetos la cual tiene un elem ento básico: la clase. En
este capítulo, aprenderem os tam bién a organizar las clases en paquetes, lo que su
pone tam bién un nivel m ás de protección para las m ismas.
U na clase es un tipo definido por el usuario que describe los atributos y los m éto
dos de los objetos que se crearán a partir de la m ism a. Los atributos definen el
estado de un determ inado objeto y los m étodos son las operaciones que definen su
com portam iento. Form an parte de estos m étodos los constructores, que perm iten
iniciar un objeto, y los destructores, que perm iten destruirlo. Los atributos y los
m étodos se denom inan en general m iem bros de la clase.
class Circulo
I
// m i e m b r o s p r i v a d o s
p r i v a t e d o u b l e x. y : // c o o r d e n a d a s d e l c e n t r o
p riv a t e double ra d io : II r a d i o d e l c i r c u l o
II m i e m b r o s p r o t e g i d o s
protected void m sgE sN e gativ o()
I
S y ste m .o u t .p rin tln ( "El r a d io e s n e g a t i v o . Se c o n v ie r t e a p o s i t i v o ” ):
// m ie m b ro s p ú b l i c o s
p u b l i c C l r c u l o O I ) // c o n s t r u c t o r s i n p a r á m e t r o s
p u b l i c C i r c u l o t d o u b l e e x , d o u b l e c y . d o u b l e r ) // c o n s t r u c t o r
I
x = ex; y — cy:
i f ( r < 0)
I
m s g E s N e g a t i v o ( ):
r = -r;
1
r a d i o = r;
p u b lic double á r e a C I r c u l o ( )
I
return Math.PI * radio * radio;
C A PÍTU LO 9: CL A SES Y PAQUETES 2 5 5
E ste ejem plo define un nuevo tipo de datos. Círculo, que puede ser utilizado
dentro de un program a fuente exactam ente igual que cualquier otro tipo. Un o b
jeto de la clase C írculo tendrá los atributos x , y y radio, los m étodos m sgEsN ega-
tivo, lo n g C ircu n feren d a y áreaC írculo, y dos constructores Círculo, uno sin
parám etros y o tro con ellos.
Atributos
L os atributos constituyen la estructura interna de los objetos de una clase. Para
declarar un atributo, proceda exactam ente igual que ha hecho para declarar cual
quier otra variable dentro de un m étodo. Por ejem plo:
class Circulo
p r i v a t e do u b le x, y;
p riv a t e double radio:
II...
I
En una clase, cada atributo debe tener un nom bre único. En cam bio, se puede
u tilizar el m ism o nom bre con atributos, en general con m iem bros, que pertenez
can a diferentes clases.
class Círculo
[
private d o u b l e x, y;
private d o u b l e r a d i o = 1:
// ...
l
Tam bién podem os declarar com o atributos de una clase, referencias a otros
objetos de clases existentes. El siguiente ejem plo define la clase P unto y después
declara el atributo centro de Círculo, de la clase Punto.
class Punto
(
private d o u b l e x. y:
class Circulo
P ara definir un m étodo m iem bro de una clase, proceda exactam ente igual que
ha hecho para definir cualquier otro m étodo en las aplicaciones realizadas en los
capítulos anteriores. N o olvide que una aplicación se basa en una clase. Como
ejem plo puede observar los m étodos C írculo y longC ircunferencia de la clase
Círculo.
class Circulo
I
II . . .
p u b l i c C 1 r c u l o ( d o u b l e ex. double cy. double r) // c o n s t r u c t o r
I
x = ex: y = cy:
i f ( r < 0)
I
m sgEsNegati v o ( ):
r = -r :
I
r a d i o = r;
l
En Java un m étodo es una definición incluida siem pre dentro del cuerpo de
una clase. A sim ism o, recuerde que los m étodos no se pueden anidar.
El concepto de clase incluye la idea de ocultación de datos, que básicam ente con
siste en que no se puede acceder a los atributos directam ente, sino que hay que
hacerlo a través de m étodos de la clase. Esto quiere decir que, de form a general, el
usuario de la clase sólo tendrá acceso a uno o m ás m étodos que le perm itirán ac
ceder a los m iem bros privados, ignorando la disposición de éstos (dichos m étodos
se denom inan m étodos de acceso). De esta form a se consiguen dos objetivos im
portantes:
Piense que si el objetivo uno no se cum pliera, cuando se diera el objetivo dos
el usuario tendría que rcescribir el código que hubiera desarrollado basándose en
la eslructura interna d e los datos.
Para controlar el acceso a los m iem bros de una clase. Java provee las palabras
clave p rív a te (privado), p ro te c te d (protegido) y p u b lic (público), aunque tam
bién es posible om itirlas (acceso predeterm inado). E stas palabras clave, denom i
nadas m odificadores d e acceso, son utilizadas para indicar el tipo de acceso
perm itido a cada m iem bro de la clase. Si observam os la clase C irculo expuesta
anteriorm ente identificam os m iem bros privados, protegidos y públicos.
Acceso predeterminado
class CRacional
I
i n t Numerador:
i n t Denominador:
N u me r a d o r = num:
i f (den = 0 ) den - 1; //el d e n o m i n a d o r no p u e d e s e r c e r o
D e n o m i n a d o r = den:
I
void V i s u a l i z a r R a c i o n a l ()
(
System .out.printín(Num erador + "/ " + D e n o m i n a d o r ):
Un m iem bro de una clase declarado sin m odificadores que indiquen el control
de acceso, puede ser accedido por cualquier clase perteneciente al m ism o paquete.
N inguna otra clase, o subclase, fuera de este paquete puede tener acceso a estos
m iem bros (estudiarem os las subclases en el capítulo siguiente). R ecuerde que las
clases im plem entadas en nuestros program as pertenecen, p o r om isión, al paquete
predeterm inado (vea en el capítulo 4 “Paquetes y protección de clases” ). De esta
form a Jav a asegura q ue toda clase pertenece a un paquete.
Acceso público
Un m iem bro declarado p u b lic (público) está accesible para cualquier otra clase o
subclase que necesite utilizarlo. La interfaz pública de una clase, o sim plem ente
interfaz, está form ada por todos los m iem bros públicos de la m ism a. Asim ism o,
los atributos s ta tic de la clase generalm ente son declarados públicos. Sirva com o
ejem plo el atributo P l de la clase M ath : p u b lic static fin a l double Pl.
Acceso privado
Un m iem bro declarado p riv a te (privado) es accesible solam ente por los m étodos
de su propia clase. Esto significa que no puede ser accedido por los m étodos de
cualquier otra clase, incluidas las subclases.
Acceso protegido
Un m iem bro declarado p ro te c te d (protegido) se com porta exactam ente igual que
uno privado para los m étodos de cualquier otra clase, excepto para los m étodos de
las clases del m ism o paquete o de sus subclases con independencia del paquete al
que pertenezcan, para las que se com porta com o un m iem bro público.
C A PÍTU LO 9: CL A SES Y PAQUETES 2 5 9
Parece lógico que la estructura de datos de un objeto fecha esté form ada por
los m iem bros día, m es y año, y perm anezca oculta al usuario. Por otra parte, las
operaciones sobre estos objetos tendrán que perm itir asignar una fecha, m étodo
asignarF echa, obtener una fecha de un objeto existente, m étodo obtenerFecha, y
verificar si la fecha que se quiere asignar es correcta, m étodo fechaC orrecta. Es
tos tres m étodos form arán la interfaz pública. C uando el día corresponda al mes
de febrero, el m étodo fechaC orrecta necesitará com probar si el año es bisiesto pa
ra lo que añadirem os el m étodo bisiesto. Ya que un usuario no necesita acceder a
este m étodo, lo declararem os protegido con la intención de que, en un futuro, sí
pueda ser accedido desde una subclase. Según lo expuesto, podem os escribir una
clase denom inada C Fecha así:
// M é t o d o s
p r o t e c t e d b o o l e a n bi s i e s t o ()
I
// c u e r p o d e l mé t o d o
I
public void asignarFechatint dd . i n t mm. int aaaa)
I
// c u e r p o d e l mét odo
)
public void obte nerFecha (int[] fecha)
I
II c u e r p o d e l mét odo
I
public b o o l e a n f e c h a C o r r e c t a ()
II c u e r p o d e l m ét o d o
2 6 0 JA V A : C U R SO DE PROGRAM A CIÓN
El paso siguiente es definir cada uno de los m étodos. Al h ablar de los m odifi
cadores de acceso quedó claro que cada uno de los m étodos de una clase tiene ac
ceso directo al resto de los m iem bros. Según esto, la definición del m étodo
asignarF echa puede escribirse así:
O bserve que por ser asignarF echa un m étodo m iem bro de la clase C Fecha,
puede acceder directam ente a los atributos día, m es y año de su m ism a clase, in
dependientem ente de que sean privados. Estos atributos corresponderán en cada
caso al o b jeto que recibe el m ensaje asignarF echa (objeto para el que se invoca el
m étodo; vea m ás adelante, en este m ism o capítulo, la referencia im plícita this).
Por ejem plo, si declaram os los objetos fe c h a I y fe c h a 2 de la clase C F echa, y en
viam os a fe c h a l el m ensaje asignarF echa:
f e c h a l . a s i g n a r F e c h a ( d d . mm. a a a a ) :
com o respuesta a este m ensaje, se ejecuta el m étodo asignarF echa que asigna los
datos dd, m m y aaaa al objeto f e c h a l ; esto es, a fe c h a l.d ía , fe c h a l.m e s y f e
chal.año-, y si a fe c h a 2 le enviam os tam bién el m ensaje asignarFecha:
f e c h a 2 . a s i g n a r F e c h a ( d d , mm, a a a a ) :
com o respuesta a este m ensaje, se ejecuta el m étodo asignarF echa que asigna los
datos dd, m m y aaaa al objeto fe c h a l: esto es, a fe c h a !.d ía , fe c h a l.m e s y f e
ch a l.a ñ o .
/////////////////////////////////////////////////////////////////
// D e f i n i c i ó n de l a c l a s e CFecha
p u b l i c c l a s s C F ec h a
I
// A t r i b u t o s
private int día. mes , año;
// M é t o d o s
protected boolean b i s i e s t o ! )
I
return ((año X 4 — 0 ) && ( a ñ o % 1 00 ! = 0 ) || ( a ñ o X 4 0 0 — 0)):
C A PÍTU LO 9: CL A SES Y PA Q U ETES 2 6 1
R esum iendo: la funcionalidad de esta clase está soportada por los atributos
privados día, m es y año, y por los m étodos asignar Fecha, obtenerFecha, fe c h a -
Correcla y bisiesto.
El m étodo público asignarF echa, recibe tres enteros y los alm acena en los
atributos d ía. m es y año del objeto que recibe el m ensaje asignarF echa (objeto
para el que se invoca dicho m étodo).
El m étodo público obtenerFecha, perm ite extraer los datos día, m es y año del
objeto que recibe el m ensaje obtenerFecha.
262 JA V A : C U R SO D E PROGRAM A CIÓN
MÉTODOS SOBRECARGADOS
En los capítulos anteriores, al trabajar con las clases de la biblioteca de Java nos
hem os encontrado con clases que im plem entan varias veces el m ism o m étodo. Por
ejem plo, en el capítulo 5 dijim os que la clase In p u t S t r e a m im plem enta tres for
m as del m étodo read:
y que la clase P rin tS tr e a m im plem enta m últiples form as de los m étodos p rin t y
p rin tln : por ejem plo:
Pues bien, cuando en una clase un m ism o m étodo se define varias veces con
distinto núm ero de parám etros, o bien con el m ism o núm ero de parám etros pero
diferenciándose una definición de otra en que al m enos un parám etro es de un tipo
diferente, se dice que el m étodo está sobrecargado.
Los m étodos sobrecargados pueden diferir tam bién en el tipo del v alor retor
nado. A hora bien, el com pilador Java no adm ite que se declararen dos m étodos
que sólo difieran en el tipo del v alor retornado; deben diferir tam bién en la lista de
parám etros; esto es, lo que im porta son el núm ero y el tipo de los parám etros.
C om o ejem plo, sobrecargarem os el m étodo asignarF echa p ara que pueda ser
invocado con cero argum entos; con un argum ento, el día; con dos argum entos, el
día y el m es; y con tres argum entos, el día, el m es y el año. Los datos día, m es o
año om itidos en cualquiera de los casos, serán obtenidos de la fecha actual pro
porcionada por el sistem a.
f e c h a . a s i g n a r F e c h a f ):
fecha.asignarFecha(dla):
f e c h a . a s i g n a r F e c h a ( d 1 a . mes):
f e c h a , a s i g n a r F e c h a ( d i a . mes . a ñ o ) ;
Si el com pilador Java no encontrara un m étodo exactam ente con los m ism os
tipos de argum entos especificados en la llam ada, realizaría sobre dichos argu
m entos las conversiones im plícitas perm itidas entre tipos, tratando de adaptarlos a
alguna de las definiciones existentes del m étodo. Si este intento fracasa, entonces
se producirá un error.
Para com probar que la clase C Fecha que acabam os de diseñar trabaja correc
tam ente, podem os escribir una aplicación Test según se m uestra a continuación:
/////////////////////////////////////////////////////////////////
// A p l i c a c i ó n que u t i l i z a la c l a s e CFecha
//
public class Test
I
// V i s u a l i z a r una f e c h a
public sta t ic void visualizarFecha(C Fech a fecha)
I
i n t [ ] f = new i n t [ 3 ] ;
f e c h a .o b t e n e r F e c h a ( f ) :
System .out.p rintln(f[0] + "/" + f[l] + "/" + f[2]);
// E s t a b l e c e r una f e c h a , v e r i f i c a r l a y v i s u a l i z a r l a
p u b lic s t a t ic void m a in (S trin g [] args)
I
C F ec h a f e c h a - new C F e c h a í ) : // o b j e t o d e t i p o C F ech a
i n t d í a . me s , a ñ o :
do
I
S y s t e m . o u t .p r in t t "d ía . ## : "): día - L e e r . d a t o I n t ( ):
System, out. p r i n t C m e s . M : "): mes - L e e r . d a t o l n t t );
CA PÍTU LO 9: CL A SES Y PA Q U ETES 2 6 5
visualizarFechaí fecha ):
N otar que la clase C Fecha declara los atributos día, m es y a ñ o privados. Esto
quiere decir que sólo son accesibles por los m étodos de su clase. Si un m étodo de
otra clase intenta acceder a uno de estos atributos, el com pilador genera un error.
En cam bio, com o C Fecha y Test pertenecen al m ism o paquete, al predeterm ina
do. los m étodos de Test sí podrían acceder el m étodo protegido bisiesto de C Fe
cha. Por ejemplo:
En cam bio, los m étodos asignarF echa, obtenerF echa y fech a C o rrecta son
públicos. Por lo tanto, son accesibles, adem ás de p o r los m étodos de su clase, por
cualquier otro m étodo de otra clase. S irva com o ejem plo el m étodo visualizarFe-
cha de la clase Test. Este m étodo presenta en la salida estándar la fecha alm ace
nada en el objeto que se le pasa com o argum ento. O bserve que tiene que invocar
al m étodo obtenerF echa para acceder a los datos de un objeto C Fecha. Esto es así
porque un m étodo que no es m iem bro de la clase del objeto, no tiene acceso a sus
datos privados.
Por om isión una clase tiene el nivel de acceso de p a q u ete; por ejem plo, la cla
se C írculo expuesta anteriorm ente tiene este nivel de acceso (no ha sido declarada
266 JA VA: CU R SO DE PROGRAM A CIÓN
p u b lic, por lo que tiene el nivel de acceso de paquete). En cam bio, cuando se de
sea que una clase tenga nivel de acceso p ú b lico , hay que calificarla com o tal utili
zando la palabra reservada p u b lic: la clase C Fecha del ejem plo anterior tiene este
nivel de acceso. O tro ejem plo: la clase L eer utilizada desde la clase Test anterior
es pública (en nuestro caso está ubicada en la carpeta m isC lases especificada por
una de las rutas de la variable de entorno CLASSPATH)-, pero aunque no hubiese
sido pública tam bién se podría u tilizar desde la clase Test, ya que am bas pertene
cen al m ism o paquete, al predeterm inado.
REFERENCIA this
f e c h a l . a s i g n a r F e c h a t d í a . mes . año):
S egún lo expuesto, el m étodo asignarF echa podría ser definido tam bién com o
se m uestra a continuación:
do
w hile ( ! fecha.fechaCorrecta()):
En este caso, igual que en el ejem plo anterior, el m étodo fechaC orrecta cono
ce con exactitud el objeto sobre el que tiene que actuar, puesto que se ha expresa
do explícitam ente. Pero ¿qué pasa con el m étodo bisiesto que se encuentra sin
referencia directa alguna en el cuerpo del m étodo fech a C o rrecta ?
En este o tro caso, la llam ada no es explícita com o en el caso anterior. L o que
ocurre en la realidad es que todas las referencias a los atributos y m étodos del
objeto para el que se invocó el m étodo fech a C o rrecta (objeto que recibió el m en
saje fech a C o rrecta), son im plícitam ente realizadas a través de th is. S egún esto, la
sentencia if anterior podría escribirse tam bién así:
N orm alm ente en un m étodo no es necesario utilizar esta referencia para acce
der a los m iem bros del objeto im plícito, pero es útil cuando haya que devolver
una referencia al mismo.
static p ara hacer q ue la constante sea de la clase y no del objeto. D eclarar una re
ferencia fin a l a un objeto supone que esa referencia sólo pueda utilizarse para re-
ferenciar ese objeto; cualquier intento accidental de m odificar dicha referencia
para q ue señale a otro objeto será detectado durante la com pilación, en vez de
causar errores durante la ejecución. Por ejem plo;
f i n a l C F e c h a c u m p l e a ñ o s = new C F e c h a O ;
C F e c h a f e c h a = new C F e c h a O :
// ...
cumpleaños = fecha: II Error: referencia constante
¿Q uién puede cam biar el m étodo? U na subclase que intente redefinirlo, pero
sólo podrá hacerlo si el m étodo no es fin a l (estudiarem os las subclases en el ca
pítulo siguiente).
Q uizás cuando desarrolle una clase por prim era v ez no tenga m uchas razones
para d ecid ir qué m étodos puede declarar final. H ágalo cuando necesite que la cla
se se ejecute con m ás rapidez, pero pensando en la lim itación que está im ponien
do a las posibles subclases de esa clase. La biblioteca de Java declara final
m uchos de los m étodos que se utilizan con m ayor frecuencia con la intención de
obtener una m ayor eficiencia durante la ejecución.
Una clase, tam bién se puede declarar final. Por ejem plo:
C uando una clase se declara fin a l estam os im pidiendo que de esa clase se
puedan derivar subclases. A dem ás todos sus m étodos se convierten autom ática
m ente en final. N o hay m uchas razones para hacer esto ya que sacrificam os una
de las características m ás potentes de la POO: la reutilización del código. En al
gunos casos excepcionales, com o ocurre con la clase M a t h de la biblioteca de Ja
va, puede ser beneficioso por las razones expuestas al h ablar de los m étodos final.
C A P ÍT U L O 9: CL A SES Y PAQUETES 2 6 9
INICIACIÓN DE UN OBJETO
Sabem os que un objeto consta de una estructura interna (los atributos) y de una
interfaz que perm ite acceder y m anipular tal estructura (los m étodos). Ahora,
¿cóm o se construye un objeto de una clase cualquiera? Pues, de form a análoga a
com o se construye cualquier otra variable de un tipo predefinido. Por ejem plo:
i n t edad ;
Este ejem plo define la variable ed a d del tipo predefinido int. En este caso, el
com pilador autom áticam ente reserva m em oria para su ubicación, le asigna un
valor (cero si se trata de un atributo de una clase, o indeterm inado si es local a un
m étodo) y procederá a su destrucción, cuando el flujo de ejecución vaya fuera del
ám bito donde haya sido definida.
E sto nos hace pensar en la idea de que de alguna m anera el com pilador llama
a un m étodo de iniciación, constructor, para iniciar cada una de las variables de
claradas, y a un m étodo de elim inación, destructor, para liberar el espacio ocupa
do por dichas variables, ju sto al salir del ám bito en el que han sido definidas.
Pues bien, con un objeto de una clase ocurre lo m ism o. Por ejem plo,
C Fech a f e c h a = new C F e c h a O :
Constructor
En Java, una form a de asegurar que los objetos siem pre contengan valores válidos
es escribir un constructor. Un constructor es un m étodo especial de una clase que
es llam ado autom áticam ente siem pre que se crea un objeto de la m ism a. Su fun
ción es iniciar nuevos objetos de su clase. C uando se crea un objeto, Java hace lo
siguiente:
270 JA V A: C U R SO DE PROGRAM A CIÓN
D ado q ue los constructores son m étodos, adm iten parám etros igual que éstos.
C uando en una clase no especificam os ningún constructor, el com pilador añade
uno público por om isión sin parám etros.
Un constructor se distingue fácilm ente porque tiene el m ism o nom bre que la
clase a la q ue pertenece (por ejem plo, el constructor para la clase C Fecha se de
nom ina tam bién C Fecha), no se hereda, no puede retom ar un valor (incluyendo
v o id ) y no puede ser declarado fin al, sta tic , a b s tra c t, s y n c h ro n iz e d o n a tiv e (los
dos prim eros m odificadores ya son conocidos; los otros lo serán en la m edida que
am pliem os nuestros conocim ientos sobre Java).
public c la ss CFecha
I
// A t r i b u t o s
p r í v a t e i n t d í a , mes , año;
// M é t o d o s
p u b l i c C F e c h a ( i n t dd. i n t mm. int aaaa) // c o n s t r u c t o r
I
d í a = dd ; mes = mm; a ñ o = a a a a :
i f ( ! f e c h a C o r r e c t a ( ))
1
System .out.println("Fecha incorrecta. Se a s i g n a la actual.");
a s i g n a r F e c h a t ):
1
1
// ...
C A P ÍT U L O 9: CLA SES Y PAQUETES 2 7 1
C uando una clase tiene un constructor, éste será invocado autom áticam ente
siem pre que se cree un nuevo objeto de esa clase. El objeto se considera construi
do con ios valores p o r om isión ju sto antes de iniciarse la ejecución del construc
tor. P o r lo tanto, a continuación, desde el cuerpo del constructor según se puede
observar en el ejem plo anterior, es posible asignar valores a sus atributos, invocar
a los m étodos de su clase, o bien llam ar a m étodos de otros objetos.
En el caso de que el constructor tenga parám etros, para crear un nuevo objeto
hay que especificar la lista de argum entos correspondiente entre los paréntesis que
siguen al nom bre de la clase del objeto. El siguiente ejem plo m uestra esto con cla
ridad:
// V i s u a l i z a r una f e c h a
p u b lic s t a t i c void v is u a liz a rF e c h a (C F e c h a fecha)
I
i n t [] f = new i n t [ 3 ] :
fecha.obtenerFecha(f);
System .out.pri n t l n ( f [0] + "/ " + f [1] + "/" + f [2]):
I
Este ejem plo define un objeto fe c h a e inicia sus datos m iem bro día, m es y
año con los valores 1, 3 y 2002, respectivam ente. Para ello, invoca al constructor
C Fecha(int dd, int mm, int a a a a ), le pasa los argum entos 1, 3 y 2002 y ejecuta el
código que se especifica en el cuerpo del m ism o. U na vez construido el objeto, vi
sualizam os su contenido haciendo uso d e un m étodo del m ism o que perm ite obte
ner sus atributos. La siguiente línea es la salida de la aplicación anterior:
1 / 3 / 2 00 2
A ñadam os ahora al m étodo m a in de la clase Test del ejem plo anterior, la lí
nea de código que se indica a continuación. ¿Q ué ocurrirá?
272 JA V A: C U R SO DE PROGRAM A CIÓN
CF e c h a o t r a F e c h a = new C F e c h a O ;
public CFechaO // c o n s t r u c t o r
I
asignarFechaO ; // a s i g n a r f e c h a actual
Por ejem plo, aplicando lo expuesto, podem os añadir a la clase CFecha cons
tructores para iniciar un objeto, por om isión con la fecha actual proporcionada por
la función asignarF echa sin parám etros, o bien especificando sólo el día, o el día
y el m es, o el día, el m es y el año; los valores no especificados se obtendrán de la
fecha actual del sistem a proporcionada por asignarFecha. El código siguiente
m uestra las distintas sobrecargas que satisfacen lo anteriorm ente expuesto:
Es posible escribir un m étodo que tenga el m ism o nom bre que el constructor;
lógicam ente, a diferencia de éste, ahora hay que especificar el tipo del valor retor
nado. N o obstante, esta form a de proceder no es aconsejable porque puede crear
confusión a la hora de interpretar el código de la clase. P or ejem plo:
Llamar a un constructor
A diferencia de los otros m étodos de la clase, un constructor no puede ser invoca
do directam ente, pero sí indirectam ente a través de th is. Esto perm ite utilizar la
técnica de m étodo abreviado, expuesta al hablar de m étodos sobrecargados, tam
bién con los constructores. Para llam ar a un constructor en la clase actual desde
otro constructor utilice la siguiente sintaxis:
public C F e c h a t i n t dd) // c o n s t r u c t o r
t h i s O ; / / i n v o c a a l c o n s t r u c t o r C F e c h a s i n p a r á m e t r o s
d 1a = d d :
i f ( I f e c h a C o r r e c t a t ))
I
System .out.printlnt"Fecha incorrecta. Se a s i g n a la actual."):
a s i g n a r F e c h a í ):
Asignación de objetos
N o olvide que cuando trabaja con objetos lo que realm ente m anipula desde cu al
quier m étodo son referencias a los objetos. P or ejem plo:
C F ec h a f e c h a l - new CFechaO;
C F ec h a f e c h a 2 - new CFecha(15):
C F ec h a f e c h a 3 - new CFecha(22. 3):
C F e c h a f e c h a 4 - f e c h a l :
f e c h a 3 - f e c h a 2 :
El m étodo copiar copia m iem bro a m iem bro el objeto pasado com o argu
m ento en el objeto que recibe el m ensaje copiar. P or ejem plo, la siguiente línea de
código copia el objeto fe c h a 2 en el objeto fe c h a 1.
fechal.copiar(fecha2);
fecha2.copiar(fecha3);
fecha 1 . c o p i a r ( f e c h a 2 ) :
Pero ¿qué podem os hacer para poder escribir las dos líneas anteriores en una
sola? E sto es, para poder escribir:
f e c h a l . c o p i a r ( f e c h a 2 . c o p i a r ( f e c h a 3 ) ):
Constructor copia
O tra form a de iniciar un objeto es asignándole otro objeto de su m ism a clase en el
m om ento de su creación. Lógicam ente, si se crea un objeto tiene que intervenir un
constructor. El prototipo para este constructor es de la forma:
276 JA V A: C U R S O D E PROGRAM A CIÓN
C om o ejem plo, añada un constructor copia a la clase C Fecha. É ste será com o
se indica a continuación:
p u b l i c C F e c h a l C F e c h a o b j ) // c o n s t r u c t o r copia
I
día = obj.di a :
mes = o b j . m e s :
año = o b j . a ñ o :
I
V em os que el constructor copia acepta com o argum ento una referencia al ob
je to a copiar y después, asigna m iem bro a m iem bro ese objeto al nuevo objeto
construido. Para probar cóm o trabaja, puede añadir a la función m a in de la clase
Test que escribim os anteriorm ente, las siguientes líneas de código:
C F ec h a f e c h a l = new C F e c h a d . 3. 2 0 0 2 ) :
C F ec h a f e c h a 2 = new C F e c h a í f e c h a l );
E ste ejem plo crea e inicia un objeto fe c h a l y a continuación crea otro objeto
fe c h a 2 iniciándole con fe c h a l. A diferencia del m étodo copiar expuesto en el
apartado anterior, inicialm ente aquí sólo existe un objeto (fechal):; después se crea
otro objeto (fech a 2 ) y se inicia con el prim ero.
DESTRUCCIÓN DE OBJETOS
De la m ism a form a que existe un m étodo que se ejecuta autom áticam ente cada
vez que se construye un objeto, tam bién existe un m étodo que se invoca autom áti
cam ente cada vez que se destruye. E ste m étodo recibe el nom bre genérico de
d estructor y en el caso concreto de Java se corresponde con el m étodo finalize.
Un objeto es destruido autom áticam ente cuando se elim inan todas las referen
cias al m ism o. U na referencia a un objeto puede ser elim inada porque el flujo de
ejecución salga fuera del ám bito donde ella está declarada, o porque explícita
m ente se le asigne el valor nuil.
Destructor
Un d estructor es un m étodo especial de una clase que se ejecuta antes de que un
objeto de esa clase sea elim inado físicam ente de la m em oria. Un destructor se
distingue fácilm ente porque tiene el nom bre predeterm inado finalize. C uando en
una clase no especificam os un destructor, el com pilador proporciona uno a través
de la clase O b je c t cuya sintaxis es la siguiente:
Para d efinir un destructor en una clase tiene que reescribir el m étodo anterior.
A diferencia de lo que ocurría con los constructores, en una clase sólo es posible
definir un destructor. En el cuerpo del m ism o puede escribir cualquier operación
que quiera realizar relacionada con el objeto que se vaya a destruir.
R esum iendo: un destructor es invocado autom áticam ente ju sto antes de que el
objeto sea recolectado com o basura por el recolector de basura de Java. Y
¿cuándo ocurre esto? C uando no queden referencias al objeto.
public c la ss CFecha
I
// ...
public c la s s Test
I
// V i s u a l i z a r una f e c h a
278 JA VA: C U R S O DE PRO G R A M A CIÓ N
Si una clase tiene m iem bros que son objetos de otras clases, su destructor se
ejecuta antes que los destructores de los objetos m iem bro. En otras palabras, el
orden de destrucción es inverso al orden de construcción.
Un destructor tam bién se puede llam ar explícitam ente así: objeto. finalize().
Sin em bargo, invocar a finalize no activa un objeto para que sea enviado a la
basura. Sólo cuando se elim inan todas las referencias que apuntan al m ism o, éste
se m arca com o destruible.
CA PÍTU LO 9: C L A SES Y PAQUETES 2 7 9
A hora bien, si desea forzar una com pleta recolección de basura (m arcar y ba
rrer), puede hacerlo llam ando al m étodo ge (garbage collector: recolector de ba
sura) de la clase S ystem . P or ejem plo:
fecha.obtenerFecha(f);
S y stem .o ut.p r i n t í n < f [0] + "/" + f[l] + "/" + f[2]);
1
P ara ver lo expuesto con detalle, vam os a escribir una clase C V ector para
construir objetos que representen m atrices num éricas con un núm ero cualquiera
de elem entos. P or lo tanto, sería inapropiado definir com o m iem bro privado de la
clase C V ector una m atriz con un núm ero fijo de elem entos. E n su lugar, definire
mos una referencia, vector, a una m atriz de tipo d o u b le, p o r ejem plo, para d es
pués asignar dinám icam ente la cantidad de m em oria necesaria para la m atriz.
o b je to C V e c to r
S X m atriz
v e c to r 1— -*
.......... □
n E le m e n to s J
V J
Y p o r los m étodos:
• constructores para crear un objeto C V ector con un núm ero de elem entos pre
determ inado, con un núm ero de elem entos especificado, a partir de una m atriz
unidim ensional, o bien a partir de otro objeto CVector.
El trabajo que tienen que realizar los constructores de la clase C Vector. de
pendiendo de los casos, es asignar la m em oria necesaria para la m atriz de da
tos e iniciar dicha m atriz con ceros (iniciación por om isión), con otra m atriz o
con otro vector, com o podem os ver a continuación:
C A PÍTU LO 9: CL A SES Y PAQUETES 2 8 1
public C V e c t o r t ) // n ú m ero de e l e m e n t o s p o r o m i s i ó n : 10
I
n E l e m e n t o s = 10:
v e c t o r = new d o u b l e [ n E 1e m e n t o s ] :
p u b lic C V ector(C V e c to r v) // c o n s t r u c t o r c o p i a
I
n E l e m e n t o s = v . n E 1e m e n t o s :
v e c t o r = new d o u b l e [ n E l e m e n t o s ] :
// C o p i a r e l o b j e t o v
f o r ( i n t i = 0: i < nElem entos: i + + )
vectorfi] = v.vector[i]¡
I
O bservar que este m étodo adem ás de copiar los atributos del objeto v en el
objeto referenciado por th is, copia tam bién los valores de la m atriz; si no hi
ciera esto últim o tendríam os una sola m atriz referenciada p o r dos objetos.
• co p ia r: m étodo que perm ite asignar un objeto C V ector a otro. O bservar que
este m étodo realiza el m ism o proceso que el constructor copia; adem ás, retor
na una referencia al objeto resultante de la copia.
• p o n erV a lo rE n: m étodo que perm ite asignar un dato al elem ento especificado
de un objeto CVector.
p u b l i c v o i d p o n e rV a lo rE n í i n t i . d o u b le v a l o r ) I v e c t o r [ i ] = v a l o r ; I
• valorEn: m étodo que devuelve el dato alm acenado en el elem ento especifica
do de un o bjeto CVector.
• longitud-, m étodo que devuelve el núm ero de elem entos de un objeto CVector.
/////////////////////////////////////////////////////////////////
// D e f i n i c i ó n de l a clase CVector
II
public class CVector
I
private double[] vector; // m a t r i z v e c t o r
private in t nElementos; II nú m ero de e l e m e n t o s de l a matriz
public C V e c t o r í d o u b l e [ ] m) // c r e a un C V e c t o r d e s d e una m a t r i z
(
nEle mentos = m .length ;
v e c t o r = new d o u b l e [ n E 1e m e n t o s ] :
// C o p i a r l o s e l e m e n t o s de l a m a t r i z m
f o r ( i n t i = 0; i < nElem entos: i + + )
v e c t o r í i ] = m [ i ]:
return this;
Para probar la clase expuesta escriba, por ejem plo, la siguiente aplicación:
/////////////////////////////////////////////////////////////////
// A p l i c a c i ó n q u e u t i l i z a la clase CVector
//
public class Test
I
// V i s u a l i z a r un v e c t o r
p u b l i c s t a t i c v o id v i s u a 1i z a r V e c t o r ( C V e c t o r v)
I
i n t ne = v . 1o n g i tudC );
f o r ( i n t i = 0: i < ne; 1 + + )
System .out.p r i n t ( v .valo r E n ( i ) + " ");
S y s t e m . o u t . p r i n t l n ( ):
C V e c t o r v e c t o r 3 = new C V e c t o r ( v e c t o r 2 );
vi sual i z a r V e c t o r lv e c t o r 3):
d o u b l e x [ ] = I 1, 2 , 3. 4, 5 . 6 . 7 I : // m a t r i z x
C V e c t o r v e c t o r 4 = new C V e c t o r ( x ) :
v i s u a l i z a r V e c t o r í v e c t o r 4 );
llam a al constructor C V ector(int ne) y crea un objeto ve cto rl con 5 elem entos.
Las líneas:
C V e c t o r v e c t o r 2 = new C V e c t o r O ;
CA PÍTU LO 9: CL A SES Y PA Q U ETES 2 8 5
llam an al co nstructor C V ector sin argum entos y crea un objeto vector2 con 10
elem entos por om isión. D espués asigna valores a cada uno de los elem entos de
vector2. L a línea:
C V e c t o r v e c t o r 3 = new C V e c t o r t v e c t o r 2 ) ;
llam a al constructor copia y crea un objeto vector3 iniciado con los datos del o b
je to vector2. Las líneas:
double x [ ] - 1 1 . 2 . 3 . 4 . 5. 6. 7 I : 7/ m a t r i z x
C V e c t o r v e c t o r 4 = new C V e c t o r ( x ) ;
C om o se puede observar, cada vez que se crea un objeto es llam ado autom áti
cam ente un constructor, lo que garantiza la iniciación del objeto. El que se llam e a
uno o a o tro constructor, depende del núm ero y tipo de argum entos especificados.
C uando el flujo de ejecución sale fuera del ám bito donde ha sido definido un
objeto C V ector, el recolector de basura m arcará y barrerá tanto el objeto com o la
m atriz referenciada p o r el m ism o, liberando la m em oria ocupada.
Sin em bargo, una clase con m iem bros que son referencias a otros objetos,
com o es C Vector, potencialm ente tiene problem as. Para com probarlo, suponga
que al diseñador de la clase C V ector se le hubiera ocurrido escribir el constructor
copia así:
public CVector(CVector v) // c o n s t r u c t o r c o p i a
I
nEle mentos = v . n E1e m e n to s:
vector = v.vector;
d o u b l e x [ ] = I 1 , 2 , 3 . 4 . 5. 6. 7 (; // m a t r i z x
C V e c t o r v e c t o r l = new C V e c t o r ( x ) :
v i s u a l i z a r V e c t o r ( v e c t o r l ) ; // e s c r i b e 1 2 3 4 5 6 7
f o r ( i n t i = 0: i < v e c t o r 2 . l o n g i t u d * ) : i + + )
v e c t o r 2 . p o n e r V a l o r E n ( i . v e c t o r 2 . v a l o r E n t i ) * 10):
v i s u a l i z a r V e c t o r ( v e c t o r 2 ) ; // e s c r i b e 10 2 0 3 0 4 0 5 0 6 0 70
I
// v e c t o r 2 ha s i d o d e s t r u i d o
v i s u a l i z a r V e c t o r { v e c t o r l ); // e s c r i b e 10 2 0 3 0 4 0 50 6 0 70
Ahora el m étodo m a in crea un objeto ve cto rl iniciado con los valores de una
m atriz x e incluye un bloque que crea un nuevo objeto vector2 a partir de v e c to r l,
para lo cual se invoca al constructor copia.
O bserve que ahora este constructor sim plem ente copia los atributos del objeto
v en los correspondientes atributos del nuevo objeto creado. P or lo tanto, el resul
tado de una sentencia com o:
C V e c t o r v e c t o r 2 = new C V e c t o r í v e c t o r l );
v e c to rl ve c to r2
m a triz
c_ _ _ v e c to r
-c_ _ _ vecto r
í n E le m e n to s [ lE le m e n to s
Piense ahora qué sucederá cuando el flujo de ejecución salga fuera del ámbito
de vecto rl. Pues que el objeto v e c to r l será enviado a la basura y elim inado por el
recolector de basura. ¿S erá enviado tam bién a la basura el objeto m atriz referen-
CA PÍTU LO 9 : CL A SES Y PAQUETES 2 8 7
Esta m ism a teoría es aplicable al m étodo copiar. Esto significa que debem os
poner un especial interés cuando escribam os m étodos que tengan com o finalidad
duplicar objetos que tienen atributos que son referencias a otros objetos.
COMPARAR OBJETOS
Según vim os en el capítulo anterior, la clase O b je c t es la clase raíz de la jerarquía
de clases de la biblioteca Java y de cualquier otra clase que im plem entem os en
nuestras aplicaciones, lo que se traduce en que todos ellas heredan los m étodos de
O bject, com o equals, to S tr in g o finalize. por ejem plo.
¿C óm o han sido im plem entados estos m étodos? Pues de una form a m uy gené
rica, sin pensar en ningún objeto en particular. P or ejem plo, e q u a ls proporciona el
m ism o resultado que el operador esto es, com para las referencias a los o b
jeto s, no sus contenidos, lo cual es lógico: no podem os com parar dos objetos que
aún no sabem os cóm o son. A hora bien, una vez diseñada una clase com o CVec-
tor, si necesitam os que el m étodo e q u a ls nos diga cóm o es un objeto C V ector con
respecto a otro, tenem os que sobreescribir dicho m étodo.
Método equals
/////////////////////////////////////////////////////////////////
// A p l i c a c i ó n que u t i l i z a la clase CVector
//
288 JA V A: C U R S O DE PROGRAM A CIÓN
public c la s s Test
I
II V i s u a l i z a r un v e c t o r
public s t a t ic void visu a liz a rV e c to r(C V e c to r v)
I
i n t ne = v . l o n g i t u d ! );
f o r ( i n t i = 0: i < ne: 1+ + )
System.out.print(v.valorEn(i) + " ” );
S y s t e m . o u t . p r i n t l n C );
C V e c t o r v e c t o r 2 = new C V e c t o r ( v e c t o r l );
for ( i n t i = 0 : i < v e c t o r 2 . 1 o n g i t u d ( ) : i + + )
v e c t o r 2 . p o n e r V a l o r E n ( i . v e c t o r 2 . v a l o r E n ( i )* 10):
v i s u a l i z a r V e c t o r ( v e c t o r 2 ) : // e s c r i b e 10 2 0 30 4 0 50 6 0 70
if (vectorl = vector2)
System, out. p r i n t l n C r e f e r e n c i a s al mi s mo o b j e t o ' ' ) :
e l se
System .out.println("referencias a objetos diferentes"):
if (vectorl.equals(vector2))
System .out.pri n t ln ("o b je to s i g u a l e s ” );
el s e
System .out.println("objetos d i f e r e n t e s " ):
1.0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7.0
10.0 2 0.0 30.0 4 0.0 50.0 6 0.0 70.0
referencias a objetos diferentes
objetos diferentes
Atributos static
La últim a versión de la clase C irculo definida al principio de este capítulo decla
raba do s atributos: centro y radio. Esto se traduce en que cada objeto de la clase,
cada círculo, tiene su propia copia de esos dos atributos. Pero seguro que en más
de una ocasión querrem os utilizar un atributo (una variable) del cual exista una
única copia que pueda ser utilizada por todos los objetos de la m ism a clase; esto
es. una variable con ám bito global. El problem a es que Java no perm ite declarar
variables globales tal com o se interpretan en otros lenguajes de program ación; ca
da variable en Java debe ser declarada dentro de una clase, la cual define su pro
pio ám bito. La alternativa que Java ofrece para dar solución al problem a
planteado es d eclarar el atributo static.
C om o ejem plo, supongam os que querem os por una parte, no tener que acce
d er a la constante P I de la clase M a t h cada vez que calculem os el área del círculo
0 la longitud de la circunferencia, y por otra, conocer el núm ero de objetos C ír
culo que hay creados en cada instante. Para hacer esto, obviam ente es m ás efi
ciente asociar dos atributos con la clase, p i y num C írculos, que con cada objeto.
El código m ostrado a continuación m uestra cóm o añadir estos atributos a la clase:
class Punto
1
p r iv a t e double x, y;
public c la ss Cireulo
(
// A t r i b u t o s
p r i v a t e s t a t i c d o u b l e pi = 3 . 1 4 1 5 9 2 ;
p u b li c s t a t i c i n t numCírculos;
// Métodos
290 JA V A: C U R SO DE PRO G R A M A CIÓ N
t h is ( 100.0, 1 0 0 . C “1 0 0 . 0 ) :
I
p u b lic double á r e a C I r c u l o ( )
I
return pi * radio * radio:
i
i
public c la ss Test
I
public static void m a in (S t r in g [] args)
I
C i r c u l o o b j l = new C l r c u l o O :
S y s t e m . o u t .p r i n t l n ( o b j 1 .1 ongCi r c u n f e r e n c ia ( ) ) ;
System .out.p r in t ln (o b jl.á re a C i r c u l o ( )):
C i r c u l o o b j 2 = new C i r c u l o í l O O . 1 0 0 , 1 0 ) :
System .out.pri ntln(o bj2.1o n gC ircun feren cia());
S y ste m .o u t.p rin tln (o b j2 .á re a C irc u lo ());
S y s t e m . o u t . p r i n t l n ( C 1 r c u l o . numCi r c u l o s ) ;
A nteriorm ente dijim os que Java no perm ite declarar variables globales. No
obstante, C írculo.num C írculos se com porta igual que si lo fuera, ya que utilizando
esta sintaxis podem os acceder a num C írculos desde cualquier otra clase.
Métodos static
Un m étodo declarado static carece de la referencia this por lo que no puede ser
invocado para un objeto de su clase, sino que se invoca en general allí donde se
necesite utilizar la operación para la que ha sido escrito. D esde este punto de vista
es im posible que un m étodo static pueda acceder a un m iem bro no static de su
clase; por la m ism a razón, sí puede acceder a un m iem bro static. C om o ejem plo,
recuerde la clase M a t h estudiada en el capítulo 5; uno de sus m étodos es sq rt y la
form a de invocarlo desde cualquier m étodo de otra clase es: M ath.sqrt(n). C om o
vem os, utilizam os esta expresión para invocar al m étodo s q r t de la clase M a t h y
calcular la raíz cuadrada de n sin pensar en ningún objeto en particular.
Un m étodo s ta tic puede acceder a los m iem bros (atributo o m étodo) sta tic de
su clase pero no puede acceder a los m iem bros no sta tic . P or ejem plo, si en el
m étodo anterior intenta establecer el atributo radio a 0, el com pilador le m ostrará
un erro r indicándole que no se puede hacer referencia a una variable no estática
desde un m étodo estático.
P or otra parte, un m iem bro s ta tic sí puede ser accedido por un m étodo inde
pendientem ente de que sea s ta tic o no. Por ejem plo, el m iem bro p i de la clase
C írculo es accedido por los m étodos no estáticos longC ircunferencia y áreaCír-
cu lo y por el m étodo estático cam biarP recisiónP iA de su m ism a clase. Si el acce
so se hace desde un m étodo de otra clase, dicho m iem bro tiene que ser invocado a
través del nom bre de la clase según se explicó anteriorm ente. Por ejem plo:
C1rculo.cambiarPrecisiónPiA(3.l4);
C i r c u l o o b j 2 = new C l r c u í o t l O O . 1 0 0 . 1 0 ) :
S y s t e m . o u t . p r i n t l n ( o b j 2 . 1ongCi r c u n f e r e n c i a ( ) ) :
S y ste m .o u t.p rin tln (o b j2 .á re a C 1 rc u lo t)):
S y s t e m . o u t . p r i n t l n ( C 1 r c u l o . n u m C I r c u l o s ):
Iniciador estático
Sabem os que tanto los atributos del objeto com o los de la clase pueden ser inicia
dos en la propia declaración. Sirva com o ejem plo el atributo p i de la clase Círcu
lo. A hora, m ientras que los atributos de la clase son iniciados cuando la clase es
cargada por prim era vez, los atributos del objeto son iniciados para cada objeto en
el instante de su creación.
C A PÍTU LO 9: CL A SES Y PAQUETES 2 9 3
stati c
I
// i n i c i a c i ó n ele l o s atributos de l a clase
1
C om o ejem plo, vam os a añadir a la clase C írculo dos atributos sta tic . seno y
coseno, q ue proporcionen las tablas del seno y coseno de grado en grado. Dichos
atributos serán iniciados a través de un iniciador estático com o se puede observar
a continuación:
p u b l i c el a s s Cí r e u l o
I
// A t r i b u t o s
p r i v a t e s t a t i c d o u b l e pi = 3 . 1 4 1 5 9 2 :
pu blic s t a t ic int num Cireulos:
p u b l i c s t a t i c d o u b l e s e n o [ ] = new d o u b l e C 3 6 0 ] :
p u b l i c s t a t i c d o u b l e c o s e n o ü = new d o u b l e [ 3 6 0 ] :
// I n i c i a d o r e s t á t i c o
static
1
// T a b l a s d e l s e n o y c o s e n o de g r a d o en g r a d o
fo r ( i n t i = 0 ; i < 360: i++)
I
d o u b l e s . c:
// C a l c u l a r e l s e n o y e l c o s e n o de i
s = M a th .s in (M a t h .t o R a d i a n s ( i )):
c = Math.co s(M a th .t o R a d ia n s ( i)):
// A l m a c e n a r l o s v a l o r e s r e d o n d e a d o s a 6 d e c i m a l e s
s e n o f i ] = M a t h . r i n t ( s * 1 0 0 0 0 0 0 )/1000000:
c o s e n o [ i ] = M a t h . r i n t ( c * 1 0 0 0 0 0 0 >/1000000:
294 JA V A: C U R S O DH PROGRAM A CIÓN
Java perm ite cualquier núm ero de iniciadores estáticos aunque el com pilador
finalm ente los fusionará en uno sólo en el m ism o orden en el que aparezcan en la
definición de la clase. E ste iniciador se ejecutará solam ente una vez: cuando el
sistem a cargue la clase p o r prim era vez.
MATRICES DE OBJETOS
Se puede crear una m atriz de objetos de cualquier clase, de la m ism a form a que se
crea una m atriz de núm eros, de caracteres, de objetos S trin g , etc. Por ejemplo,
suponiendo que tenem os definida una clase C Persona podem os definir la matriz
listaTeléfonos con 100 elem entos de la form a siguiente:
1 i s t a T e l é f o n o s [ i ] = new C P e r s o n a í [ a r g u m e n t o s ] ) :
El listado siguiente m uestra un ejem plo de una clase C Persona que define los
atributos privados nom bre, dirección y teléfono relativos a una persona, y los
m étodos públicos que form an la interfaz de esta clase de objetos:
/////////////////////////////////////////////////////////////////
// D e f i n i c i ó n de l a c l a s e CPersona
//
public c la ss CPersona
I
// A t r i b u t o s
p r i v a t e S t r i n g nombre:
private Strin g dirección;
p riva te long te léfon o:
// M é t o d o s
public CPersonaí) I )
public CPersonaíString nom, String dir. long t e l )
I
nombre = nom:
di r e c c i ó n = d i r :
teléfono = t e l ;
nombre = nom;
r e t u r n n o mbr e ;
public void a s i g n a r T e l é f o n o ( 1 o ng t e l )
I
teléfono = t e l ;
U n m étodo com o asignarN om bre sim plem ente asigna el nom bre pasado co
m o argum ento al atributo nom bre del objeto que recibe el m ensaje. Y un m étodo
com o obtenerN om bre devuelve el atributo nom bre del objeto que recibe el m en
saje. La explicación para los otros m étodos es análoga. P or ejem plo:
C P e r s o n a o b j = new C P e r s o n a O ;
o b j . a s i g n a r N o m b r e ( " J a v i e r " );
System .out.println(obj.obtenerN om bre()): // e s c r i b e : Javier
El listado siguiente m uestra un ejem plo de lo que puede ser la clase lista de
teléfonos, que denom inarem os C ListaTfnos. D efine los atributos privados lista-
Teléfonos. m atriz de objetos C Persona, y nE lem entos, núm ero de elem entos de la
m atriz, y los m étodos que se describen a continuación:
/////////////////////////////////////////////////////////////////
// D e f i n i c i ó n de l a clase CListaTfnos.
//
public c la ss CListaTfnos
I
p r i v a t e C P e r s o n a f ] 1 i s t a T e l é f o n o s ; // m a t r i z de o b j e t o s
p r i v a t e i n t n E l e m e n t o s : // nú me r o d e e l e m e n t o s de l a m a t r i z
Para crear un objeto lista de teléfonos escribirem os una línea de código como
la siguiente:
S egún este ejem plo, la clase C ListaT fnos tiene que tener un constructor sin
argum entos ¿Q ué debe hacer este constructor? Iniciar un objeto C ListaTfnos con
una m atriz listaTeiéfonos con 0 elem entos:
A ntes de que se ejecute el cuerpo del constructor anterior, nE lem entos vale 0
y listaTeléfonos n u il; y después de que se ejecute, nE lem entos sigue valiendo 0 y
listaT eléfonos referencia una m atriz de longitud 0 (propiedad le n g th = 0).
p u b l i c b o o le an el i m i n a r ( l o n g tel)
I
// B u s c a r e l t e l é f o n o y e l i m i n a r r e g i s t r o
f o r ( i n t i = 0; i < n E l e m e n t o s : i + + )
i f (1 ist a T e lé fo n o sfi] . obtenerTeléfono() == te l)
I
1i s t a í e l é f o n o s ü i ] = n u il ;
un E l e m e n t o M e n o s d i s t a T e l é f o n o s ) ;
return true:
)
return false:
I
O bservam os que cuando se invoca al m étodo unE lem entoM enos se pasa como
argum ento la lista de teléfonos actual que ahora quedará referenciada por su pa
rám etro listaA ctual ¿Q ué tiene que hacer este m étodo? Pues, asignar al atributo
UstaTeléfonos un nuevo espacio de m em oria que perm ita albergar un elemento
m enos de los que tiene actualm ente, co p iar uno a uno los elem entos que tenía
hasta ahora la m atriz (referenciados p o r listaA ctual) m enos el que tiene asignado
la referencia n u il y decrem entar el atributo nE lem entos. C uando la ejecución de
este m étodo finalice, el bloque de m em oria viejo referenciado p o r listaActual será
enviado a la basura y recolectado p o r el recolector de basura.
C A P ÍT U L O 9: CLASES Y PAQUETES 2 9 9
pos = 1 i s t a t f n o s . b u s c a r t c a d e n a b u s c a r . p o s i c i ó n _ i n i c i o _ b ú s q u e d a ):
p u b lic CPersona va lo rE n ( in t i )
I
i f ( i > = 0 && i < n E l e m e n t o s )
r e t u r n 1 i s t a T e l é f o n o s [ i ]:
300 JA V A : C U R SO D E PRO G R A M A CIÓ N
el se
I
System .out.p rin tln C Índ ice f u e r a de l i m i t e s " ) ;
r e t u r n n u l 1:
El m étodo longitud devuelve el núm ero de elem entos que tiene actualm ente la
m atriz listaTeléfonos.
1. B u s c a r
2. B u s c a r s i g u i e n t e
3. A ñ a d i r
4. E l i m i n a r
5. S a l i r
Opción: 3
n o mbr e : Javier
dirección: Santander
teléfono: 942232323
1. B u s c a r
2. B u s c a r s i g u i e n t e
3. A ñ a d i r
4. E l i m i n a r
5. S a l i r
Opción:
A la vista del resultado anterior, esta aplicación m ostrará un m enú que pre
sentará las operaciones que se pueden realizar sobre la lista de teléfonos. Poste
riorm ente, la operación elegida será identificada p o r una sentencia sw itch y
procesada de acuerdo al esquem a presentado a continuación:
// C r e a r un o b j e t o l i s t a d e t e l é f o n o s v a c i o
C L i s t a T f n o s l i s t a t f n o s = new C L i s t a í f n o s ( ) :
do
I
opc i ón = me nú( ):
switch (opción)
I
hhhhhhhhhhhhhhhhhhhhhhhhhhhhbbí
// B u s c a r un e l e m e n t o q u e c o n t e n g a " c a d e n a b u s c a r " .
// E s t a s u b c a d e n a s e r á o b t e n i d a d e l t e c l a d o ,
pos = 1 i s t a t f n o s . b u s c a r ( c a d e n a b u s c a r , 0);
// S i s e e n c u e n t r a , m o s t r a r s u s d a t o s
break;
b us c a r
II B u s c a r e l s i g u i e n t e e l e m e n t o q u e c o n t e n g a l a subcadena
// u t i l i z a d a en l a ú l t i m a b ú s q u e d a .
pos = 1i s t a t f n o s .b u s c a r ( c a d e n a b u s c a r. pos + 1);
II S i s e e n c u e n t r a , m o s t r a r s u s d a t o s ,
break; __
// O b t e n e r d e l t e c l a d o l o s d a t o s n o mb r e , d i r e c c i ó n y
II t e l é f o n o d e l n u e v o e l e m e n t o a a ñ a d i r , y a ñ a d i r l o ,
l i s t a t f n o s . a ñ a d i r t n e w C P e r s on a ( no mb r e. d i r e c c i ó n , t e l é f o n o ) ) ;
break;
■ bhhhhmhhhhbhhhhhhnhbbhhhhhhhbhmnhi
// O b t e n e r d e l t e c l a d o e l nú me r o de t e l é f o n o a e l i m i n a r y
// e l i m i n a r l o de l a 1 i s t a .
elim inado = 1i s t a t f n o s . e lim i n a r t t e lé f o n o ) ;
break¡
c a s e 5: / / s a l i r
1i s t a t f n o s = n u i l :
w h i 1e ( o p c i ó n != 5 ) ;
import j a v a . i o . * ;
/////////////////////////////////////////////////////////////////
II A p l i c a c i ó n p a r a t r a b a j a r con m a t r i c e s de o b j e t o s
//
public class Test
I
public static in t menúO
I
S y s t e m . o u t . p r i n t ( “ \ n \ n ” ):
S y s t e m . o u t . p r i n t l n C ' l . B u s c a r ” );
302 JA V A: C U R S O D E PRO G R A M A CIÓ N
// C r e a r un o b j e t o l i s t a de t e l é f o n o s v a c i o ( c o n c e r o e l e m e n t o s )
C L i s t a T f n o s l i s t a t f n o s = new C L i s t a T f n o s ( ):
i n t o p c ió n = 0. pos = -1:
S t r in g cadenabuscar - n u il;
S t r i n g n o mbr e , d i r e c c i ó n ;
long te léfono;
boolean e lim ina d o - f a l s e ;
do
I
try
1
sw itch (opción)
f l u j o S . p r i n t t " c o n j u n t o de c a r a c t e r e s a b u s c a r ");
c a d e n a b u s c a r - f l u j o E . r e a d L i n e í ):
pos = l i s t a t f n o s . b u s c a r ( c a d e n a b u s c a r , 0 ):
i f ( pos — -1)
i f (1 i s t a t f n o s . 1 o n g i t u d ( ) ! = 0 )
f l u j o S . p r i n t l n ( " b ú s q u e d a f a l l i d a ” ):
el se
f 1 u j o S . p r i n t l n ( "1 i s t a v a c i a " ) :
el se
I
f 1 u j o S . p r i n t l n ( 1 i s t a t f n o s . v a l o r E n ( p o s ) . obtener No mb r e( ) ) :
f l u j o S . p r i n t l n d i s t a t f n o s . v a l o r E n ( p o s ) . o b t e n e r D i r e c c i ó n ( ));
C A PÍTU LO 9: CL A SES Y PAQUETES 3 0 3
f l u j o S . p r i n t l n í 1i s t a t f n o s . v a l o r E n ( p o s ) . o b t e n e r T e l é f o n o ( ) ) ;
I
break;
c a s e 2: // b u s c a r s i g u i e n t e '
pos = 1 i s t a t f n o s . b u s c a r t c a d e n a b u s c a r . pos + 1 ) :
i f (pos == -1)
i f (1 i s t a t f n o s . 1 o n g i t u d ( ) ! = 0 )
f 1u j o S . p r i n t l n ( " b ú s q u e d a f a l 1 i d a ” );
el se
f 1 u j o S . p r i n t l n ( "1 i s t a v a c i a " );
el s e
I
f 1u j o S . p r i n t l n ( 1 i s t a t f n o s . v a l o r E n ( p o s ) . o bt e ner Nombret) ) ;
f l u j o S . p r i n t I n d i s t a t f n o s . v a l o r E n ( p o s ) . o b t e n e r ü i r e c c i ó n ( )):
f l u j o S . p r i n t l n ( l i s t a t f n o s . v a l o r E n ( p o s ) . o b t e n e r T e l é f o n o í ));
I
break;
c a s e 3: añadir
flujoS.printt"nom bre: ” ); nombre = f l u j o E . r e a d L i n e t ):
f l u j o S . p r i n t C d i r e c c i ó n : " ) ; d i r e c c i ó n = f l u j o E . r e a d L i n e ( );
flujoS.printt"teléfono: ” ); t e l é f o n o = L e e r . d a t o L o n g t ):
1 i s t a t f n o s . a ñ a d i r ( n e w C Pers ona( nombr e, d i r e c c i ó n , t e l é f o n o ) ) ;
break;
c a s e 4: // e l i m i n a r
f 1 u j o S . p r i n t ( " t e l é f o n o : " ) ; t e l é f o n o = L e e r . d a t o L o n g ( );
e lim in a d o = 1i s t a t f n o s . e l i m i n a r ( t e l é f o n o ) :
i f (elim inado)
f l u j o S . p r i n t l n ( " r e g i s t r o e l i m i n a d o " ):
el se
i f (1 i s t a t f n o s . l o n g i t u d ( ) ! = 0)
f l u j o S . p r i n t l n ( " t e l é f o n o no e n c o n t r a d o ” ):
el se
f l u j o S . p r i n t l n ( ” 1 i s t a v a c i a " ):
break; __ _
c a s e 5: // s a l i r
1i statfnos = nuil;
PAQUETES
En el capítulo 4 ya fue expuesto el concepto de paquete. Si recuerda, dijim os que
un paquete es un conjunto de clases, lógicam ente relacionadas entre sí, agrupadas
bajo un nom bre; incluso, un paquete puede contener a otros paquetes.
304 JA V A : C U R S O DE PROGRAM A CIÓN
Tam bién vim os que la propia biblioteca de clases de Java estaba organizada
en paquetes dispuestos jerárquicam ente. La jerarq u ía a la que nos referim os es
análoga a la estructura jerárq u ica de carpetas o directorios que utilizam os para o r
ganizar los ficheros en un disco duro.
A sim ism o sabem os que para referim os a una clase d e un paquete, tenem os
que hacerlo anteponiendo al nom bre de la m ism a el nom bre de su paquete, ex
cepto cuando el paquete haya sido im portado explícitam ente, com o se indica en el
siguiente ejem plo, o im plícitam ente (caso del paquete ja v a .Ia n g ). P or ejem plo, la
aplicación Test anterior utiliza, entre otras, la clase In p u t S t r e a m R e a d e r del pa
quete java.io. D ebido a que la aplicación incluye la línea de código:
import j a v a . i o . * :
podem os referim os a esa clase sim plem ente p o r su nom bre. En otro caso, ten
dríam os que haber utilizado su nom bre com pleto: ja v a .io .In p u tS tre a m R e a d e r.
R esum iendo: los paquetes ayudan a organizar las clases en grupos para faci
litar el acceso a las m ism as cuando las necesitem os en un program a; reducen los
conflictos de nom bres (lógicam ente, la probabilidad de que dos nom bres coinci
dan será m enor cuantos m ás elem entos intervengan); y perm iten proteger las cla
ses (una clase con nivel de protección de paquete, clase no pública, no está
disponible para otros paquetes, ni siquiera para los subpaquetes).
Crear un paquete
Para crear un paquete hay que seguir básicam ente los pasos indicados a continua
ción:
1. Seleccio nar e l nom bre d e l paquete. Para nom brar un paquete, Sun M icrosys
tem s recom ienda utilizar el nom bre de su dom inio de Internet, pero con los
elem entos a la inversa. Por ejem plo, si Sun hubiera seguido esta recom enda
ción en todos los casos, todos sus paquetes em pezarían p o r co m .su n .ja v a ya
que el dom inio de Internet para Java es ja va .su n .co m . Puede alargar el nom
bre para describir genéricam ente las clases del paquete; por ejemplo:
com .sun.java.sw ing. La idea que se persigue es la exclusividad del nombre
del paquete, con el fin de no cau sar conflictos con los paquetes de otros.
R ealicem os un ejem plo para practicar. D ejando ahora a un lado el dom inio de
Internet, supongam os que deseam os crear los paquetes:
m isClases.es
mi s C l a s e s . ú t i l i d a d e s
CA PÍTU LO 9: CL A SES Y PAQUETES 3 0 5
C L A S S P A T H " . : c : \ j a v a \ j d k l . 3 \ m i s C l a s e s : c : \ j a v a \ j d k l .3
S iguiendo con el ejem plo, cream os la carpeta m isC lases en la ruta especifica
da y, dentro de ella, las carpetas es y utilidades.
B misClases
!■■P~l es
•Q utilidades
Para finalizar el ejem plo, coloque la clase L eer que im plem entam os en el ca
pítulo 5, en la carpeta es (entrada salida). E dítela y añada la siguiente línea:
package
import j a v a . i o . * ;
p u b l i c c l a s s Leer
I
II C u e r p o de l a clase
I
package m i s C l a s e s . u t i 1i d a d e s :
p u b lic e l a s s CPersona
I
// C u e r p o de l a clase
306 JA V A: C U R SO DE PROGRAM A CIÓN
package H H B S H H
pu blic c la ss CListaTfnos
I
// C u e r p o d e l a clase
I i^ . _
Para probar los paquetes que acabam os de crear, copie en un nuevo directorio
la aplicación Test que realizam os anteriorm ente (sólo el fichero Test.java). Des
pués, edítela y añada las sentencias im p o r t necesarias para especificar el paquete
al que pertenecen las clases Leer, C Persona y C ListaTfnos utilizadas por la apli
cación. Finalm ente, com pile y ejecute la aplicación para com probar los resulta
dos. Puede observar que al com pilar la aplicación Test tam bién serán com piladas
las clases Leer, C Persona y C ListaTfnos, si aún no lo estaban.
import m i s C l a s e s . e s . * :
import m i s C l a s e s . u t i l i d a d e s . * :
import j a v a . i o . * :
/////////////////////////////////////////////////////////////////
// A p l i c a c i ó n para t r a b a j a r con m a t r i c e s de o b j e t o s
//
public c la ss Test
1
// C u e r p o de l a clase
U na clase que envuelva un núm ero racional es útil porque m uchos de estos
núm eros no pueden ser representados exactam ente utilizando el tipo float. Por
ejem plo, 1/3 + 1/3 + 1/3, que es 1, utilizando el tipo float sería 0,333333 +
0,333333 + 0,333333, que es 0,999999. Para evitar este tipo de errores debem os
considerar a un núm ero racional com o un objeto con entidad propia. Esto se con
sigue diseñando una clase, denom inada p o r ejem plo C R acional, con los atributos
num erador y denom inador, y con una interfaz que perm ita realizar cualquier ope
ración en la que pueda intervenir un núm ero racional.
public c la s s CRacional
I
// A t r i b u t o s
p r i v a t e lo ng numerador;
p r i v a t e lo ng denominador;
CA PÍTU LO 9: C L A SES Y PAQUETES 3 0 7
// M é t o d o s
I:
pu blic CRacional() // c o n s t r u c t o r
I
n u m e r a d o r = 0:
denomi n a d o r = 1;
I
C uando utilicem os argum entos para construir un núm ero racional, otras ope
raciones que debe realizar el constructor son verificar si el denom inador es cero,
en cu y o caso podem os forzar a que sea 1, o negativo, en cuyo caso invertim os el
signo del num erador y del denom inador. A sim ism o, sim plificará la fracción siem
pre que sea posible. Según esto la definición del constructor C R acional con dos
argum entos puede ser así:
if ( denominador = = 0 )
I
S y s t e m . o u t . p r i n t l n ( " E r r o r : d e n o m i n a d o r 0. Se a s i g n a 1.");
d e n o m i n a d o r = 1;
I
308 JA V A: C U R S O DE PROGRAM A CIÓN
if ( denomi n a d o r < 0 )
I
numerador = -numerador;
denominador = denominador;
1
S i m p l i f i c a r C );
I
La función m iem bro Sim plificar utiliza el algoritm o de Euclides para obtener
el m áxim o com ún divisor (m cd) del num erador y del denom inador, y sim plificar
el núm ero racional dividiendo el num erador y el denom inador p o r ese m cd.
O tros constructores de interés pueden ser: uno que nos convierta un entero en
un núm ero racional y otro, el constructor copia:
public C R a c i o n a l ( l o n g num ) // c o n s t r u c t o r
I
n u m e r a d o r = num;
denomi n a d o r = 1;
public C R a c io n a l( CRacional r ) // c o n s t r u c t o r c o p i a
I
numerador = r .n u m e ra d o r;
denominador = r .denominador;
C A PÍTU LO 9: CL A SES Y PAQUETES 3 0 9
C om o el operador + no está definido para los núm eros racionales y tam poco
podem os definirlo, la solución puede ser utilizar una sintaxis com o la siguiente:
r3 - rl.sum art r 2 ) :
Com o vem os, la solución es escribir un m étodo su m a r con un parám etro que
haga referencia a un objeto C Racional, el operando de la derecha; el operando de
la izquierda referencia el objeto que recibe el m ensaje sum ar. La función debe de
volver una referencia al objeto C R acional resultado de la sum a. Según lo ex
puesto, el m étodo puede ser el siguiente:
Esta versión crea un objeto tem p invocando al constructor C Racional con los
valores resultantes de realizar la sum a, y devuelve tem p com o resultado ya sim
plificado por el constructor. Este m étodo podría escribirse tam bién así:
p u b l i c C R a c i o n a l sumar( C R a c i o n a l r )
i
r e t u r n new C R a c i o n a l ( n u m e r a d o r * r . d e n o m i n a d o r +
denominador * r.num erador,
denominador * r.den o m ina do r ):
|
Esta versión crea un objeto tem poral invocando al constructor C Racional con
los valores resultantes de realizar la sum a, y lo devuelve com o resultado una vez
sim plificado por el constructor.
l o n g n - 2:
C R a c i o n a l r 2 = new C R a c i o n a l d . 4):
310 JA V A : C U R SO DE PRO G R A M A CIÓ N
C uando ejecute este código, observará que todo funciona correctam ente. Esto
se debe a que la expresión new C Racional(n), utilizando el constructor de la clase,
construye un objeto tem poral que es el que recibe el m ensaje sum ar. El resto del
proceso ocurre com o se ha explicado anteriorm ente.
Pensem os ahora en las operaciones de com paración; por ejem plo en la opera
ción que nos perm ita saber si dos racionales son iguales. Supongam os las si
guientes declaraciones:
C R a c i o n a l r l = new C R a c i o n a l ( 1 ) :
C R a c i o n a l r 2 = new C R a c i o n a l d . 4 ) :
C R a c io n a l r3;
r3 = r l .s u m a r ( r 2 ) :
C R a c i o n a l r 4 = new C R a c i o n a l ( r 2 ) :
// ...
P ara com probar si dos objetos r2 y r3 son iguales, podem os proceder com o se
puede observar a continuación:
if (r3.e q u als(r2)) rl = r 3 . s u m a r ( r 4 );
esto es, el usuario no debe tener la posibilidad de acceder a los atributos num era
d o r y denom inador de form a independiente.
Jav a tiene varias sobrecargas de los m étodos p r in t y p r in tln para perm itir la
salida de valores de tipos predefinidos y objetos de las clases O b jec t, S trin g y
m atriz de caracteres. Si nuestra intención es utilizar la m ism a sintaxis para visua
lizar un objeto C Racional, nos encontrarem os con que, lógicam ente, no existe una
sobrecarga de estos m étodos para esta clase d e objetos. P ensando en lo que real
m ente hacen estos m étodos, convertir su argum ento en un objeto S trin g , podem os
redefinir el m étodo to S trin g heredado de la clase O b je c t, para convertir un objeto
C R acional en un objeto S trin g . De esta form a, m ostrar un objeto C R acional re
sultará tan sencillo com o se m uestra a continuación:
C R a c i o n a l r l = new C R a c i o n a l U . 4 ) ;
Syste m .o u t.p ri n t l n ( r l .t o S t r i n g ( )):
Igual que para los m étodos p rin t y p rin tln . Java tiene varias sobrecargas del
m étodo re a d pero no existe una sobrecarga que perm ita leer objetos de la clase
C R acional a través del teclado. Por esta razón, vam os a añadir a esta clase un
m étodo leer que perm ita teclear un núm ero racional según el form ato siguiente:
/ - ]entero[/entero¡. E ste m étodo, utilizando el m étodo dato de la clase L eer que
expusim os en el capítulo 5, leerá en núm ero racional com o una cadena de caracte
res. U na vez leído, el m étodo leer verificará si el form ato es válido; para ello debe
cum plirse que el p rim er carácter sea el signo m enos o un dígito del 0 al 9, que los
siguientes caracteres sean dígitos y que si hay una “/” , sólo sea una y no esté en la
últim a posición. La lectura se repetirá m ientras la cadena no sea válida; en otro
caso, se extraerá el num erador y el denom inador que utilizarem os com o argu
m entos en la construcción del objeto C R acional que será devuelto por el m étodo.
A continuación puede ver el código com pleto para este m étodo:
ba r r a s = 0;
System .o ut.p rin tí" [ - ] entero[/entero]: "):
r a c i o n a l = L e e r . d a t o O ; // l e e r e l r a c i o n a l
i f ( r a c i o n a l . 1 e n g t h ( ) = = 0)
carácterVálido = false;
else
I
// El p r i m e r c a r á c t e r p u e d e s e r un d í g i t o o e l s i g n o menos
c a r á c t e r V á l i do =
( r a c i o n a l . c h a r A t ( O ) > = ’ 0 ’ && r a c i o n a l . c h a r A t ( O ) < = ' 9 ' ) ||
(racional.charAt(O) = && r a c i o n a l . 1 e n g t h í ) > 1 ) ;
// El ú l t i m o c a r á c t e r no p u e d e s e r una /
i f ( r a c i o n a l .c h a r A t í r a c i o n a l .1 e n g t h í )-1 ) == ’/ ’ )
carácterVálido = false:
I
// El r e s t o de l o s c a r a c t e r e s p u e d e n s e r d í g i t o s o / ( s ó l o una)
f o r ( i = 1; c a r á c t e r V á l i d o && i < r a c i o n a 1 . 1 e n g t h í ) ; 1 + + )
I
c a r á c t e r V á l i d o = r a c i o n a l . c h a r A t í i ) >= ' 0 ' &&
r a c i o n a 1 . c h a r A t ( i ) <= ' 9 ' ||
racional.charA tíi) —
i f ( r a c i o n a l . c h a r A t í i ) == ’ / ’ ) barras++;
i f ( b a r r a s > 1) c a r á c t e r V á l i d o = f a l s e :
I
if ( IcarácterVálido) System .o u t .p r i n t l n ( " Entrada no v á l i d a . " ) :
I
w hile (Ic a rá c te rV á lid o ):
// E x t r a e r e l n u m e r a d o r y e l d e n o m i n a d o r
i f ( ( i = r a c i o n a l . i n d e x O f í ' / ’ ) ) — - 1 ) // no h a y d e n o m i n a d o r
I
num = L o n g . p a r s e L o n g í r a c i o n a l );
den = 1:
I
el se
I
num = L o n g . p a r s e L o n g í r a c i o n a l . s u b s t r i n g í O , i ) ) : // 0 a i - 1
den = L o n g . p a r s e L o n g í r a c i o n a l . s u b s t r i n g í i + 1 ) ) :
I
// C o n s t r u i r y d e v o l v e r e l o b j e t o C R a c i o n a l
r e t u r n new C R a c i o n a l ( n u m . d e n ) ;
CRacional r l = new C R a c i o n a l ( 1 ) :
CRacional r 2 = new C R a c i o n a l í l . 4 ) ;
í< ■• ■
CA PÍTU LO 9: CLASES Y PAQUETES 3 1 3
rl.copiar(r2);
En ocasiones, puede ser necesario saber si un núm ero racional es cero. Por
ejem plo, para saber si el racional r es cero podríam os escribir:
if ( r . e s C e r o t )) ...
En este ejem plo se observa que el objeto r recibe el m ensaje esC ero. La res
puesta a este m ensaje será la ejecución del m étodo esC ero que deberá devolver
tr u e si el racional es cero, o false en caso contrario. D icho m étodo puede escribir
se así:
// V e r i f i c a r s i e s 0
p u b l i c booleari e s C e r o t )
I
return n u m e r a d o r = = 0;
I
O tras operaciones de interés pueden ser increm entar y decrem entar en una
unidad un núm ero racional. P or ejem plo:
r 2 . c o p i a r ( r l . i n c r e m e n t a r ( ) ) ; // i n c r e m e n t a r r l y copiarlo en r 2
r 3 . d e c r e m e n t a r ( ) ; // i n c r e m e n t a r r 3
Los m étodos correspondientes que perm iten realizar las operaciones m encio
nadas son los siguientes:
// I n c r e m e n t a r en 1
p u b lic CRacional increm entar!)
314 JA V A : C U R S O D E PROGRAM A CIÓN
numerador + = denominador;
return th is;
// D e c r e m e n t a r en 1
p u b lic CRacional decrem entar()
I
numerador -= denominador;
return th is;
1
rl. c o p i a r ( r l . c a m b i a d o D e S i g n o ( ) ) :
El m étodo cam biadoD eSigno debe devolver el v alor cam biado de signo del
racional que recibió este m ensaje, pero sin m odificar éste. La solución se m uestra
a continuación:
// - u n a r i o
p u b lic CRacional cambiadoDeSigno()
I
C R a c i o n a l temp = new C R a c i o n a l ( -numerador, denominador ):
r e t u r n temp;
1
L os m étodos expuestos no son los únicos; sim plem ente son un ejem plo de las
m uchas operaciones que se pueden program ar. A continuación se m uestra el códi
go com pleto que hem os escrito para la clase C R acional:
/////////////////////////////////////////////////////////////////
// C l a s e p a r a o p e r a r con números racionales (u tiliza la clase Leer)
//
public c la ss CRacional
I
// A t r i b u t o s
p r i v a t e lo ng numerador;
p r i v a t e lo n g denominador;
// M é t o d o s
protected CRacional Sim p lifica r()
I
// M á x i m o común d i v i s o r
l o n g mcd, temp, r e s t o ;
mcd = M a t h . a b s ( n u m e r a d o r );
temp = M a t h . a b s ( d e n o m i n a d o r );
CA PÍTU LO 9: C L A SES Y PAQUETES 3 1 5
public CRacionalO // c o n s t r u c t o r
I
n u m e r a d o r = 0:
denomi n a d o r = 1:
// S u m a r n ú m e r o s racionales
p u b lic CRacional sumart C R a c i o n a l r )
I
return new C R a c i o n a l ( n u m e r a d o r * r . d e n o m i n a d o r +
denomi n a d o r * r . n u m e r a d o r ,
denomi n a d o r * r . d e n o m i n a d o r ):
// R e s t a r n ú m e r o s r a c i o n a l e s
p u b lic CRacional r e s t a r í CRacional r )
I
return new C R a c i o n a l ( n u m e r a d o r * r . d e n o m i n a d o r -
denomi n a d o r * r . n u m e r a d o r ,
denomi n a d o r * r . d e n o m i n a d o r ):
// M u l t i p l i c a r n ú m e r o s r a c i o n a l e s
p u b l i c C R a c i o n a l muí t i p l i c a r ( C R a c i o n a l r )
I
return new C R a c i o n a l ( n u m e r a d o r * r . n u m e r a d o r ,
denominador * r.den o m ina do r ):
// D i v i d i r n ú m e r o s r a c i o n a l e s
p u b lic CRacional d i v i d i r í CRacional r )
I
r e t u r n new C R a c i o n a 1 ( n u m e r a d o r * r . d e n o m i n a d o r .
denominador * r.nu m erado r );
// V e r i f i c a r s i d o s n ú m e r o s r a c i o n a l e s son iguales
p u b lic boolean e q u a ls í CRacional r )
I
return ( numerador * r . denominador =
d e n o m in a d o r * r . n u m e r a d o r );
// V e r i f i c a r s i un r a c i o n a l e s menor q u e o t r o
p u b l i c b o o l e a n me n o r ( C R a c i o n a l r )
I
return ( numerador * r .denominador <
d e n o m i n a d o r * r . n u m e r a d o r );
// V e r i f i c a r s i un r a c i o n a l e s m a y o r que o t r o
p u b l i c b o o l e a n mayor( C R a c i o n a l r )
I
return ( numerador * r .denominador >
d e n o m i n a d o r * r . n u m e r a d o r );
CA PÍTU LO 9: CL A SES Y PAQUETES 3 1 7
// D e v o l v e r un nú mero r a c i o n a l como c a d e n a
public S trin g to S t r in g O
I
return new S t r i n g t n u m e r a d o r + " / " + denominador):
// E s t a b l e c e r un nú me r o r a c i o n a l
p u b lic s t a t i c CRacional l e e r O
I
l o n g num. den;
int i . b a rra s:
boolean c a r á c t e r V á li d o :
String ra c io n a l;
do
I
b a r r a s = 0;
System .out.printC" [ - ] entero[/entero]: "):
r a c i o n a l = L e e r . d a t o O ; II l e e r e l r a c i o n a l
if ( r a c i o n a l . 1 e n g t h ( ) = = 0)
c a r á c t e r V á l i do = f a l s e :
el se
I
II E l p r i m e r c a r á c t e r p u e d e s e r un d i g i t o o e l s i g n o menos
c a r á c t e r V á l i do =
( r a c i o n a l . c h a r A t ( O ) > = ' 0 ' && r a c i o n a l . c h a r A t ( O ) < = ' 9 ' ) ||
( r a c i o n a l.charAt(O) == && r a c i o n a l . 1e n g t h ( ) > 1 ) :
II El ú l t i m o c a r á c t e r no p u e d e s e r una /
i f ( r a c i o n a l .c h a r A t t r a c i o n a l .1e n g t h ( )-1 ) = '/ ')
carácterVálido = false:
I
II El r e s t o de l o s c a r a c t e r e s pueden s e r d í g i t o s o / ( s ó l o una)
f o r ( i = 1; c a r á c t e r V á l i d o && i < r a c i o n a l . 1 e n g t h ( ) ; i + + )
I
carácterVálido = r a c i o n a l . c h a r A t ( i ) > = ' 0 ' &&
r a c i o n a l . c h a r A t ( i ) < = ' 9 ’ ||
racional.charA t(i) = '/ ':
if ( r a c i o n a l.c h a r A t í i ) == ' / ’ ) barras++:
if ( b a r r a s > 1) c a r á c t e r V á l i d o = f a l s e ;
1
if ( ¡carácterVálido) S y s t e m . o u t . p r i n t l n ( " E n t r a d a no v á l i d a . " ) :
I
w hile ( ¡ c a rá c te rV á lid o ):
// E x t r a e r e l n u m e r a d o r y e l d e n o m i n a d o r
i f ( ( i = r a c i o n a l . i n d e x O f ( ' / ' ) ) = = - 1 ) // no h a y d e n o m i n a d o r
I
num = L o n g . p a r s e L o n g f r a c i o n a l ) ;
den = 1:
318 JA V A: C U R SO DE PRO G R A M A CIÓ N
el se
I
num = L o n g . p a r s e L o n g ( r a c i o n a l . s u b s t r i n g ( 0 , i ) ) : // 0 a i - 1
den = L o n g . p a r s e L o n g ( r a c i o n a l . s u b s t r i n g ( i + 1 ) ) :
)
II C o n s t r u i r y d e v o l v e r e l o b j e t o C R a c i o n a l
r e t u r n new C R a c i o n a l ( n u m . d e n ) :
II C o p i a r un r a c i o n a l en o t r o
p u b lic CRacional co p ia r( CRacional r )
I
numerador = r.num erador;
denominador = r.den o m ina do r:
return th is :
// V e r i f i c a r s i e s 0
p u b lic boolean e s C e r o O
I
return numerador = 0;
// I n c r e m e n t a r en 1
p u b lic CRacional incre m entare)
I
numerador + = denominador:
return th is:
// D e c r e m e n t a r en 1
p u b lic CRacional d ecrem entar()
I
numerador -= denominador;
return th is;
// - u n a r i o
p u b lic CRacional cambiadoDeSigno()
I
C R a c i o n a l temp = new C R a c i o n a l ( -numerador, denominador );
r e t u r n temp;
CA PÍTU LO 9: C L A SES Y PAQUETES 3 1 9
EJERCICIOS RESUELTOS
Una m atriz m ultidim ensional en Java representa un conjunto de elem entos que
pueden ser accedidos m ediante variables suscritas o de subíndices. D ichos subín
dices son especificados utilizando uno o m ás corchetes: []. Por ejem plo:
d o u b l e [ ] [ ] [ ] m i M a t r i z D o u b l e - new d o u b l e C 5 ] [ 1 0 ] [ 4 ] :
i n t i . j . k . c o n t a = 1;
/ / ...
m i M a t r i z D o u b l e [ i ] [ j ] [ k ] = conta++;
U na construcción sim ilar puede realizarse utilizando una m atriz unidim ensio
nal y m anipularla com o si fuera una m atriz m ultidim ensional. P ara ello, definire
m os una clase C M atriz con los siguientes atributos:
La clase C M atriz tiene com o función representar una m atriz m ultidim ensio
nal. O bserve q ue el m iem bro m atriz sirve para referenciar una m atriz de una di
m ensión de elem entos de tipo d o u b le. que el m iem bro nD im s contiene el núm ero
de dim ensiones y dim sM atriz es una referencia a una m atriz que contendrá el va
lor de cada una de ellas.
final int A “ 5:
final int B - 10:
int i . j . c o n t a = 1;
CMatriz m - new C M a t r i z t A. B ): // m a t r i z d e 2 d i m e n s i o n e s (A*B)
// A s i g n a r d a t o s a l a m a t r i z m
f o r ( i - 0: i < A: i + + )
f o r ( j - 0: j < B: j + + )
m .asignarDato(conta++, i, j );
// V i s u a l i z a r l a m a t r i z m
f o r ( i = 0 : i < A: i + + )
I
f o r ( j = 0: j < B: j + + )
System .out.print(m .obtenerDato( i. j ) + " "):
S y s t e m . o u t . p r i n t l n t ):
320 JA V A : C U R SO DE PRO G R A M A CIÓ N
E n este ejem plo m representa una m atriz de dos dim ensiones. O bserve que pa
ra acceder a un elem ento utilizam os dos subíndices / y j . Pero com o la m atriz físi
cam ente es una m atriz de una dim ensión, la idea fundam ental es im plem entar un
m ecanism o que convierta una posición dada por 1. 2 ó 3 subíndices en la posición
equivalente de la m atriz unidim ensional. Por ejem plo, si los subíndices del ele
m ento al que deseam os acceder son i l , i2 c i3 y las dim ensiones de la m atriz m
son d i , d2 y d 3 , el desplazam iento se calcula así: ((H *d2)+ i2)*d3+ i3.
m atriz
d im s M a triz D im e n s io n e s A , B y C
nD im s N ú m e ro d e d im e n s io n e s
Según lo expuesto, la clase C M atriz estará form ada por los atributos privados
m encionados, por el m étodo privado.
void c o n s t r u i r ( i n t [] di m )
CM atrizí)
CMatrizí int di )
C M a t r i z í i n t d i . i n t d2 )
C M a t r i z t i n t d i . i n t d 2, i n t d3 )
int totalElem entosí)
in t desplazamiento! i n t f ] subind )
void a sig n a rD a to í in t dato, int il )
vo id a s ig n a rD a to ! i n t dato, int il. int i 2 )
void asignarD atot in t dato, i n t i l . i n t i 2. i n t i3 )
double obtenerDato! in t i l )
double obtenerDato! in t i l . int i 2 )
double obtenerDato! i n t i l . i n t i 2. i n t i 3 )
2. Escriba los constructores CM atriz. Sus parám etros se corresponden con los
valores de las dim ensiones de la m atriz. Estos m étodos invocan al m étodo
con stru ir para crear un objeto CM atriz.
void c o n s t r u i r ( i n t [ ] dim )
dim m atriz unidim ensional de enteros que contiene el valor de cada una
de las dim ensiones.
Por ejem plo, si n es 2, dim [0] y d i mf J ] tienen que ser valores m ayores que
cero y dim [2¡ no interviene. E ntonces el núm ero de elem entos de la m atriz se
ría dim fO ] * d im [ l] . E ste valor será calculado p o r el m étodo totalElem entos
que se expone en el apartado siguiente.
5. Escriba el m étodo totalE lem entos. E ste m étodo calcula el núm ero total de
elem entos de la m atriz de 1, 2 ó 3 dim ensiones.
El m étodo totalE lem entos retom a el núm ero total de elem entos de la matriz.
6. E scriba el m étodo desplazam iento. Este m étodo calcula la posición que tiene
dentro de la m atriz unidim ensional referenciada p o r m atriz, el elem ento que
está en la m atriz m ultidim ensional en la posición especificada por los subín
dices alm acenados en la m atriz referenciada p o r subind. Previam ente, verifica
sí los subíndices están dentro de los lím ites perm itidos.
7. Escriba el m étodo asignarD ato. E ste m étodo asigna un dato d al elem ento de
la m atriz m ultidim ensional, especificado por los subíndices i l , i2 e i3. asig
narD ato invoca al m étodo desplazam iento para calcular el desplazam iento.
8. Escriba el m étodo obtenerD ato. E ste m étodo obtiene un d ato del elem ento de
la m atriz m ultidim ensional, especificado p o r sus subíndices i l , i2 e i3. obte
nerD ato invoca al m étodo desplazam iento para calcular el desplazam iento.
El m étodo obtenerD ato retom a el valor alm acenado en el elem ento especifi
cado de la m atriz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 2 3 24
25 2 6 27 2 8 2 9 30 31 3 2 3 3 34 35 3 6 3 7 38 39 4 0 41 4 2 43 44 45
4 6 4 7 4 8 4 9 50
10. ¿P or qué es necesario sobrecargar los m étodos asignarD ato y obtenerD ato?
C M a t r i z m( A . B ):
m.obtenerDato! i . j );
//////////////////////////////////////////////////////////////////
// M a t r i z m u l t i d i m e n s i o n a l b a s a d a en una u n i d i m e n s i o n a l
//
public class CMatriz
I
private doublef] m atriz; // m a t r i z u n i d i m e n s i o n a l
private i n t nDims; // nú me r o de d i m e n s i o n e s
private i n t [ ] dim sM atriz; // v a l o r d e c a d a d i m e n s i ó n
C A PÍTU LO 9: CLASES Y PA Q U ETES 3 2 3
public CM atrizO II c o n s t r u c t o r
I
i n t d i m [ ] = I 10 I : // d i m e n s i ó n por omisión
c o n s t r u i r í di m );
//////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
// A p l i c a c i ó n para t r a b a j a r con C M a t r i z
II
public class Test
I
public static void m a in (S trin g [] args)
I
f i n a l i n t A = 5:
f i n a l i n t B = 10:
i n t i . j . c o n t a = 1:
C M a t r i z m = new C M a t r i z t A. B ); // m a t r i z d e 2 d i m e n s i o n e s
// A s i g n a r d a t o s a l a m a t r i z m
f o r ( i = 0: i < A: i + + )
for ( j = 0: j < B: j + + )
m .asignarDato(conta++. i. j ):
// V i s u a l i z a r l a m a t r i z m
f o r ( i - 0 : i < A; i + + )
I
f o r ( j = 0 : j < B: j + + )
System .out.print(m .obtenerDato( i . j ) + " " ) :
S y s t e m . o u t . p r i n t l n í );
R espuesta a la pregunta 10. Los m étodos asignarD ato y obtenerD ato están
sobrecargados para poder utilizar sus form as adecuadas según se trate de una m a
triz de 1. 2 ó 3 dim ensiones.
p u b l i c C M a t r i z t i n t d i . i n t d2 ) // c o n s t r u c t o r
p r í v a t e v o i d c o n s t r u i r ( i n t [ ] dim )
pu b lic int total Elem entos!)
EJERCICIOS PROPUESTOS
1. Suponiendo un texto escrito en m inúsculas y sin signos de puntuación (una
palabra estará separada de otra por un espacio en blanco), realizar un p ro
gram a que lea texto de la entrada estándar (del teclado) y dé com o resultado
la frecuencia con que aparece cada palabra leída del texto.
El resultado se alm acenará en un m atriz en la que cada elem ento será un objeto
C Palabra con los atributos:
S tr in g palabra; // p a l a b r a
int contador; // número de v e c e s q u e a p a r e c e en e l texto
2. E scribir una clase C om plejo para trabajar con núm eros com plejos.
¿Q ué es un núm ero com plejo? U n núm ero com plejo está com puesto por dos nú
m eros reales y se representa de la form a a+ bi\ a recibe el nom bre de com ponente
real y b el de com ponente im aginaria. Si b = 0, se obtiene el núm ero real a, lo que
quiere decir que los núm eros reales son un caso particular de los núm eros com
plejos.
Los núm eros com plejos cubren un cam po que no tiene sentido en el cam po de los
núm eros reales. Por ejem plo, no existe ningún núm ero real que sea igual a V -9 .
T am poco tienen sentido las expresiones (-2 )3 /2 o lo g (-2 ). Para resolver este tipo
CA PÍTU LO 9: CLASES Y PAQUETES 3 2 7
Puesto que un núm ero com plejo (a, b) es un par ordenado de núm eros reales,
puede representarse geom étricam ente m ediante un punto en el plano; dicho de
o tra forma, m ediante un vector. De aquí se deduce que: a + b i, núm ero com plejo
en form a binóm ica, es equivalente a m (cos a + i sen a ), núm ero com plejo en
form a polar, lo que indica que a = m eo s a y que b = m sen a.
El núm ero positivo m = -Ja~ + b~ se denom ina m ódulo o valor absoluto y el án
g ulo a = a re Ig(b/a) recibe el nom bre de argum ento.
Suma: ( a . b )+ ( c , d ) =(a+c.b+d)
D iferencia: ( a . b ) - ( c . d ) — ( a - c . b - d )
Producto: <a.b)*(c.d)=(ac-bd.ad+bc)
C ociente: ( a , b ) / ( c , d )=( ( a c + b d ) / ( c 2 +d 2 ) , ( be - a d ) / ( c 2 + d 2 ) )
Estas operaciones y otras form arán parte de la interfaz de la clase C om plejo. Las
com paraciones entre com plejos estarán referidas a sus m ódulos.
Según la definición dada, podem os representar un com plejo com o un objeto que
tenga d o s atributos, uno para alm acenar la parte real y otra para la parte im agina
ria.
C ada térm ino del polinom io será representado por una clase C Term ino y cada po
linom io por una clase CPolinom io.
La clase C Term ino tendrá dos atributos privados: coeficiente y exponente, y los
m étodos necesarios para perm itir al m enos:
La clase C P olinom io tendrá dos datos m iem bro privados: núm ero de térm inos que
tiene el polinom io (nroTerm inos) y una m atriz que referenciará los térm inos del
polinom io (term ino), así com o los m étodos necesarios para perm itir al menos:
SUBCLASES EINTERFACES
Las características fundam entales de la POO son abstracción, encapsulam iento,
herencia y p olim orfism o. H asta ahora sólo hem os abordado la abstracción y la
encapsulación.
E ntre las características enum eradas anteriorm ente, hay una que destaca: la
herencia. L a herencia provee el m ecanism o m ás sim ple para especificar una for
ma alternativa de acceso a una clase existente, o bien para definir una nueva clase
que añada nuevas características a una clase existente. Esta nueva clase se deno
m ina subclase o clase derivada y la clase existente, superclase o clase base.
C la s e C C u e n ta
X
)
C la s e C C u e n ta C o rrie n te
)
C la s e C C u e n ta A h o rro
j
U na jerarq u ía de clases m uestra cóm o los objetos se derivan de otros objetos
m ás sim ples heredando su com portam iento. Los usuarios de C++ y de otros len
guajes de program ación orientada a objetos están acostum brados a ver jerarquías
de clases para describir la herencia. Los de Java seguirán, en general, los m ism os
pasos.
330 JA V A : C U R S O D E PROGRAM A CIÓN
C uando una clase se diseña para ser genérica, es casi seguro que no necesita
rem os crear objetos de ella; la razón de su existencia es proporcionar los atributos
y com portam ientos que serán com partidos por todas sus subclases. U na clase que
se com porte de la form a descrita se denom ina clase abstracta y se define com o tal
calificándola explícitam ente abstracta (a b s tra c t). Por ejem plo:
U na clase abstracta puede contener el m ism o tipo de m iem bros que una clase
que no lo sea, y adem ás pueden contener m étodos abstractos, que una clase no
abstracta no puede contener.
A la vista de este ejem plo, puede intentar declarar la clase C Cuenta no abs
tracta y com probará que el com pilador Java le m uestra un m ensaje indicándole
que una clase sólo puede contener m étodos abstractos si es abstracta.
CA PÍTU LO 10: SUBC LA SES E INTERFACES 3 3 1
SUBCLASES Y HERENCIA
V uelva a echar una ojeada a la figura m ostrada al principio de este capítulo. Se
trata de u na jerarq u ía de clases que puede ser analizada desde dos puntos de vista:
2. C uando se abordó el diseño de una aplicación para adm inistrar las cuentas de
una entidad bancaria, la solución fue diseñ ar una clase especializada para cada
una de las cuentas y agrupar el código com ún en una superclase de éstas.
A trib u to S ig n ificad o
nom bre D alo de tipo S tr in g que alm acena el nom bre del pro
pietario de la cuenta.
cuenta D ato de tipo S tr in g que alm acena el núm ero de la
cuenta.
saldo D ato de tipo d o u b le que alm acena el saldo de la cuenta.
tipoD elnterés D ato de la clase de tipo d o u b le que alm acena el tipo de
interés.
332 JA V A: C U R SO DE PROGRAM A CIÓN
M éto d o S ig n ificad o
C Cuenta E s el constructor de la clase. Inicia los datos nom bre,
cuenta, saldo y tipoD elnterés.
asignarN om bre Perm ite asignar el dato nom bre.
obtenerN om bre R etorna el dato nom bre.
asignarC uenta Perm ite asignar el dato cuenta.
obtenerC uenta R etorna el dato cuenta.
estado R etorna el saldo de la cuenta.
com isiones Es un m étodo abstracto sin parám etros que será redefi-
nido en las subclases. Se ejecutará los días uno de cada
m es para cobrar el im porte del m antenim iento de una
cuenta.
ingreso Es un m étodo que tiene un parám etro cantidad de tipo
d o u b le que añade la cantidad especificada al saldo ac
tual de la cuenta.
reintegro Es un m étodo que tiene un parám etro cantidad de tipo
d o u b le que resta la cantidad especificada del saldo ac
tual de la cuenta.
a signarTipoD einterés M étodo que perm ite asignar el dato tipoD elnterés.
obtenerT ipoD elnterés M étodo que retom a el dato tipoD elnterés.
intereses M étodo abstracto. C alcula los intereses producidos.
//////////////////////////////////////////////////////////////////
// C l a s e C C u e n t a : c l a s e a b s t r a c t a q u e a g r u p a los datos c o mu n e s a
// c u a l q u i e r t i p o de c u e n t a b a n c a r i a .
//
public abstract class CCuenta
I
// A t r i b u t o s
private String nombr e:
private String cuenta:
p ri vate double saldo:
p r iv a t e double tipoDelnterés:
// M é t o d o s
publi c CCuenta( ) I I ;
public CCuenta(String nom, String cue, double sal. double tipo)
I
asignarNombret nom):
asignarCuenta(cue):
i n g r e s o í s a l );
asignarTipoDelnterés!tipo):
CA PÍTU LO 10: SUBC LA SES E IN TERFA CES 3 3 3
p u b lic double e s t a d o ! )
I
return saldo:
)
p u b lic double o b te n e r T ip o D e ln t e ré s t)
I
return tipoDelnterés;
I
U na form a de hacer esto sería definir una nueva clase C C uentaAhorro con los
atributos y m étodos de C Cuenta, a los que añadiríam os los nuevos atributos y
m étodos, según m uestra el esquem a siguiente:
C A P ÍT U L O 10: SU B C LA SE S E IN TERFA CES 3 3 5
E sta form a de proceder puede que funcione, pero no deja d e ser una m ala so
lución; adem ás de suponer un derroche de tiem po y esfuerzo, todo el trabajo que
ya estaba realizado no ha servido para nada. A quí es donde la herencia ju eg a un
papel im portante; la utilización de esta característica evitará que recurram os a
soluciones com o la planteada. A través de la herencia. Java perm ite definir la cla
se C C uentaA horro com o una extensión d e CCuenta. Y esto ¿cóm o se hace? D efi
niendo una subclase de la clase existente.
El ejem plo m ostrado a continuación define la clase C C uentaA horro com o una
extensión de CCuenta.
U na subclase puede serlo de una sola superclase, lo que se denom ina herencia
sim ple o derivación sim ple. Java, a diferencia de otros lenguajes orientados a o b
jeto s, no perm ite la herencia m últiple o derivación m últiple, esto es, que una sub
clase se derive de dos o m ás clases.
336 JA V A : C U R SO DE PRO G R A M A CIÓ N
Una subclase puede, a su vez, ser una superclase de otra clase, dando lugar así
a una jera rq u ía de clases. P or lo tanto, una clase puede ser una superclase directa
de una subclase, si figura explícitam ente en la definición de la subclase, o una su
perclase indirecta si está varios niveles arriba en la jerarq u ía de clases, y por lo
tanto no figura explícitam ente en el encabezado de la subclase.
1. U na subclase hereda todos los m iem bros de su superclase, excepto los cons
tructores, lo que no significa que tenga acceso directo a todos los miembros.
U na consecuencia inm ediata de esto es que la estructura interna de datos de
un objeto de una subclase, estará form ada por los atributos que ella define y
p o r los heredados de su superclase.
U na subclase sí puede acceder directam ente a los m iem bros públicos (public)
y protegidos (p ro te c te d ) de su superclase; y en el caso de que pertenezca al
m ism o paquete de su superclase, tam bién puede acceder a los m iem bros pre
determ inados.
3. Los m iem bros heredados por una subclase pueden, a su vez, ser heredados
por m ás subclases de ella. A esto se le llam a propagación de herencia.
C ontinuando con el ejem plo, diseñem os una nueva clase C C uentaA horro que
tenga, adem ás de las m ism as capacidades de C Cuenta, las siguientes:
A trib u to S ig n ificad o
cuotaM an ten i m i en to D ato de tipo d o u b le que alm acena la com isión que cobrará
la entidad bancaria p o r el m antenim iento de la cuenta.
M éto d o S ig n ificad o
C C uentaAhorro Es el constructor de la clase. Inicia los atributos de la m is
ma.
asignarC uotaM anten Establece la cuota de m antenim iento de la cuenta.
obtenerC uotaM anten D evuelve la cuota de m antenim iento d e la cuenta.
com isiones M étodo que se ejecuta los días uno de cada m es para cobrar
el im porte correspondiente al m antenim iento de la cuenta.
intereses M étodo que perm ite calcular el im porte correspondiente a
los intereses/m es producidos.
import j a v a . u t 1 1 . * :
//////////////////////////////////////////////////////////////////
// C l a s e C C u e n t a A h o r r o : c la s e derivada de CCuenta
//
public class CCuentaAhorro extends CCuenta
{
// A t r i b u t o s
p r iv a t e double cuotaMantenimiento;
338 JA V A: C U R SO DE PROGRAM A CIÓN
// M é t o d o s
public CCuentaAhorro() II // c o n s t r u c t o r sin parámetros
i f ( d í a ! = 1) r e t u r n 0 . 0 :
// A c u m u l a r l o s i n t e r e s e s p o r mes s ó l o l o s d í a s 1 de c a d a mes
double in te re s e s P ro d u c id o s = 0.0;
i n t e r e s e s P r o d u c i d o s = e s t a d o ! ) * o b t e n e r T i p o ü e l n t e r é s t ) / 1200.0;
ingreso!interesesProducidos):
A tributos M étodos
A tributos M étodos
intereses
ingreso
reintegro
asignarTipoD elnterés
obtenerT ipoD elnterés
cuotaM antenim icnto constructores C C uentaA horro
asignarC uotaM anten
obtenerC uotaM anten
com isiones
intereses
Escribam os ahora una pequeña aplicación basada en una clase Test que cree
un objeto C CuentaAhorro:
public c la ss Test
I
public s t a t ic void m a in (S trin g [] args)
1
C C u e n t a A h o r r o c l i e n t e O l = new C C u e n t a A h o r r o C );
e l i e n t e O l . a s i g n a r N o m b r e ( " U n n o m b r e " ):
e l i e n t e O l . a s i g n a r C u e n t a f " U n a c u e n t a " );
e l i e n t e O l .a s i g n a r T i p o D e I n t e r é s ( 2 . 5 ) :
elienteOl.asignarCuotaM anten(300);
e l i e n t e O l . in g r e s o (1000000):
e l i e n t e O l . re i n t e g r o C 5 0 0 0 0 0):
e l i e n t e O l . c o m i s i o n e s ( ):
// c l i e n t e O l no p u e d e a c c e d e r a l o s miembros p r i v a d o s , como
// c u e n t a .
class ClaseA
I
public int atributo_x = 1:
public i n t método_x()
(
return atributo_x * 10:
1
public in t método_y()
I
return a t r i b u t o _ x + 10 0 :
public c la ss Test
I
public static void m a in (S trin g [] args)
(
C l a s e B o b j C l a s e B = new C l a s e B O :
S y s t e m . o u t . p r i n t l n ( o b j C l a s e B . a t r i b u t o _ x ) : // e s c r i b e 2
S y s t e m . o u t . p r i n t l n ( o b j C l a s e B . m é t o d o _ y ( ) ) : // e s c r i b e 101
S y s t e m . o u t . p r i n t l n t o b j C l a s e B . m é t o d o _ x ( ) ) ; // e s c r i b e - 2 0
public in t método_x()
I
return su p e r.atribu to_x * -10;
I
super.atributo_x
th is.atribu to_x
( ( C1a s e A ) t h i s ) . a t r i buto_x
Sin em bargo, puede haber ocasiones en que deseem os que un objeto de una
subclase responda al m ism o m étodo heredado de su superclase pero con un com
portam iento diferente. Esto im plica redefinir en la subclase el m étodo heredado de
su superclase.
Se puede observar que este m étodo ha sido redefinido en la C laseB para que
realice unos cálculos diferentes a los que realizaba en la C laseA.
En el caso de que el m étodo heredado por la subclase sea abstracto, com o su
cede en nuestro ejem plo acerca de las cuentas bancarias, es obligatorio redefinir-
lo, de lo contrario la subclase debería ser declarada tam bién abstracta.
344 JA V A : C U R SO DE PRO G R A M A CIÓ N
super,método_x()
thi s ,m é to d o _ x ()
En cam bio, una expresión com o la siguiente es válida para el com pilador, pe
ro, de acuerdo con lo que aprendió en el apartado anterior, no producirá los re
sultados que quizá usted esperaba.
// M é t o d o de l a C 1 a s e B
publi c in t m étodo_z()
I
II...
return ( ( C 1 a s e A ) t h is ) .método_x() + atribu to_x;
C A P ÍT U L O 10: SUBC LA SES E IN TERFA CES 3 4 5
Lo anteriorm ente expuesto se traduce en que prim ero se ejecutan los cons
tructores de las superclases de arriba a abajo en la jera rq u ía de clases y finalm ente
el de la subclase. Esto sucede así, porque una subclase contiene todos los atributos
de su superclase, y todos tienen que ser iniciados, razón p o r la que el constructor
de la subclase tiene que llam ar im plícita o explícitam ente al de la superclase.
Sin em bargo, cuando se hayan definido constructores con parám etros tanto en
las subclases com o en las superclases, tal vez se desee construir un objeto de la
subclase iniciándolo con unos valores determ inados. En este caso, la definición ya
conocida p ara los constructores de una clase cualquiera se extiende ahora para
perm itir al constructor de la subclase invocar explícitam ente al constructor de la
superclase. E sto se hace utilizando la palabra reservada su p e r:
Se puede observar que la sintaxis y los requerim ientos son análogos a los uti
lizados con th is cuando se llam a a otro constructor de la m ism a clase.
346 JA VA: C U R SO DE PROGRAM A CIÓN
p u b l i c C C u e n t a A h o r r o ( S t r i n g nom, S t r i n g c u e , d o u b l e sal,
d o u b l e t i p o , d o u b l e m a nt )
I
supertnom, cue, s a l , t i p o ) ; // i n v o c a al c o n s t r u c t o r C C u e n t a
asignarCuotaManten(m ant); // i n i c i a c u o t a M a n t e n i m i e n t o
I
S egún lo expuesto, cuando se crea un objeto de una subclase, por ejem plo
clienteO l o cliente02, prim ero se construye la porción del objeto correspondiente
a su superclase y a continuación la porción del objeto correspondiente a su sub
clase. Esto es u na form a lógica de operar, ya que perm ite al constructor de la sub
clase h acer referencia a los atributos de su superclase que ya han sido iniciados.
Según lo expuesto, los objetos de una subclase son construidos de abajo hacia
arriba; esto es, la pila de llam adas relativas a los constructores de las clases invo
lucradas crece hasta llegar a la clase raíz en la jerarq u ía de clases; en este instante,
com ienza a ejecutarse el constructor de esta superclase: prim ero se construyen sus
atributos ejecutando, cuando sea necesario, los constructores de los m ism os, y
después, se pasa a ejecutar el cuerpo del constructor de dicha superclase; y a con
tinuación se ejecuta el cuerpo del constructor de la subclase. Este orden se aplica
recursivam ente por cada constructor de cada una de las clases.
P o r ejem plo, si definim os en una subclase un m étodo finalize para liberar los
recursos asignados por dicha clase, debem os rede finir el m étodo finalize en la su
perclase para liberar tam bién los recursos asignados por ella. Pero si el m étodo fi
nalize de la subclase no invoca explícitam ente al m étodo finalize de la superclase,
este últim o nunca será ejecutado y los recursos asignados por la superclase no se
rán liberados ¿C uándo debem os invocar al m étodo finalize de la superclase? El
m ejor lugar para hacerlo es en la últim a línea del m étodo finalize de la subclase,
porque com o la parte del objeto de la subclase se ha construido una vez que esta
ba construida la parte del objeto de la superclase, en m ás de una ocasión los vín
culos existentes entre una y otra parte exigirán deshacer lo construido, ju sto en el
orden inverso.
Para ver prácticam ente la form a de im plem entar los destructores, volvam os al
ejem plo anteriorm ente expuesto con la ClaseA y la C laseB, y añadam os un des
tructor a cada una de ellas que supuestam ente hace algo.
348 JA V A: C U R SO DE PRO G R A M A CIÓ N
class ClaseA
(
public int a t r i b u t o _ x = 1:
public i n t método_x()
I
return atributo_x * 10:
1
public i n t método_y()
(
return a t r i b u t o _ x + 10 0 :
1
public c la ss Test
I
public static void m a in (S trin g [] args)
I
C l a s e B o b j C l a s e B = new C l a s e B ! ) :
/ / ...
objClaseB = nuil:
C A PÍTU LO 10: SUBC LA SES E IN TERFA CES 3 4 9
// E j e c u t a r e l r e c o l e c t o r de b a s u r a
R unti rne r u n t i m e = R u n t i m e . g e t R u n t i m e t );
run t i m e . g e ( ) ;
r u n t i m e . r u n F i n a l i z a t i o n í ):
Puesto que Java proporciona para cada clase que definam os un m étodo fin a li
ce heredado de la clase O b je c t, una program ación segura aconseja redefinir este
m étodo en cada una de las subclases que escribam os, aunque no haga nada; sim
plem ente con la intención de invocar al m étodo fín alize de la superclase, p o r si
alguna versión futura de la m ism a incluye un m étodo finalize.
JERARQUIA DE CLASES
U na subclase puede asim ism o ser una superclase de otra clase, y a sí sucesiva
m ente. En la siguiente figura se puede ver esto con claridad:
C la s e O b je c t
J
^ C la s ^ C u e n t^ ^ ^ J
C la s e C C u e n ta C o rrie n te C o n ln
J
350 JA V A: CU R SO D E PROGRAM A CIÓN
La raíz del árbol es la clase que representa el tipo m ás general, y las clases
term inales en el árbol (nodos hoja) representan los tipos m ás especializados.
Com o ejem plo, vam os a com pletar la jerarq u ía de clases expuesta con las cla
ses que faltan: C C uentaC orriente y C C uentaC orrienteC onln.
La clase C C uentaC orriente es una nueva clase que hereda de la clase CCuen
ta. Por lo tanto, tendrá todos los m iem bros de su superclase, a los que añadiremos
los siguientes:
A trib u to S ig n ificad o
transacciones D ato de tipo in t que alm acena el núm ero de transac
ciones efectuadas sobre esa cuenta.
im porte PorTrans D ato de tipo d o u b le que alm acena el im porte que la
entidad bancaria cobrará por cada transacción.
transE xentas D ato de tipo in t que alm acena el núm ero de transac
ciones gratuitas.
M éto d o S ig n ificad o
C C uentaC orriente Es el constructor de la clase. Inicia los atributos de la
m ism a.
deerem entarTransacciones D ecrem enta en 1 el núm ero de transacciones.
asignarlm porteP orTrans Establece el im porte p o r transacción.
obtenerlm porteP orT rans D evuelve el im porte por transacción.
asignarTransE xentas Establece el núm ero de transacciones exentas.
obtenerTransE xentas D evuelve el núm ero de transacciones exentas.
ingreso A ñade la cantidad especificada al saldo actual de la
cuenta e increm enta el núm ero de transacciones.
CA PÍTU LO 10: SU B C LA SE S E IN TERFA CES 3 5 1
import j a v a .ú t i l .*;
//////////////////////////////////////////////////////////////////
// C l a s e C C u e n t a C o r r i e n t e : c la se derivada de C C u e n t a
//
public class C C u e n t a C o r r i e n t e e x t e n d s CCuenta
I
// A t r i b u t o s
private int transacciones:
p riv a t e double im p o rte P o rT ra n s;
private int transExentas;
I I Métodos
pu blic CCuentaCorriente*) II // c o n s t r u c t o r sin parámetros
p u b l i c C C u e n t a C o r r i e n t e * S t r i n g nom. S t r i n g c u e , d o u b l e s a l .
double tip o , double imptrans, in t tra nsex)
I
s u p e r ( n o m , c u e . s a l , t i p o ) ; // i n v o c a al c o n s t r u c t o r C C u e n t a
t r a n s a c c i o n e s = 0; // i n i c i a t r a n s a c c i o n e s
a s i g n a r l m p o r t e P o r T r a n s ( i m p t r a n s ) ; // i n i c i a i m p o r t e P o r T r a n s
asignarTransExentas( tra n se x ); // i n i c i a t r a n s E x e n t a s
public vo id d e c re m e n ta rT ra n sa c c io n e s()
I
t r a n s a c c i o n e s - -;
return:
1
importePorTrans = imptrans;
// A c u m u l a r l o s i n t e r e s e s p o r mes s ó l o l o s d i a s 1 de c a d a mes
double in te re s e s P ro d u c id o s - 0.0:
// H a s t a 3 0 0 0 e u r o s al 0 . 5 % . El r e s t o al i n t e r é s e s t a b l e c i d o ,
i f ( e s t a d o ! ) < - 3000)
in t e r e s e s P r o d u c id o s = e s t a d o ! ) * 0.5 / 1200.0;
else
I
i n t e r e s e s P r o d u c i d o s = 3000 * 0 . 5 / 1 2 0 0 .0 +
( e s t a d o ! ) - 3000) * o b t e n e r T i p o D e l n t e r é s ! ) / 1200.0:
I
i n g r e s o ! i n t e r e s e s P r o d u c i d o s );
// E s t e i n g r e s o no d e b e i n c r e m e n t a r l a s transacciones
d e c r e m e n t a r T r a n s a c c i o n e s ! );
return interesesProducidos;
I
I
//////////////////////////////////////////////////////////////////
O bserve que el constructor de la clase C C uentaC orriente tiene los parám etros
necesarios para iniciar sus datos m iem bro, excepto transacciones que inicialm ente
vale 0 . y los heredados de su superclase. El cuerpo del constructor consta de la
llam ada al constructor de su superclase y de las llam adas a los m étodos de la pro
pia clase que perm iten iniciar de form a segura los atributos de la m ism a. Tam bién
se ha im plem entado un constructor sin parám etros.
P rocediendo de form a sim ilar a com o lo hem os hecho para las clases C C uen
taA horro y C C uentaC orriente. construim os a continuación la clase C CuentaCo-
rrienteC onln (cuenta corriente con intereses) derivada de CC uentaC orriente.
Supongam os que este tipo de cuenta se ha pensado para que acum ule intere
ses de form a distinta a los otros tipos de cuenta, pero para obtener una rentabili
dad m ayor respecto a C C uentaC orriente.
D igam os que se trata de una cuenta de tipo C C uentaC orriente que precisa un
saldo m ínim o de 3000 euros para que pueda acum ular intereses. Según esto,
C C uentaC orrienteC onln, adem ás de los m iem bros heredados, sólo precisa im-
piem entar sus constructores y variar el m étodo intereses:
354 JA V A : CU R SO DE PRO G R A M A CIÓ N
M éto d o S ig n ificad o
C C uentaC orriente E s el constructor de la clase. Inicia los atributos de la mis
ma.
intereses Perm ite calcular el im porte/m es correspondiente a los inte
reses producidos. Precisa un saldo m ínim o de 3000 euros.
import j a v a . ú t i l .*;
//////////////////////////////////////////////////////////////////
// C l a s e C C u e n t a C o r r i e n t e C o n l n : c l a s e d e r i v a d a de C C u e n t a C o r r i e n t e
//
public c la ss CCuentaCorrienteConln extends CCuentaCorriente
(
// M é t o d o s
p u b lic CCuentaCorrienteConln!) (1 // c o n s t r u c t o r sin parámetros
// A c u m u l a r i n t e r é s m e n s u a l s ó l o l o s d i a s I de c a d a mes
double in te re s e s P ro d u c id o s = 0.0:
i n t e r e s e s P r o d u c i d o s = e s t a d o ! ) * o b t e n e r T i p o O e l n t e r é s ! ) / 1200.0:
i n g r e s o ! i n t e r e s e s P r o d u c i d o s ):
// E s t e i n g r e s o n o d e b e i n c r e m e n t a r l a s t r a n s a c c i o n e s
d e c r e m e n t a r T r a n s a c c i o n e s ! );
//////////////////////////////////////////////////////////////////
directa, puede tener varias superclases indirectas: todas las que haya en el cam ino
para llegar desde su superclase hasta la clase raíz; esto es im portante porque lo
que una subclase hereda de su superclase, será heredado a su vez p o r una subclase
de ella, y así sucesivam ente.
i n g r e s o ! i n t e r e s e s P r o d u c i d o s );
d e c r e m e n t a r T r a n s a c c i o n e s ! );
s u p e r . i n g r e s o ! i n t e r e s e s P r o d u c i d o s );
S y s t e m . o u t . p r i n t l n ( el i e n t e O l . o b te n e rN o m b re !));
Sy ste m .o u t.p rin tín (e l i e n teO l.obtenerCuenta() );
System .out.printín(el ienteO l.estado!)):
S y s t e m . o u t .p r in t ín ( e lie n t e O l.obtenerTi p o D e In te ré s!));
System .out.printín(el ie n te O l.in te re se s!)):
356 JA V A: C U R SO DE PRO G R A M A CIÓ N
C C u e n t a C o r r i e n t e C o n l n e l i e n t e 0 2 = new C C u e n t a C o r r i e n t e C o n I n ( ) ;
e l i e n t e 0 2 . a s i gn arN o m bre("el i ente 0 2 " ) ;
e l i e n t e 0 2 . a s i g n a r C u e n t a C 1 2 3 4 5 6 7 8 9 0 ” );
e lie n te 0 2 .asig n a rT ip o D e In te ré s(3.0):
elien te02.asignarT ransExentas(0):
el i e n t e 0 2 . a s i g n a r l m p o r t e P o r T r a n s ( l . O ) :
e lie n te 0 2 . in g r e s o (20000);
el i e n t e 0 2 . r e i n t e g r o ( 1 0 0 0 0 ) :
e l i e n t e 0 2 . i n t e r e s e s ( ):
e l i e n t e 0 2 . c o m i s i o n e s ( );
System .out.p rin tln íclie nte02.obtenerN om bre()):
Syste m .out.p rin tln (eliente02.ob ten erC ue nta()):
S y ste m .o u t .p rin tln íc lie n t e 0 2 .estado());
Finalm ente, indicar que aunque en ninguna clase de nuestra jerarquía han in
tervenido m iem bros sta tic , su com portam iento en cuanto a la herencia se refiere
es el m ism o que el de los otros m iem bros, pero teniendo presente que son m iem
bros de la clase; y si es necesario, cuando se trate de m étodos, tam bién pueden ser
redefinidos, aunque, en este caso, el nom bre de la clase indicará la versión del
m étodo que se invocará. U na advertencia, si definiera, por ejem plo, en C Cuenta el
atributo tipoD elnterés sta tic , lógicam ente se m antendría una única copia que uti
lizarían tanto los objetos de C C uenta com o los de sus subclases.
E ste ejem plo declara una variable clienteO I de la subclase C C uentaC orriente
de CCuenta. D espués crea un objeto de e sa subclase y alm acena su referencia en
la variable clienteO I. U na vez que disponem os de la referencia a un objeto pode
m os trabajar con él com o lo hem os venido haciendo hasta ahora. P or ejem plo:
S t r i n g c u e n t a = el i e n t e O l . o b t e n e r C u e n t a ( ) :
d o u b l e s a l d o = e l i e n t e O l . e s t a d o í );
Conversiones implícitas
El ejem plo anterior no aporta nada que nos sorprenda; operaciones com o ésas ya
han sido expuestas anteriorm ente. Pero, qué pasaría si a la variable clienteO I le
asignam os la referencia a un objeto de la subclase C C uentaC orrienteC onln de
C C uentaC orriente. P or ejem plo:
Si ejecutam os este ejem plo, com probarem os que los resultados obtenidos son
los m ism os que obtuvim os con el ejem plo anterior. Esto es así porque Java per
m ite convertir im plícitam ente una referencia a un objeto de una subclase en una
referencia a su superclase directa o indirecta. V eam os o tro ejem plo:
358 JA VA: C U R SO DE PROGRAM A CIÓN
C Cu e n t a c l i e n t e :
CCuentaCorriente clie n te O l =
new C C u e n t a C o r r i e n t e * " e l i e n t e O l " , " 1 2 3 4 5 6 7 8 9 1 " ,
10000. 3 . 5 . 1 . 0 . 6 ) :
CCuentaCorrienteConln c lie n te 0 2 =
new C C u e n t a C o r r i e n t e C o n l n * " e l i e n t e 0 2 " , " 1 2 3 4 5 6 7 8 9 2 ” .
El ejem plo anterior declara una referencia cliente de la clase C C uenta, la cual
utilizam os después para referenciar indistintam ente a un objeto clienteO l de la
clase C C uentaC orriente, o a un objeto cliente02 de la clase C C uentaC orriente
C onln.
C uando accedem os a un objeto p o r m edio de una variable no del tipo del ob
jeto, sino del tipo de alguna de sus superclases (directa o indirectas) según m ues
tra el ejem plo anterior, es el tipo de la variable el que determ ina qué m ensajes
puede recibir el objeto referenciado; dicho de otra form a, es este tipo el que de
term ina qué m étodos pueden ser invocados por el objeto referenciado. ¿C uáles
son esos m étodos? Pues los correspondientes al tipo de la variable que utilizam os
para hacer referencia al objeto, no los de la clase del objeto.
CCuenta e l i e n t e ;
CCuentaCorriente c lie n te O l =
new C C u e n t a C o r r i e n t e * " e l i e n t e O l " . " 1 2 3 4 5 6 7 8 9 1 " ,
10000, 3 . 5 , 1 . 0 . 6);
CCuentaCorrienteConln c lie n te 0 2 =
new C C u e n t a C o r r i e n t e C o n l n * " e l i e n t e 0 2 ” . " 1 2 3 4 5 6 7 8 9 2 " .
2 0 0 0 0 , 2 . 0 . 1 . 0 . 6 ):
eliente = e lie n te O l:
el i e n t e . a s i g n a r Im p o r te P o r T r a n s ( 1 . 0 ) : // er r o r : no es un método de CCuenta
// . . .
el i ente = el i e n t e 0 2 :
c l i e n t e . a s i g n a r T r a n s E x e n t a s ( l O ) : // er r o r : no es un método de CCuenta
77 . . .
CA PÍTU LO 10: SUBC LA SES E IN TERFA CES 3 5 9
Este últim o ejem plo define las m ism as referencias y objetos que el anterior.
Pero ahora observam os que un intento de acceder al m étodo asignarlm porteP or-
Trans ocasiona un error. Esto es porque el tipo de la variable cliente, que es
C Cuenta, determ ina que el objeto referenciado por ella sólo puede recibir m ensa
je s de la clase de dicha variable; dicho de otra form a, sólo puede ser m anipulado
p o r m étodos de la clase C C uenta (propias y heredadas). L o m ism o diríam os res
pecto al m ensaje a signarTransE xentas enviado al objeto inicialm ente referencia-
do p o r cliente02 y finalm ente, tam bién p o r cliente.
CCuenta cliente:
CCuentaCorriente c lie n te O l =
new C C u e n t a C o r r i e n t e ! " e l i e n t e O l " , " 1 2 3 4 5 6 7 8 9 1 " ,
10000, 3 . 5 , 1.0, 6):
CCuentaCorrienteConln c lie n te 0 2 =
new C C u e n t a C o r r i e n t e C o n l n ! " e l i e n t e 0 2 " , " 1 2 3 4 5 6 7 8 9 2 " .
20000. 2 . 0 . 1 .0 . 6 ):
double in t e r e s e s :
cliente = elien teO l:
i n t e r e s e s = e l i e n t e . i n t e r e s e s ! ) ; // C C u e n t a C o r r i e n t e . i n t e r e s e s ! )
// ...
Este ejem plo declara cliente de la clase C Cuenta, la cual utilizam os después
para referenciar indistintam ente a un objeto clienteO l de la clase C C uentaC o
rriente, o a un objeto cliente02 de la clase C C uentaC orrienteC onln. Por otra par
te, el m étodo intereses está definido en la superclase C C uenta y redefinido en sus
subclases C C uentaC orriente y C C uentaC orrienteC onln. P or lo tanto, la expresión
cliente.intereses!) invocará a C C uentaC orriente.intereses!) si cliente señala a un
objeto C C uentaC orriente, e invocará a C C uentaC orrienteC onln.intereses!) si
cliente señala a un objeto C C uentaC orrienteC onln.
Conversiones explícitas
La conversión contraria, de una referencia a un objeto de la superclase a una refe
rencia a su subclase, no se puede hacer, aunque se fuerce a ello utilizando una
construcción cast, excepto cuando el objeto al que se tiene acceso a través de la
referencia a la superclase es un objeto de la subclase. Por ejem plo:
360 JA V A: C U R SO DE PROGRAM A CIÓN
CCuentaCorriente clie n te O l =
new C C u e n t a C o r r i e n t e ! " e l i e n t e O l ” , ” 1 2 3 4 5 6 7 8 9 1 ” .
10000. 3 . 5 . 1.0. 6 );
CCuentaCorrienteConln c lie n te :
c l i e n t e = c l i e n t e O l : // e r r o r de c o m p i l a c i ó n : c o n v e r s i ó n i m p l í c i t a
// no p e r m i t i d a
// La s i g u i e n t e l i n e a d u r a n t e l a e j e c u c i ó n l a n z a un a e x c e p c i ó n
// de t i p o C 1 a s s C a s t E x c e p t i o n . d e b i d o a q u e l a c o n v e r s i ó n
// e x p l í c i t a r e q u e r i d a no s e p e r m i t e
c lie n t e = (CCuentaCorrienteConlri)cl i e n t e O l:
// La l i n e a a n t e r i o r s e r i a v á l i d a s i c l i e n t e O l r e f e r e n c i a r a a un
// o b j e t o d e l a c l a s e d e c l i e n t e , e s t o e s . C C u e n t a C o r r i e n t e C o n l n .
POLIMORFISMO
L a utilización de subclases y de m étodos definidos en una clase y redefinidos en
sus clases derivadas es frecuentem ente denom inada program ación orientada a
objetos. En cam bio, la facultad de llam ar a una variedad de m étodos utilizando
exactam ente el m ism o m edio de acceso, proporcionada p o r los m étodos redefini-
dos en las subclases, es a veces denom inada polim orfism o.
La palabra “ polim orfism o" significa “la facultad d e asum ir m uchas form as”,
refiriéndose a la facultad de llam ar a m uchos m étodos diferentes utilizando una
única sentencia.
A sim ism o, sabem os que una referencia a una subclase puede ser convertida
im plícitam ente por Java en una referencia a su superclase directa o indirecta. Esto
significa q ue es posible referirse a un objeto de una subclase utilizando una varia
ble del tipo de su superclase.
p u b lic e la s s Test
I
public static void m a in ( S t r in g ! ] args)
C A P ÍT U L O 10: SU B C LA SE S E IN TERFA CES 3 6 1
C C u e n t a [ ] c l i e n t e = new C C u e n t a [ 1 0 0 ] ;
// C r e a r o b j e t o s y g u a r d a r s u s r e f e r e n c i a s en l a m a t r i z
c l i e n t e [ 0 ] = new C C u e n t a A h o r r o ! " e l i e n t e O O " . ”3 0 0 0 1 2 3 4 5 0 " .
10000. 2 . 5 . 3 0 ) :
c l i e n t e í l ] = new C C u e n t a C o r r i e n t e ! " e l i e n t e O l " . " 6 0 0 0 1 2 3 4 5 0 " .
10000, 2 .0 . 1 . 0 . 6 ):
cliente[2] = new C C u e n t a C o r r i e n t e C o n l n ! " e l i e n t e 0 2 ” ,
" 4 0 0 0 1 2 3 4 5 0 " , 10000. 3 . 5 . 1.0, 6);
for (in t i = 0; e l i e n t e [ i ] ! = n u i l ; i + + )
I
System .o ut.p r i n t ( c l i e n t e [ i ] . obtenerNombre!) +
S y s t e m . o u t . p r i n t 1n ( e l i e n t e [ i ] . i n t e r e s e s ! ) ) :
Este ejem plo define una m atriz cliente de tipo C C uenta con 100 elem entos
que Java inicia con el valor n u il. D espués crea un objeto de una de las subclases y
alm acena su referencia en el prim er elem ento de la m atriz; aquí Java realizará una
conversión im plícita del tipo de la referencia devuelta por n ew al tipo CCuenta.
E ste proceso se repetirá para cada objeto nuevo que deseem os crear (en nuestro
caso tres veces). Finalm ente, utilizando un bucle m ostram os el nom bre del cliente
y los intereses que le corresponderán p o r m es. Pregunta: ¿en cuál d e las dos líneas
de este bucle se aplica la definición de polim orfism o? L ógicam ente en la últim a
porque, según lo estudiado hasta ahora, invoca a las distintas definiciones del
m étodo intereses utilizando el m ism o m edio de acceso: una referencia a CCuenta.
C om o ejem plo, vam os a escribir un program a que cree un objeto que repre
sente a una entidad bancaria con un cierto núm ero de clientes. Este objeto estará
definido por una clase que denom inarem os C Banco y los clientes serán objetos de
alguna de las clases de la jerarq u ía construida en los apartados anteriores.
C la s e O b je c t
)
L[ Clase C C u e n t ^ ^ ^ ^ J [_ C la s e C B a n c o J
C la s e C C u e n ta A h o rro
J
C la s e C C u e n ta C o rrie n te
}
0 C la s e C C u e n ta C o rrie n te C o n ln
)
362 JA VA : C U R SO D E PROGRAM A CIÓN
L a estructura de datos que represente el banco tiene que ser capaz de alm ace
nar objetos C C uentaAhorro, C C uentaC orriente y C C uentaC orrienteC onln. Sa
biendo que cu alquier referencia a un objeto de una subclase puede convertirse
im plícitam ente en una referencia a un objeto de su superclase, la estructura idónea
es una m atriz de referencias a la superclase C Cuenta. E sta m atriz será dinám ica;
esto es, aum entará en un elem ento cuando se añada un objeto de alguna de las
subclases y dism inuirá en uno cuando se elim ine; inicialm ente tendrá 0 elem entos.
Según esto, la clase C Banco, que no pertenece a nuestra jerarquía, tendrá los atri
butos y m étodos que se exponen a continuación:
A trib u to S ig n ificad o
clientes M atriz de referencias de tipo CCuenta.
nE lem entos N úm ero de elem entos de la m atriz.
M éto d o S ig n ificad o
CBanco Es el constructor de la clase. Inicia la m atriz clientes con
cero elem entos.
unE lem entoM ás A ñade un elem ento vacío (n u il) al final d e la m atriz, in
crem entando su longitud en 1.
unE lem entoM enos Elim ina un elem ento cuyo valor sea n u il, decrem entando
su longitud en 1.
insertarC liente A signa un objeto de alguna de las subclases de C Cuenta al
elem ento i de la m atriz clientes.
clienteEn D evuelve el objeto que está en la posición i de la m atriz
clientes.
longitud D evuelve la longitud de la m atriz.
elim inar Elim ina el objeto que coincida con el núm ero de cuenta pa
sado com o argum ento, poniendo el elem ento correspon
diente de la m atriz a valor nuil.
buscar D evuelve la posición en la m atriz clientes del objeto cuyo
nom bre o cuenta, total o parcial, coincida con el valor pa
sado com o argum ento.
//////////////////////////////////////////////////////////////////
// C l a s e C B a n c o : c l a s e que m a n t i e n e una m a t r i z de r e f e r e n c i a s a
// o b j e t o s de c u a l q u i e r t i p o de c u e n t a b a n c a r i a .
//
public class C Ba n c o
I
private C C u e n t a [ ] c l i e n t e s : // m a t r i z de o b j e t o s
private i n t n E l e m e n t o s : // nú m e r o de e l e m e n t o s de l a m a t r i z
CA PÍTU LO 10: SUBC LA SES E IN TERFA CES 3 6 3
pu blic CBancoí)
I
// C r e a r una m a t r i z v a c i a
n E l e m e n t o s - 0:
c l i e n t e s = new C C u e n t a [ n E l e m e n t o s ] ;
p u b l i c CCuenta c l i e n t e E n í int i )
(
// D e v o l v e r l a r e f e r e n c i a al o b j e t o i de l a m a t r i z
i f ( i > = 0 && i < n E l e m e n t o s )
r e t u r n c 1i e n t e s [ i ]:
el se
I
System .out.p r i n t í n ( " índice fuera de l i m i t e s " ) :
r e t u r n n u l 1;
364 JA V A: C U R SO DE PROGRAM A CIÓN
//////////////////////////////////////////////////////////////////
CA PÍTU LO 10: SUBC LA SES E IN TERFACES 3 6 5
Para finalizar, queda escribir una aplicación que utilizando la clase CBanco,
construya la entidad bancaria objetivo del ejem plo propuesto. Esta aplicación pre
sentará un m enú com o el indicado a continuación:
1. Saldo
2. Buscar sig u ie n te
3. Ingreso
4. Reintegro
5. Añadir
6. Elim inar
7. Mantenimiento
8. Sal i r
Opción:
p u b lic c la s s lest
I
p u b l i c s t a t i c CCuenta leerD atostint op) I ... I
do
1
o p c i ó n = m e n ú ( ):
366 JA V A : C U R S O DB PRO G R A M A CIÓ N
switch (opción)
I
c a s e 1: // s a l d o
// B u s c a r un e l e m e n t o p o r e l no mb r e o p o r l a c u e n t a .
// La s u b c a d e n a d e b ú s q u e d a s e r á o b t e n i d a d e l t e c l a d o ,
pos = b a n c o .b u sc a ríc a d e n a b u sc a r, 0):
// S i s e e n c u e n t r a , m o s t r a r n o mb r e , c u e n t a y s a l d o
break:
c a s e 2: II b u s c a r s i g u i e n t e
// B u s c a r e l s i g u i e n t e e l e m e n t o que c o n t e n g a l a s u b c a d e n a
// u t i l i z a d a en l a ú l t i m a b ú s q u e d a ( c a s e 1 ) .
pos = b a n c o . b u s c a r ( c a d e n a b u s c a r . pos + 1):
// S i s e e n c u e n t r a , m o s t r a r n o mb r e , c u e n t a y s a l d o
break;
c a s e 3: // i n g r e s o
c a s e 4: // r e i n t e g r o
// I n g r e s a r o r e i n t e g r a r una c a n t i d a d en l a c u e n t a
// e s p e c i f i c a d a . Ambos d a t o s s e s o l i c i t a r á n d e l t e c l a d o ,
pos = b a n c o . b u s c a r ( cu enta . 0 ) :
i f ( o p c i ó n = = 3)
b a n co.e lienteEn(p os).ingreso(canti d a d );
el se
ban co.clienteEn(pos).reintegro(cantidad):
break:
c a s e 5: // a ñ a d i r
// A ñ a d i r un n u e v o c l i e n t e . El o b j e t o c o r r e s p o n d i e n t e
II s e r á d e v u e l t o p o r e l m ét o d o s t a t i c l e e r D a t o s d e e s t a
// a p l i c a c i ó n , que o b t e n d r á l o s d a t o s d e s d e e l t e c l a d o .
b a n c o . a ñ a d i r ( l e e r D a t o s ( t i p o _ o b j e t o ) ):
break;
c a s e 6 : // e l i m i n a r
// E l i m i n a r e l c l i e n t e q u e c o i n c i d a c o n l a c u e n t a
// t e c l e a d a .
banco.elim inar(cuenta);
break:
c a s e 7: // m a n t e n i m i e n t o
II C o b r a r c o m i s i o n e s e i n g r e s a r i n t e r e s e s
f o r (pos = 0: pos < b a n c o .1o n g i t u d ( ); pos++)
I
b a n c o . e l i e n t e E n ( p o s ) . c o m i s i o n e s í ):
b a n c o . e l i e n t e E n ( p o s ) . i n t e r e s e s ! );
1
break;
c a s e 8 : // s a l i r
banco = n u i l ;
w h i 1e ( o p c i ó n != 8 ) ;
CA PÍTU LO 10: SUBC LA SES E IN TERFA CES 3 6 7
import j a v a . i o . * ;
/////////////////////////////////////////////////////////////////
// A p l i c a c i ó n p a r a t r a b a j a r c o n l a c l a s e CBanco y la j e r a r q u í a
II d e c l a s e s d e r i v a d a s de C C u e n t a
//
public c la ss Test
I
// P a r a l a e n t r a d a d e d a t o s s e u t i l i z a L e e r . c l a s s
p u b l i c s t a t i c C C u e n t a 1e e r D a t o s ( i n t o p)
I
CCuenta obj = n u i l :
S t r i n g n o mbr e , c u e n t a :
d o u b l e s a l d o , t i p o i , m a nt ;
System, out. p r i n t í "Nom bre : "):
nombre = L e e r . d a t o ! );
System, o u t . p r i n t ! " C u e n t a : ");
c u e n t a = L e e r . d a t o ! ):
System, o u t . p r i n t ! " S a l d o : ");
s a l d o = L e e r . d a t o D o u b l e ! ):
S y s t e m , o u t . p r i n t ! " T i p o de i n t e r é s : ");
tip o i = Leer.datoDouble!):
i f ( op = = 1)
I
S y s t e m . o u t . p r i n t ! " M a n t e n i m i e n t o .................. : " ) ;
mant = L e e r . d a t o D o u b l e ( ) ;
o b j = new C C u e n t a A h o r r o í n o m b r e . c u e n t a , s a l d o , tipoi. mant):
1
else
I
int transex;
368 JA V A : C U R SO DE PRO G R A M A CIÓ N
double imptrans;
S y s t e m . o u t . p r i n t ! " Importe por t r a n s a c c i ó n : ");
i m p t r a n s - L e e r . d a t o D o u b l e ! );
S y ste m .o u t.p rin tC 'T ra n sa c c io n e s e xe n ta s..: "):
t r a n s e x = L e e r . d a t o l n t ! ):
if (op = = 2)
o b j = new C C u e n t a C o r r i e n t e ! n o m b r e , c u e n t a , s a l d o , t i p o i ,
imptrans. tra nsex ):
else
o b j = new C C u e n t a C o r r i e n t e C o n I n ( n o m b r e . c u e n t a , s a l d o .
t i p o i . imptrans. transex)
)
return obj ;
i n t o p c i ó n = 0. pos = -1:
S t r in g cadenabuscar = n u il:
S t r i n g n o mb r e , c u e n t a :
double cantidad:
boolean e lim ina d o = f a l s e :
CA PÍTU LO 10: SUBC LA SES E IN TERFACES 3 6 9
do
I
o p c i ó n = m e n ú ( );
switch (opción)
I
c a s e 1: // s a l d o
flu j o S . p r in t t "N o m b r e o cuenta, to tal o p a rc ia l ” );
cadenabuscar = L e e r.d a to O ;
pos = b a n c o . b u s c a r ( c a d e n a b u s c a r , 0 );
i f ( pos = = -1 )
i f ( b a n c o . 1 o n g i t u d ( ) ! = 0)
f 1 u j o S . p r i n t l n ( " b ú s q u e d a f a l 1 i d a ” ):
el se
f l u j o S . p r i n t l n ( " n o hay c l i e n t e s " ) ;
else
I
f 1u j o S . p r i n t l n ( b a n c o . e l i e n t e E n í p o s ) . o b te n e rN o m b re ());
f 1u j o S . p r i n t l n ( b a n c o . e l i e n t e E n ( p o s ) . o b t e n e r C u e n t a ( ) ) ;
f lu j o S . p r i n tln (b a n c o .e lie n te E n tp o s) . e sta d o t));
I
break;
c a s e 2: II b u s c a r s i g u i e n t e
pos = b a n c o . b u s c a r t c a d e n a b u s c a r , pos + 1);
i f ( pos = •1)
i f ( b a n c o . 1 o n g i t u d ( ) ! = 0)
f 1 u j o S . p r i n t l n ( " b ú s q u e d a f a l 1 i d a " ):
el se
f l u j o S . p r i n t l n ( " n o h a y c l i e n t e s ” );
else
[
f 1u j o S .p ri n t l n ( b a n c o . e l i e n t e E n ( p o s ) .obtenerNom bret));
f l u j o S . p r i n t l n ( b a n c o . e l i e n t e E n ( p o s ) . o b t e n e r C u e n t a O );
f lu jo S . p r in t ln t b a n c o .c li enteEn(pos) . e sta d o t)):
1
break;
c a s e 3: // i n g r e s o
c a s e 4 : // r e i n t e g r o
f l u j o S . p r i n t t "C uen ta: " ) : cuenta = L e e r . d a t o O :
pos = b a n c o . b u s c a r ( c u e n t a , 0 ) ;
i f (pos = -1)
i f ( b a n c o . 1o n g i t u d ( ) ! = 0 )
f 1u j o S . p r i n t l n < " b ú s q u e d a f a l 1 i d a " ):
el se
f l u j o S . p r i n t l n { " n o hay c l i e n t e s " ) ;
el se
I
f l u j o S . p r i n t ( " C a n t i d a d : " ) ; c a n t i d a d = L e e r . d a t o D o u b l e ( ):
i f ( o p c i ó n = = 3)
b a n c o . e l i e n t e E n ( p o s ) . i n g r e s o ( c a n t i d a d ):
el se
370 JA V A: C U R SO DE PROGRAM A CIÓN
banco.el i e n t e E n ( p o s ) . r e i n t e g r o ( c a n t i d a d ) ;
I
break;
c a s e 5: // a ñ a d i r
f l u j o S . p r i n t ( " T i p o de c u e n t a : l - ( C A ) , 2 - ( C C ) . 3 - C C I “ ):
do
o p c i ó n = L e e r . d a t o l n t í );
w h i l e ( o p c i ó n < 1 || o p c i ó n > 3 ) :
b a n c o . a ñ a d i r ( l e e r D a t o s ( o p c i ó n ) ):
break:
c a s e 6 : // e l i m i n a r
f l u j o S . p r i n t ( "C uen ta: “ ); cuenta = L e e r . d a t o O ;
e l i m i n a d o = b a n c o . e l i m i n a r ( c u e n t a );
i f (elim inado)
f l u j o S . p r i n t l n ( " r e g i s t r o e l i mi n a d o " );
el s e
i f ( b a n c o . 1o n g i t u d ( ) ! = 0)
f l u j o S . p r i n t í n ( " c u e n t a no e n c o n t r a d a " ) ;
el se
f l u j o S . p r i n t l n C ' n o hay c l i e n t e s " ) :
break:
c a s e 7: // m a n t e n i m i e n t o
f o r ( p o s = 0: p o s < b a n c o . l o n g i t u d ( ) ; p o s + + )
I
b a n c o . e l i e n t e E n í p o s ) . comi s i o n e s ( ):
b a n c o . el i e n t e E n ( p o s ) . i n t e r e s e s ! ) :
1
break:
c a s e 8: // s a l i r
banco = n u i l ;
whi 1 e ( o p c i ón 1=8);
/////////////////////////////////////////////////////////////////
Se puede observar que el m antenim iento de las cuentas de los clientes (case 7)
resulta sencillo gracias a la aplicación de la definición de polim orfism o. Esto es.
el m étodo com isiones o intereses que se invoca para cada cliente depende del tipo
del objeto referenciado p o r el elem ento accedido de la m atriz clientes de banco.
MÉTODOS EN LÍNEA
C uando el com pilador Java conoce con exactitud qué m étodo tiene que llam ar pa
ra responder al m ensaje que se ha program ado que un objeto reciba en un instante
determ inado, puede tom ar la iniciativa de reem plazar la llam ada al m étodo p o r el
cuerpo del m ism o. Se dice entonces que el m étodo está en línea. El que se pro
CA PÍTU LO 10: SUBC LA SES E IN TERFA CES 3 7 1
duzca esta circunstancia, por ejem plo, porque el m étodo es corto, redundará en
tiem pos de ejecución m ás bajos ya que se evita que el intérprete Java tenga que
llam ar al m étodo. En principio, en Java, todos los m étodos de una clase pueden
ser m étodos en línea.
La consulta dinám ica acerca del m étodo que hay que invocar es rápida, pero
no tan rápida com o invocar a un m étodo directam ente. A fortunadam ente no hay
m uchos casos en los que Java necesite usar la consulta dinám ica. P or ejem plo, los
m étodos finales (fin al), los estáticos (sta tic ) y los privados (p riv a te ) pueden ser
invocados directam ente; y si son cortos son candidatos a ser m étodos en línea. Si
un m étodo es final, el com pilador sabe que ese m étodo no puede ser redefinido,
por lo tanto existe una sola versión; si es estático es invocado anteponiendo el
nom bre de su clase; y si es privado no puede ser invocado por un m étodo que no
sea de su clase. P or lo tanto, en ninguno de los tres casos habrá que tom ar una de
cisión acerca de a qué m étodo hay que llam ar.
INTERFACES
De form a genérica una interfaz se define así: un dispositivo o un sistem a utilizado
por entidades inconexas para interactuar. Según esta definición un control rem oto
es una interfaz, el idiom a inglés es una interfaz, etc. A nálogam ente, una interfaz
Jav a es un dispositivo que perm ite interactuar a objetos no relacionados entre sí.
Las interfaces Java en realidad definen un conjunto de m ensajes que se puede
aplicar a m uchas clases de objetos, a los que cada una de ellas debe responder de
form a adecuada. P or eso, una interfaz recibe tam bién el nom bre de protocolo.
El m odificador de acceso p u b lic indica que la interfaz puede ser utilizada por
cualquier clase de cualquier paquete. Si no se especifica, entonces sólo estará ac
cesible para las clases definidas en el m ism o paquete que la interfaz. U na interfaz
puede incluirse en un paquete exactam ente igual que una clase.
El nom bre de una interfaz se puede utilizar en cualquier lugar donde se pueda
utilizar el nom bre de una clase.
Para com prender con claridad las interfaces vam os a realizar un ejem plo de
una interfaz 1Fecha que va a ser utilizada para que dos clases (G r e g o r ia n C a le n -
d a r y una de las subclases de C C uenta) interactúen entre sí.
import j a v a . ú t i l .*;
//////////////////////////////////////////////////////////////////
II interfaz IFecha: métodos y c o n s t a n t e s para obtener
// e l d í a , mes y a ñ o
//
public interface IFecha
I
public final s t a t i c i n t D I A _ D E L _ M E S = C a l e n d a r . DAY_OF_MONTH;
public final s t a t i c i n t M E S _ D E L _ AÑ 0 = C a l e n d a r . M O N T H ;
public final s t a t i c i n t AÑO = C a l e n d a r . Y E A R ;
Se puede observar que una interfaz sólo declara los m étodos, no los define, y
adem ás puede definir constantes. T am bién, cada una de las declaraciones y defi
374 JA V A : C U R SO DE PRO G R A M A CIÓ N
niciones finaliza con un punto y com a (;). T odos los m étodos declarados en una
interfaz son im plícitam ente públicos y abstractos (p u b lic y a b stract); y todas las
constantes son im plícitam ente públicas, finales y estáticas (p u b lic, fin a l y static).
En am bos casos, el uso de estos m odificadores es sólo una cuestión de estilo.
C ualquier clase puede tener acceso a las constantes de la interfaz a través del
nom bre de la m ism a. Por ejem plo:
1 F e c h a . AÑ O
En cam bio, una clase que im plem ente la interfaz puede tratar las constantes
com o si las hubiese heredado; esto es, accediendo directam ente a su nom bre.
import j a v a . u t i 1 . * ;
//////////////////////////////////////////////////////////////////
// C l a s e C C u e n t a A h o r r o : c la s e derivada de CCuenta
//
public c la ss C Cu en taAh o rro e x t e n d s CCuenta implements IFecha
I
II...
public void com isiones!)
I
// S e a p l i c a n m e n s u a l m e n t e p o r e l m a n t e n i m i e n t o d e l a cuenta
i f ( d i a ! ) = = 1) r e i n t e g r o ( c u o t a M a n t e n i m i e n t o ) ;
I
Si una clase im plem enta una interfaz, todas sus subclases heredarán los nue
vos m étodos que se hayan im plem entado en la superclase, así com o las constantes
definidas por la interfaz. Por ejem plo, m odifiquem os la clase C C uentaC orrienle
para que utilice tam bién la interfaz ¡Fecha:
•import j a v a . ú t i l .*;
//////////////////////////////////////////////////////////////////
// C l a s e C C u e n t a C o r r i e n t e : c l a s e d e r i v a d a d e C C u e n t a
II
public class CCuentaCorriente extends CCuenta implements IFecha
I
// ...
public void com isionesí)
I
// S e a p l i c a n m e n s u a l m e n t e p o r el m a n t e n i m i e n t o de l a c u e n t a
i
n . . .
II...
// I m p l e m e n t a c i ó n de l o s m é t o d o s de l a interfaz IFecha
p u b l i c i n t d 1a ( )
I
GregorianCalendar fechaActual = new G r e g o r i a n C a l e n d a r í ):
376 JA VA : C U R S O D E PRO G R A M A CIÓ N
return f e c h a A c t u a l . g e t ( D I A _ D E L _ M E S );
1
public i n t mes O ( return 0; ] // no s e n e c e s i t a
public in t año() I return 0: I II n o s e n e c e s i t a
I
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// C l a s e C C u e n t a C o r r i e n t e C o n l n : c la se derivada de C C u e n t a C o r r i e n t e
//
public c la ss CCuentaCorrienteConIn extends CCuentaCorriente
I
// .. .
public double inte resesO
(
í f ’C d i a O ! = 1 || e s t a d o ? ' ) < 3000) return 0.0;
II...
C la s e O b je c t
J
Clase CCuenta
J C la s e C B a n c o
J
C la s e C C u e n ta A h o rro
i In te rfa z IF e c h a
C la s e C C u e n ta C o rrie n te
i
C la s e C C u e n ta C o rrie n te C o n ln
)
Probablem ente habrá pensado que hubiéram os obtenido el m ism o resultado
im plem entado la interfaz ¡Fecha en la superclase C Cuenta. Pues, si es así, tiene
CA PÍTU LO 10: SU B C LA SE S E IN TERFA CES 3 7 7
razón. El hecho de haber im plem entado la interfaz en las subclases ha sido pura
m ente didáctico.
import j a v a . ú t i l
//////////////////////////////////////////////////////////////////
// C l a s e IFecha: métodos y c o n s t a n t e s para obtener
// e l d í a , mes y a ñ o
//
public abstract class IFecha
I
public final s t a t i c i n t D Í A _ O E L _ M E S = C a 1e n d a r . D A Y _ 0 F _ M 0 N T H :
public final s t a t i c i n t M E S _ D E L _ A Ñ O = C a 1e n d a r . MONTH:
public final s t a t i c i n t AÑO »= C a l e n d a r . Y E A R ;
public a b s t r a c t i n t d i a ( );
public a b str a c t in t mes():
public abstract in t año():
I
//////////////////////////////////////////////////////////////////
E videntem ente nuestro problem a en concreto tiene una solución, que es deri
var la clase C Cuenta de la clase abstracta ¡Fecha e im plem entar en C Cuenta los
m étodos proporcionados por ¡Fecha. Pero nuestro objetivo no es dar solución a
este problem a, sino presentar ejem plos adecuados acerca de lo que se quiere ex
plicar.
378 JA V A : C U R SO D E PRO G R A M A CIÓ N
P o r ejem plo, se puede declarar una m atriz clientes que sea de tipo / Fecha y
asignar a cada elem ento un objeto de algunas de las subclases de C C uenta:
I F e c h a ü c l i e n t e s - new I F e c h a [ 3 ] :
C C u e n t a A h o r r o c l i e n t e O - new C C u e n t a A h o r r o ( ) ;
c lie n te s [0 ] = clienteO;
// .. .
( ( C C u e n t a A h o r r o ) e l i e n t e s [ 0 ] ) . a s i g n a r N o m b r e ( " e l i e n t e O " ):
System .out.p rintln(elienteO .ob tenerN om breí));
II...
U na variable del tipo de una interfaz espera referenciar un objeto que tenga
im plem entada d icha interfaz, de lo contrario el com pilador Java m ostrará un error.
En el ejem plo anterior la variable clientes[O j de tipo ¡Fecha hace referencia a un
objeto C C uentaA horro que im plem enta esa interfaz.
A sim ism o, se puede observar que es posible convertir im plícitam ente referen
cias a objetos que im plem entan una interfaz en referencias a esa interfaz y vice
versa, pero en este caso, explícitam ente. Lógicam ente, con lo que sabemos,
podem os ded ucir que con una referencia a la interfaz sólo se tiene acceso a los
m étodos y constantes declarados en dicha interfaz.
In te rfa z Ixxx
c C la s e l
J C la s e 3
J C la s e 2
o b j s [ 0 3 - n e w C 1 a s e l ( ) ;
o b j s [ l ] - n e w C l a s e 2 ( ) :
o b j s [ 2 ] = n e w C l a s e 3 ( ) ;
f o r í i n t i = 0 : i < o b j s . l e n g t h : i + + )
o b j s [ i ] . m ( ) ; / / i n v o c a a l m é t o d o m d e l o b j e t o C l a s e l . C l a s e 2
/ / o C l a s e 3 r e f e r e n c i a d o p o r o b j s C i ]
I
I
• C aptar sim ilitudes entre clases no relacionadas sin forzar entre ellas una rela
ción artificial. Una acción de este tipo perm itiría incluso, definir una matriz
de objetos de esas clases y aplicar, si fuera necesario, la definición de poli
m orfism o.
• D eclarar m étodos que una o m ás clases deben im plem entar en determ inadas
situaciones.
Suponga que se ha diseñado una clase de objetos que puede tener un com
portam iento especial siem pre que im plem ente unos determ inados métodos.
Por ejem plo, cuando aprenda sobre applets y subprocesos com probará que
usar un subproceso en un applet im plica que la clase de éste im plem ente la
interfaz R unnable.
• Publicar la interfaz de program ación de una clase sin descubrir cóm o está im-
plem entada.
C uando una clase im plem ente m últiples interfaces puede suceder que dos o
m ás interfaces diferentes im plem enten el m ism o m étodo. Si esto ocurre, proceda
de alguna de las form as indicadas a continuación:
• Si los m étodos tienen el m ism o prototipo, basta con definir uno en la clase.
• Si los m étodos difieren en el núm ero o tipo de sus parám etros, estam os en el
caso de una sobrecarga del m étodo: im plem ente todas las sobrecargas.
• Si los m étodos sólo difieren en el tipo del v alor retornado, no existe sobrecar
ga y el com pilador produce un error, ya que dos m étodos pertenecientes a la
m ism a clase no pueden d iferir sólo en el tipo del resultado.
CLASES ANIDADAS
U na clase anidada es una clase que es un m iem bro de otra clase. Por ejem plo, en
el código m ostrado a continuación, C Fecha es una clase anidada:
U na clase se debe definir dentro de otra sólo cuando tenga sentido en el con
texto de la clase que la incluye o cuando depende de la función que desem peña la
clase q ue la incluye. Por ejem plo una ventana puede definir su propio cursor: en
este caso, la ventana puede ser un objeto de una clase y el cursor de una clase ani
dada.
Clases internas
C uando un m iem bro de una clase es una clase anidada y no es static recibe el
nom bre de clase interna p o r tratarse de un m iem bro del objeto. Esto significa que
cuando se cree un objeto de la clase externa tam bién se creará uno de la interna.
Por ejem plo, según la definición anterior de C Persona y C F echa, el siguiente có
digo crea un objeto o b j de la C Persona que incluye un objeto de la CFecha.
CPersona o bj - new C P e r s o n a O ;
Si una ciase interna está asociada con un objeto, lógicam ente no puede tener
m iem bros static.
public c la ss CPersona
I
p r i v a t e S t r i n g n o mbr e :
p r i v a t e CFecha f e c h a N a c i m i e n t o ;
private c la ss C F ec h a
I
private i n t d 1 a . mes . a ñ o ;
private C F e c h a ( i n t d d , i n t mm, int aa)
I
día = dd : mes = mm; a ñ o = a a :
I
l
public C P e rso n a () I 1
CA PÍTU LO 10: SU B C LA SE S E IN TERFA CES 3 8 3
C uando com pile la aplicación Test anterior, podrá observar que Java genera,
adem ás del fichero Test.class y de C Persona.class. el fichero C Persona$C Fe-
cha.class correspondiente a la clase interna. P or lo tanto, cuando quiera instalar la
aplicación en o tra m áquina no olvide que cada clase interna tiene su propio fiche
ro de clase, y que deben incluirse ju n to con los de las clases de nivel superior. Re
sum iendo, para poder ejecutar la aplicación Test del ejem plo anterior son
necesarios los ficheros: Test.class, C P ersona.class y C PersonaSC Fecha.class.
C l a s e 2 o b j = new C l a s e 2 ( ) :
o b j . m e t C l a s e 2 ( 1, 2 ) :
publi c c la s s C1ase2
I
p u b l i c v o i d m e t C la s e 2 ( i n t x. final int y)
(
i n t i = x + y;
final int c = x + y :
c la s s Clase3
I
// i n t a » x ; II e r r o r : x no e s f i n a l
i n t b = y;
void m etClase3()
I
// System .out.pri n t l n ( b + i): // e r r o r : i no e s f i n a l
S y s t e m . o u t . p r i n t l n t b + c );
1
C l a s e 3 o b j = new C l a s e 3 ( ):
o b j . m e t C l a s e 3 ( ):
Clases anónimas
A lgunas veces podem os escribir una clase dentro de un m étodo sin necesidad de
identificarla. U na clase definida de esta form a recibe el nom bre de clase anónim a.
P or otra parte, para crear con new un objeto de una clase necesitam os conocer
el nom bre de la m ism a. E ntonces ¿para qué se utiliza una clase anónim a?
interface Interfaz
I
p u b l ic a b s t r a c t v o i d p ( );
public abstract v o i d m ( ):
CA PITU LO 10: SU B C LA SE S E IN TERFA CES 3 8 5
class Clase2
I
private int i :
// ...
public In te rfa z metClase2() // m é t od o de l a clase 2
I
r e t u r n new C l a s e 3 ( );
En este ejem plo se puede observar que la C lase2 define una clase interna Cla-
se3 y un m étodo m e tC la se l que devuelve una referencia del tipo Interfaz a un
objeto Clase3 que im plem enta dicha interfaz. El m étodo m a in de C la se l pone a
prueba las capacidades de Clase2.
donde Ixxx es el nom bre de la interfaz que im plem enta la clase. Por ejem plo:
Resum iendo, una clase anónim a es una form a de evitar el que tengam os que
pensar en un nom bre trivial para una clase pequeña en código, utilizada en un lu
gar concreto. Evidentem ente, el precio que se paga es que no podrem os crear o b
jeto s de esta clase fuera del lugar donde haya sido definida.
EJERCICIOS RESUELTOS
Se quiere escribir un program a para m anipular ecuaciones algebraicas o polinó-
m icas dependientes de las variables x e y. Por ejem plo:
C ada térm ino del polinom io será representado por una clase C Term ino y cada
polinom io p o r una clase C P olinom io.
Q uizás se pregunte qué sentido tiene realizar este ejercicio, si no trata con
subclases. La respuesta es sencilla, este ejercicio es la antesala al ejercicio que a
continuación se propone.
//////////////////////////////////////////////////////////////////
La clase C Term ino representa un térm ino del polinom io, el cual queda per
fectam ente definido cuando se conoce su coeficiente, el grado de la variable x y el
grad o de la variable y: coeficiente, exponenteD eX y exponenteD eY.
Para acceder a los atributos de un térm ino se han im plem entado las funciones
típicas de asignar y obtener el valor alm acenado en el atributo que se trate en cada
388 JA V A: C U R SO DE PROGRAM A CIÓN
caso. O tros m étodos im plem entados son: un constructor sin argum entos y otro
con argum entos para perm itir construir un objeto C Term ino a partir de unos valo
res determ inados; un constructor copia para poder construir un nuevo térm ino a
partir de otro existente; un m étodo copiar para poder copiar un térm ino en otro
existente; y un m étodo m ostrarTerm ino para visualizar un térm ino en la pantalla.
Es evidente que extender esta clase a térm inos de polinom ios dependientes de
m ás de dos variables no entraña ninguna dificultad; es cuestión de añadir más
datos m iem bro y las funciones de acceso correspondientes.
import ja va .m a th .*:
//////////////////////////////////////////////////////////////////
// C l a s e C P o l i n o m i o . Un o b j e t o C P o l i n o m i o consta de un o o más
// o b j e t o s CTermino.
//
public c la ss CPolinomio
1
private C T e r m i n o [ ] t é r m i n o s : // m a t r i z de o b j e t o s
private i n t n E l e m e n t o s : // nú me r o de e l e m e n t o s de l a m a t r i z
C P o l i n o m i o p o l i n o m i o A = new C P o l i n o m i o ( );
Para increm entar la m atriz de referencias del polinom io en un elem ento, ini
ciado con el valor n u il, escribirem os el m étodo unE lem entoM ás y para elim inar
un elem ento previam ente establecido a nuil, el m étodo unE lem entoM enos. A m
bos m étodos se m uestran a continuación:
vacío al final) y después busca el lugar donde debe ser insertado el nuevo térm i
no; si ese lugar no es el últim o, hace un hueco m oviendo un lugar en esta direc
ción los térm inos que hay desde ahí hasta el final.
El algoritm o utilizado para saber el lugar que le corresponde al térm ino que se
inserta en orden ascendente prim ero p o r x y después p o r y, es m uy sencillo: a cada
unidad del exponente de x le dam os un peso ( y a cada unidad del exponente de y
un peso de 1; la sum a de am bas cantidades nos d a el valor utilizado para efectuar
la ordenación requerida. El valor de k es la potencia de 10 que sea igual o m ayor
que el m ayor de los exponentes de e y del térm ino a insertar.
Para elim inar un térm ino, escribirem os el m étodo elim in a r!erm in o que recibe
com o argum ento el índice del elem ento de la m atriz térm inos del objeto CPoli-
n om io que hace referencia al m ism o. U tilizando ese índice, se envía el objeto
C Term ino a la basura poniendo a n u il el elem ento que lo referencia y se llam a al
m étodo unE lem entoM enos para quitar dicho elem ento de la m atriz y decrem entar
en 1 el núm ero de elem entos de la m ism a. El m étodo devuelve tr u e si la opera
ción se realiza satisfactoriam ente y false en caso contrario.
Para obten er el térm ino i del polinom io im plem entam os el m étodo térm inoEn.
E ste m étodo recibe com o argum ento el índice i del térm ino del polinom io que se
desea recuperar y devuelve una referencia al m ism o.
p u b l i c CTermino t é r m in o E n ( i n t i)
I
// D e v o l v e r l a r e f e r e n c i a al o b j e t o i de l a m atriz
i f ( i > = 0 && i < n E l e m e n t o s )
r e t u r n té rm i n o s [ i ]:
el se
I
System .out.p rintlní"índ ice fuera de l i m i t e s " ) :
return n u il;
El m étodo longitud devuelve el núm ero de térm inos del polinom io.
public C P o l i n o m i o c o p i a r ( C P o l i n o m i o p) // a s i g n a c i ó n
I
// C o p i a r e l o r i g e n en e l n u e v o d e s t i n o
n E l e m e n t o s = p . n E 1e m e n t o s :
t é r m i n o s = new C T e r m i n o E n E l e m e n t o s ] ;
fo r ( i n t i = 0 : i < nElementos: i++)
t é r m i n o s E i ] = new C T e r m i n o ( p . t é r m i n o s [ i ] ) :
392 JA VA: C U R SO D E PROGRAM A CIÓN
return this:
I
O bserve que prim ero se construye un nueva m atriz de referencias del m ismo
tam año que la m atriz origen y después se asigna a cada uno de sus elem entos, el
correspondiente duplicado del objeto C Term ino (invocando al constructor copia).
Si no duplicáram os los objetos C Term ino. esto es, si hiciéram os:
los dos polinom ios, origen y destino, harían referencia a los m ism os térm inos; con
lo cual, las m odificaciones realizadas en uno de ellos repercutirían tam bién de la
m ism a form a en el otro.
El siguiente m étodo perm ite sum ar dos polinom ios. L a idea básica es cons
truir un tercer polinom io que contenga los térm inos de los otros dos, pero sum an
d o los coeficientes de los térm inos que se repitan en am bos. L os térm inos en el
polinom io resultante tam bién quedarán ordenados ascendentem ente, por el m ismo
criterio que se expuso anteriorm ente. U na vez finalizada la sum a, se elim inarán
los térm inos que hayan resultado nulos (coeficiente 0). U n ejem plo de cóm o invo
car a este m étodo puede ser el siguiente:
pR = p A . s u m a r ( p B ) ;
a) Partiendo de los polinom ios p A y p B que se quieren sum ar, obtener un térm i
no de cada uno de ellos.
b) C om parar los dos térm inos (uno de cada polinom io) según el criterio explica
do cuando se expuso el m étodo insertarTerm ino, y alm acenar el m enor en el
polinom io pR.
c) O btener el siguiente térm ino del polinom io al que pertenecía el térm ino alm a
cenado en p R . y volver al punto b).
d) C uando no queden m ás elem entos en uno de los dos polinom ios de partida, se
copian directam ente en p R todos los elem entos que queden en el otro polino
mio.
d o u b l e coef ' A, c o e f B ;
i n t expXA. e x p Y A . expXB, e x p Y B ;
C P o l i n o m i o pR = new C P o l i n o m i o ( ): II p o l i n o m i o resultante
// S u m a r pA con pB
w h i l e ( i p a < na && i p b < nb )
I
c o e f A = t é r m i n o s [ i p a ] . o b t e n e r C o e f i c i e n t e ( );
e x p X A = t é r m i n o s [ i p a ] . o b t e n e r E x p o n e n t e D e X Í );
e xp Y A = t é r m i n o s [ i p a ] . o b t e n e r E x p o n e n t e D e Y { ) ;
c o e f B = p B . t é r m i n o s f i p b ] . o b t e n e r C o e f i c i e n t e ( );
expXB = p B . t é r m i n o s C i p b ] .o b t e n e r E x p o n e n t e D e X ( ) ;
expYB = p B . t é r m i n o s t i p b ] .o b t e n e r E x p o n e n t e D e Y ( ) ;
k = 10;
w h i l e ( M a t h . a b s ( e x p X A ) > k || M a t h . a b s ( e x p Y A ) > k ) k = k*10;
if ( expXA — e x p X B && e x p Y A = = e x p Y B )
I
pR.insertarTerm ino(new CTerminoícoefA+coefB. expXA, expYA));
ipa++: ipb++;
I
else if ( expXA * k + expYA < expXB * k + expYB)
I
p R . i n s e r t a r T e r m i n o í n e w C T e r m i n o ( c o e f A . expXA. expYA));
ipa++;
1
el se
I
p R . in s e rt a rT e rm irio ín e w C T e r m in o f c o e f B . expXB. expYB));
i pb + + ;
// T é r m i n o s r e s t a n t e s en e l pA
w h i l e ( i p a < na )
I
c o e f A = t é r m i n o s [ i p a ] . o b t e n e r C o e f i c i e n t e t ):
expXA - t é r m i n o s [ i p a ] , o b t e n e r E x p o n e n t e D e X ( ):
e xp Y A - t é r m i n o s [ i p a ] . o b t e n e r E x p o n e n t e D e Y ( ) ;
p R . i n s e r t a r T e r m i n o ( n e w C T e r m i n o í c o e f A , expXA. expYA));
i pa++:
1
// T é r m i n o s r e s t a n t e s en e l pB
w h i l e ( i p b < nb )
I
c o e f B = p B . t é r m i n o s [ i p b ] . o b t e n e r C o e f i c i e n t e í );
expXB = p B . t é r m i n o s t i p b ] . o b t e n e r E x p o n e n t e D e X ( ) ;
expYB = p B . t é r m i n o s f i p b ] . o b t e n e r E x p o n e n t e D e Y ( ) ;
p R . i n s e r t a r T e r m i n o ( n e w C T erm in o tco e fB, expXB. e x p Y B ));
i pb + + ;
394 JA V A: C U R SO DE PROGRAM A CIÓN
// Q u i t a r l o s t é r m i n o s c o n c o e f i c i e n t e 0
k - 0;
w hile ( k < pR.nElementos )
I
if ( p R . t é r m i n o s [ k ] . o b t e n e r C o e f i c i e n t e ( ) = = 0)
I
pR.elim inarTerm ino(k);
pR.nElem entos--:
I
el se
k++;
1
return pR;
1
El siguiente m étodo perm ite m ostrar todos los térm inos del polinom io pasado
com o argum ento.
w h i 1e ( i - - ! = 0 )
t é r m i n o s [ i ] . m o s t r a r T e r m i no ( ):
I
E ste otro m étodo que se expone a continuación, devuelve el valor del polino
m io para los valores de x e y pasados com o argum entos.
f o r ( i n t i = 0: i < n E l e m e n t o s : i + + )
v += t é r m i n o s t i ] . o b t e n e r C o e f i c i e n t e ( ) *
Math.pow(x. t é r m i n o s [ i ] . obtenerExponenteDeX( ) ) *
M a t h .p o w ( y . térmi n o s [ i ] . o b te n e rE x p o n e n te D e Y ( ) ) ;
r e t u r n v;
1
El siguiente program a, utilizando las clases C Term ino y C Polinom io, lee dos
polinom ios, crea un tercero sum a de los dos anteriores y visualiza el polinom io
resultante, así com o su valor para x e y igual a 1.
// Suma de p o l i n o m i o s d e p e n d i e n t e s de d o s v a r i a b l e s .
// E s t a a p l i c a c i ó n u t i l i z a l a c l a s e L e e r .
//
public class Test
I
C A PÍTU LO 10: SUBC LA SES E IN TERFACES 3 9 5
// V i s u a l i z a r el p r i m e r s uma nd o
396 JA V A: C U R SO DE PROGRAM ACIÓN
S y s t e m . o u t . p r i n t l " R o l i n o m i o A: ” ):
p o l i n o m i o A . m o s t r a r P o l i n o m i o ( ):
S y s t e m . o u t . p r i n t l n í );
II V i s u a l i z a r e l s e g u n d o suma nd o
S y s t e m . o u t . p r i n t í " P o l i n o m i o B: “ ):
p o l i n o m i o B . m o s t r a r P o l i n o m i o í );
S y s t e m . o u t . p r i n t l n í );
// V i s u a l i z a r e l p o l i n o m i o suma
S y s t e m . o u t . p r i n t ( " P o l i n o m i o R: ” ):
p o l i nomi o R . m o s t r a r P o l i n o m i o í );
S y s t e m . o u t . p r i n t l n í );
// V i s u a l i z a r e l v a l o r d e l p o l i n o m i o suma p a r a x = 1 e y - 1
S y s t e m . o u t . p r i n t l n í “P a r a x - 1 e y = 1. e l v a l o r e s : ’ +
p o l i n o m i o R . v a l o r P o l o n o m i o í 1. 1 ) ) :
EJERCICIOS PROPUESTOS
2 x 'y - xyz* + 8.25 m ás 5xsy - 2 xiy + 7x2 - 3 igual a 5xsy + 7x2 - x y z ' + 5.25
C ada térm ino del polinom io será representado por una clase C TenninoE nX ,
C Term inoE nX Y o C T enninoE nX Y Z y cada polinom io por una clase CPolinom io.
La clase C Term inoE nX Y se derivará de C T erm inoE nX y la clase C T enninoE nX Y Z
de CTerminoEnXY:
C la s e O b je c t
L C la s e C T e r m i n o E r ó ^ ^ J {_ C la s e C P o lin o m io
C la s e C T e rm in o E n X Y
L C la s e C T e r m in o E n X Y Z ^
EXCEPCIONES
El lenguaje Jav a incorpora soporte para m anejar situaciones anóm alas, conocidas
com o “excepciones”, que pueden ocurrir durante la ejecución de un program a.
C on el sistem a de m anipulación de excepciones de Java, un program a puede co
m unicar eventos inesperados a un contexto de ejecución m ás capacitado para res
ponder a tales eventos anorm ales. Estas excepciones son m anejadas por código
fuera del flujo norm al de control del program a.
Las excepciones proporcionan una m anera lim pia de verificar errores; esto es,
sin abarrotar el código básico de una aplicación utilizando sistem áticam ente los
códigos de reto m o de los m étodos en sentencias if y sw itch para controlar los po
sibles errores que se puedan dar. V eam os con un ejem plo, a qué nos estam os refi
riendo:
i n t c ó d i d o D e E r r o r = 0;
c ó d i d o D e E r r o r = 1e e r F i c h e r o í n o m b r e );
i f ( códidoDeError != 0 )
I
// O c u r r i ó un e r r o r a l l e e r el fichero
switch( códidoDeError )
1
c a s e 1:
// No s e e n c o n t r ó e l f i c h e r o
// . . .
break;
c a s e 2:
// El f i c h e r o e s t á c o r r u p t o
// . . .
break:
c a s e 3:
// El d i s p o s i t i v o no e s t á l i s t o
// . . .
398 JA V A: C U R SO DE PRO G R A M A CIÓ N
break;
default:
// O t r o e r r o r
// . . .
I
I
el se
I
// P r o c e s a r los datos leídos del fichero
I
El código del ejem plo anterior trata de leer un fichero alm acenado en el disco
invocando al m étodo leerFichero. E ste m étodo devuelve un valor 0 si se ejecuta
satisfactoriam ente y un valor distinto de 0 en otro caso. Para analizar este hecho
se ha utilizado una sentencia if. En el caso de que se produzca un error, una sen
tencia sw itc h se encargará de verificar qué es lo que ha ocurrido y tratar de resol
verlo de la m ejor form a posible. Lo que se persigue es que el program a no sea
abortado inesperadam ente por el sistem a, sino diseñar una continuación o term i
nación norm al dentro de lo ocurrido.
O bservar el código que ha sido necesario escribir para tratar un posible error
de no poder leer un fichero del disco. Pensem os ¿cuántos errores m ás podrían
abortar nuestra aplicación? Para que esto no suceda ¿se im agina la com plejidad
del código escrito una vez añadido todo el necesario para tratar cada uno de ellos?
El m anejo de excepciones ofrece una form a de separar explícitam ente el código
que m aneja los errores, del código básico de una aplicación, haciéndola m ás legi
ble, lo que desem boca en un buen estilo de program ación. P or ejem plo:
try
(
// C ó d i g o de l a aplicación
catch(.clase_de_excepción e)
{
I I C ó d i g o de t r a t a m i e n t o d e e s t a excepción
I
c a t c h í o t r a _ c l a s e _ d e _ e x c e p c i ó n e)
(
// C ó d i g o de t r a t a m i e n t o para o t r a c l a s e de e x c e p c i ó n
1
EXCEPCIONES DE JAVA
D urante el estudio de los capítulos anteriores, seguro que se habrá encontrado con
excepciones com o las siguientes:
T h ro w a b le
J
n Exceptio n
J
E rror
J
R u n tim e E x c e p tio n
J
C la s s N o tF o u n d E x c e p tio n
J
lO E x c e p tio n
J
E O F E x c e p tio n
J
400 JA V A: C U R SO DE PROGRAM A CIÓN
La clase E x ce p ció n cubre las excepciones que una aplicación norm al puede
m anipular. T iene varias subclases entre las que destacan: R u n tim e E x c e p tio n e
IO E x c e p tio n .
MANEJAR EXCEPCIONES
C uando un m étodo se encuentra con una anom alía que no puede resolver, lo lógi
co es que lance (th ro w ) una excepción, esperando que quien lo llam ó directa o
indirectam ente la atrape (catch ) y m aneje la anom alía. Incluso él m ism o podría
atrapar y m anipular dicha excepción. Si la excepción no se atrapa, el program a fi
nalizará autom áticam ente.
import j a v a . i o . *:
^ p u b l i c c l a s s Leer
'' I
public static Strin g d a t o ()
I
Strin g sdato - "";
try
I
// D e f i n i r un f l u j o d e c a r a c t e r e s de e n t r a d a : f l u j o E
I n p u t S t r e a m R e a d e r i s r - new I n p u t S t r e a m R e a d e r t S y s t e m . i n ) ;
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r ( i s r ) :
//
402 JA VA : C U R SO DE PROGRAM A CIÓN
Las palabras try y catch trabajan conjuntam ente y pueden traducirse así:
“p o n er a prueba un fragm ento de código por si lanzara una excepción; si se eje
cuta satisfactoriam ente, continuar con la ejecución del program a; si no, atrapar la
excepción lanzada y m anejarla” .
if (error) t h r o w new I O E x c e p t i o n ( ):
public c la s s Test
I
public static void m a in (S trin g [] args)
I
String str;
System .out.printí"Dato: "):
s t r - L e e r , d a t o ( );
II...
B u ffe re d R e a d e r.re a d U n e
J
Para im plem entar un m anejador para una clase de excepción hay que hacer
las do s cosas que se indican a continuación:
try
I
// ...
s d a t o = f l u j o E . r e a d L i n e í ):
I
c a t c h ( I O E x c e p t i on e)
1
S y s t e m . e r r . p r i n t l n ( " E r r o r : " + e .g e t M e s s a g e í ) ) ;
I
Por ejem plo, si el p rim er bloque catch especifica un parám etro de la clase
T h ro w ab le . ningún otro bloque que le siga podría alcanzarse; esto es, cualquier
excepción lanzada sería atrapada por ese prim er bloque, ya que cualquier referen
cia a una subclase puede ser convertida im plícitam ente p o r Java en una referencia
a su superclase directa o indirecta.
try
1
II...
I
catch(EOFException e)
I
II M a n e j a r e s t a c l a s e de e x c e p c i ó n
I
c a t c h t I O E x c e p t i o n ' e)
I
// M a n e j a r e s t a c l a s e de e x c e p c i ó n o de a l g u n a de s u s subclases.
// e x c e p t o E O F E x c e p t i o n
I
c a t c h t E x c e p t i o n e)
[
// M a n e j a r e s t a c l a s e de e x c e p c i ó n o de a l g u n a de s u s subclases,
// e x c e p t o E O F E x c e p t i o n e I O E x c e p t i o n
I
BLOQUE DE FINALIZACIÓN
Si no se trata de m anejar excepciones, sino de realizar alguna acción por obliga
ción (por ejem plo, liberar algún recurso externo que se haya adquirido, cerrar un
fichero, etc.) ponga el código adecuado dentro de un bloque fin a lly después del
bloque try o de un bloque catch. El bloque fin a lly deber ser siem pre el últim o.
L a ejecución del bloque fin a lly queda garantizada independientem ente de que
finalice o no la ejecución del bloque try. Q uiere esto decir que aunque se abando
ne la ejecución del bloque try porque, por ejem plo, se ejecute una sentencia re
tu rn . el bloque fin a lly se ejecuta.
finally
I
// C e r r a r el f i c h e r o
i f ( f c l i != n u i l ) f e 1i . c 1o s e ( ):
I
406 JA V A : C U R SO D E PROGRAM A CIÓN
El objeto lista de la clase C ListaC lientes encapsula una m atriz que alm acena
rá la lista d e los clientes del banco. El m étodo a ñ a d ir puede lanzar una excepción
A r r a y ln d e x O u t O f B o u n d s E x c e p t io n si el índice utilizado para acceder a los
elem entos de la m atriz encapsulada en el objeto C ListaC lientes tiene un valor fue
ra de los lím ites establecidos. L os m étodos P rintW riter y escribir pueden lanzar
una excepción IO E x c e p t io n si el fichero no puede abrirse, o bien ocurre un error
de escritura. Las excepciones im plícitas com o A r r a y ln d e x O u t O f B o u n d s E x c e p
tion no estam os obligados a m anejarlas, pero las explícitas com o IO E x c e p t io n sí.
P o r eso se ha añadido un m anejador para este tipo de excepción. F inalm ente se ha
añadido un bloque fin a lly para garantizar que, ocurra lo que ocurra, el fichero se
rá cerrado cuando se abandone el m étodo escribirD atos.
¿Es realm ente necesario el bloque fin a lly ? En el ejem plo anterior, el m étodo
escribirD atos no proporciona un m anejador de excepciones p o r si ocurre un error
durante la ejecución del m étodo añadir. Entonces, si este error sucede ¿cóm o ce
rraríam os el fichero? L a respuesta es: el bloque fin a lly es lo últim o que se ejecuta
antes de abandonar el m étodo escribirD atos, lo que ocurrirá cuando:
En el p rim er caso, después del bloque try se ejecutará el bloque fin a lly y se
saldrá del m étodo escribirD atos. En el segundo caso, se ejecutará el bloque catch
proporcionado p o r escribirD atos, después el bloque fin a lly y se saldrá del m éto
do. Y en el tercer caso, se ejecutará el bloque catch proporcionado por el sistem a,
después el bloque fin a lly y se saldrá del m étodo.
Los bloques try y fin a lly pueden tam bién utilizarse conjuntam ente, sin que
sea necesario incluir un bloque catch.
DECLARAR EXCEPCIONES
Java requiere que cualquier m étodo que pueda lanzar una excepción la declare o
la atrape. Por ejem plo ¿veam os qué ocurre si en el m étodo escribirD atos presen
tado en el ejem plo anterior elim inam os el bloque c a tch ? O bservarem os que cuan
do Java com pile este m étodo indicará m ediante un m ensaje de error que la
excepción IO E x c e p t io n no está interceptada ni declarada p o r el m étodo escri-
C A PÍTU LO I I: E X CEPC IO N ES 4 0 7
birD atos. Y ¿p o r qué sucede esto? Porque el m étodo F ile W r it e r (se trata de un
constructor) invocado por escribirD atos está definido en la biblioteca de Java así:
1. D ar inform ación a los usuarios de la clase que proporciona este m étodo sobre
las cosas anorm ales que puede hacer el m étodo.
Se puede observar que ahora no hay un bloque catch que intercepte la excep
ción IO E x c e p tio n . Esta form a de proceder obligará a cualquier m étodo que invo
que a escribirD atos a atrapar la excepción. P or ejem plo:
public c la ss C MiC1ase
I
// ...
public v o i d m( i n t a)
I
// ...
if (a — 0)
t h r o w new E V a l o r N o V a l i d o ( " E r r o r : v a l o r c e r o " ) :
/ / ...
I
II...
parám etro a del m étodo m de C M iC lase sea 0; en este caso, el m étodo m lanza
(th ro w ) una excepción de la clase E V alorN oV alido creando un objeto de esta cla
se. Para crear (new ) ese objeto se invoca al constructor E V alorN oV alido pasando
com o argum ento, en este caso, la cadena “ Error: valor cero” .
Si un m étodo m lanza una excepción debe declararlo, para que los usuarios de
C M iC lase, que es quien proporciona el m étodo m , estén inform ados sobre las co
sas anorm ales que puede hacer dicho m étodo.
O tra alternativa es que el propio m étodo que lanza la excepción la atrape, co
m o puede observar en el ejem plo siguiente. Lo que sucede es que escribir un m é
todo que lance una o m ás excepciones y él m ism o las atrape es anticiparnos a las
necesidades que pueda tener el usuario de la clase que proporciona ese m étodo, en
cuanto al tratam iento de la excepción se refiere.
que en la pila de llam adas quedaran otras que pudieran atraparla, no serán tenidas
en cuenta; esto es, sólo se ejecuta el m anejador del m étodo por el que haya pasado
el flujo de control m ás recientem ente.
En este instante tenem os una clase, CM iC lase, cuyo m étodo m declara una
excepción de tipo EV alorN oV alido. Un m étodo de cualquier otra clase que utilice
el m étodo m de esta clase debe detectar esa posible anom alía, de lo contrario el
com pilador Java m ostrará un error. D icho m étodo expresará esa necesidad ence
rrando el código que puede intentar (try ) producir tal anom alía en un bloque try
con un m anejador para esa excepción. Por ejem plo:
T enga en cuenta que un m anejador tiene alcance a las variables locales del
m étodo donde se ha definido pero no a las variables locales al bloque try o a otros
bloques catch.
Según hem os visto, una excepción se atrapa en un bloque catch que declare
un argum ento de su clase o superclase; pero com o lo que se lanza es un objeto,
puesto q ue th ro w especifica a continuación una llam ada al constructor de la clase
de excepción, si necesitáram os transm itir inform ación adicional desde el punto de
lanzam iento al m anejador, lo podem os hacer a través de argum entos en el cons
tructor.
l i d o O :
II . . .
Según lo expuesto anteriorm ente, una vez atrapada una excepción, el m aneja
d o r puede d ecidir volver a lanzarla para q ue sea procesada p o r otro m anejador.
Esto im plica que el m étodo declare que puede lanzar ese tipo de excepción. E n el
ejem plo anterior se puede observar que otroM étodo declara que puede lanzar una
excepción de la clase EValorN oValido.
CA PÍTU LO 11: EX CEPCION ES 4 1 3
P o r últim o, no todas las excepciones tienen que servir para m anipular errores.
Puede tam bién m anejar excepciones que no sean errores.
EJERCICIOS RESUELTOS
1. A ñadir a la aplicación realizada en el capítulo 9 sobre el m antenim iento de una
lista de teléfonos, el código necesario para m anejar la excepción O u t O f M e m o r y -
E r r o r que Jav a lanza cuando un m étodo intenta realizar una asignación dinám ica
de m em oria y no hay disponible un bloque de m em oria del tam año requerido.
C om o ejem plo de tratam iento de este error vam os a añadir a la clase CLis
taTfnos de la aplicación “lista de teléfonos” realizada en el capítulo 9 un m aneja-
dor para esta clase de excepción. C argue esta aplicación, visualice la clase
C ListaTfnos y com pruebe los m étodos que utilizan new para reservar m em oria;
concretam ente, cuando se crea un objeto C ListaTfnos y cada vez que se necesita
aum entar o dism inuir el tam año de la m atriz de objetos C Persona. Para dar solu
ción al problem a propuesto, añadirem os un nuevo m étodo a la clase CListaTfnos
denom inado asigtiarM em oria. E ste m étodo tendrá un parám etro de tipo entero
414 JA V A: C U R SO D E PROGRAM A CIÓN
que se corresponderá con el núm ero de elem entos de la m atriz para los que de
seam os asignar m em oria y devolverá una referencia al nuevo bloque de m em oria
asignado. Si no hay un bloque de m em oria del tam año solicitado, el m anejador de
la excepción O u tO fM e m o ry E rro r visualizará el m ensaje de error predeterm ina
do y devolverá la referencia al bloque de m em oria existente.
publi c C L is t a T f n o s t )
I
// C r e a r una lista vacia
n E l e m e n t o s = 0; _ _ _ _ _ _ _________ __
1istaTeléfonos - asignarM em oria(nElem entos);
i f ( 1 i s t a A c t u a l ( i ] ! - n u i l )
1 i s t a T e l é f o n o s [ k + + ] - 1 i s t a A c t u a l [ i ] ;
n E l e m e n t o s - - ;
I
// ...
2. En el capítulo 5 im plem entam os una clase L eer con los m iem bros siguientes:
M é to d o S ig n ific a d o
dato D evuelve un objeto S t r in g correspondiente a la cadena
tecleada.
datoShort D evuelve el dato de tipo sh o rt tecleado, o el valor
S h o r t . M I N _ V A L U E si el dato tecleado no se corresponde
con un short.
datolnt D evuelve el dato de tipo ¡nt tecleado, o el valor In te -
g e r . M I N _ V A L U E si el dato tecleado no se corresponde
con un int.
datoLong D evuelve el dato de tipo lo n g tecleado, o el valor
L o n g . M I N _ V A L U E si el dato tecleado no se corresponde
con un long.
datoF loat D evuelve el dato de tipo float tecleado, o el valor
F lo a t .N a N si el dato tecleado no se corresponde con un
float.
datoD ouble D evuelve el dato de tipo d o u b le tecleado, o el valor D o u -
b le .N a N si el dato tecleado no se corresponde con un d o u
ble.
U na alternativa al m anejador anterior podría ser otro que ante un dato no vá
lido (por ejem plo: xxx; 3.5; 3,5; etc.) solicitara teclear un dato correcto. H arem os
una excepción para el carácter fin de fichero (C trl+ Z ). En este caso, dato devuel
ve n u il (porque re a d L in e devuelve n u il) señal para que nuestro m étodo devuelva
un valor que sirva para identificar que se pulsó Ctrl+Z; conviene, si es posible,
que este valor no pertenezca al conjunto de valores válidos que se quiera leer. De
esta form a, podrem os u tilizar C trl+ Z com o m arca para finalizar una entrada m a
siva de datos. Según esto, podem os reescribir el m étodo datoShort así:
C om o ejem plo, el siguiente código, utilizando el m étodo anterior, perm ite in
troducir datos de tipo in t hasta pulsar las teclas Ctrl+Z:
S y s t e m . o u t . p r i n t l n ( " I n t r o d u c i r d a t o s . F i n a l i z a r con C t r l + Z ” );
System .out.p rin t("D a to int: ");
w h i l e ( i < 1 00 && ( a [ i ] = L e e r . d a t o l n t l ) ) ! = e o f )
1
i ++ ;
C A PÍTU LO I I: EXCEPCION ES 4 1 7
catchíNumberFormatException e)
I
System .out.p rintí"Ese d a t o no e s válido. Teclee otro: "):
r e t u r n d a t o F l o a t í ):
C om o ejem plo, el siguiente código utilizando el m étodo anterior perm ite in
troducir datos de tipo flo a t hasta pulsar las teclas Ctrl+Z:
S y s t e m . o u t . p r i n t l n ( ” I n t r o d u c i r d a t o s . F i n a l i z a r con C t r l + Z " ) ;
S y s t e m . o u t . p r i n t í " D a t o f l o a t : ” );
w h i l e ( i < 100 && F l o a t . i s N a N ( a [ i ] = L e e r . d a t o F l o a t ( ) ) ! = e o f )
I
i++:
S y ste m .o u t.p r i n t ( "Dato float: ");
I
EJERCICIOS PROPUESTOS
1. Im plem entar los m anejadores para el resto de los m étodos de la clase L eer de
form a análoga a com o se ha hecho en el ejercicio anterior.
2. La clase C Cuenta que im plem entam os en el capítulo 10, tiene un m étodo reinte
g ro que m uestra un m ensaje “ Error: no dispone de saldo” cuando se intenta retirar
una cantidad y no hay suficiente saldo. M odifique esta clase para que el m étodo
reintegro lance una excepción ESaldolnsuficiente.
La solución para hacer que los datos persistan de una ejecución para otra es
alm acenarlos en un fichero en el disco en vez de en una m atriz en m em oria. E n
tonces, cada vez que se ejecute la aplicación que trabaja con esos datos, podrá leer
del fichero los que necesite y m anipularlos. N osotros procedem os de form a aná
loga en m uchos aspectos de la vida ordinaria; alm acenam os los datos en fichas y
guardam os el conjunto de fichas en lo que generalm ente denom inam os fichero o
archivo.
<-
■ X.
420 JA VA : C U R SO D E PROGRAM A CIÓN
n om bre
cam po
d ir e c c ió n teléfono
/
r e g ist ro r e g ist ro
fiel" e r o
---------- /
C ada cam po alm acenará el dato correspondiente. El conjunto de cam pos des
critos form a lo que hem os denom inado registro, y el conjunto de todos los regis
tros form an un fichero que alm acenarem os, por ejem plo, en el disco bajo un
nom bre.
Por lo tanto, para m anipular un fichero que identificam os p o r un nom bre, son
tres las operaciones que tenem os que realizar: ab rir el fichero, escribir o leer re
gistros del fichero y cerrar el fichero. En la vida ordinaria hacem os lo m ism o,
abrim os el cajón que contiene las fichas (fichero), cogem os una ficha (registro)
para leer datos o escribir datos y, finalizado el trabajo con la ficha, la dejam os en
su sitio y cerram os el cajón de fichas (fichero).
P odem os agrupar los ficheros en dos tipos: ficheros de la aplicación (son los
ficheros .java, .class, etc. que form an la aplicación) y ficheros de datos (son los
que proveen de datos a la aplicación). A su vez. Java ofrece dos tipos diferentes
de acceso a los ficheros de datos: secuencial y aleatorio.
Para dar soporte al trabajo con ficheros, la biblioteca de Java proporciona va
rias clases de entrada/salida (E/S) que perm iten leer y escribir datos a, y desde, fi
cheros y dispositivos (en el capítulo 5 trabajam os con algunas de ellas).
CAPÍTULO 12: T R A B A JA R C O N FICHEROS 4 2 1
el p ro g ra m a le e d ato s
( 1 r ^
P ro g ra m a F ic h e ro 1
L J 1 flujo d e e n tra d a
s \
P ro g ra m a ►
flujo d e s a lid a
f F ic h e ro
\
1
► ► ► ► ► ► ► ► ► ► ► ►
el p ro g ra m a e s c rib e d ato s
________ )
Entonces, para que un program a pueda obtener inform ación desde un fichero
tiene que abrir un flujo y leer la inform ación en él alm acenada. A nálogam ente,
para que un program a puede enviar inform ación a un fichero tiene que abrir un
flujo y escribir la inform ación en el mismo.
L os algoritm os para leer y escribir datos son siem pre m ás o m enos los m is
mos:
L eer E s c rib ir
A b rir un flu jo desde un fich ero A b rir un flu jo hacia un fich ero
M ientras ha ya inform ación M ientras haya inform ación
L eer inform ación E scribir inform ación
C errar e l flujo C errar el flujo
Java im plem enta la jerarq u ía de clases p ara la E/S sin incluir dem asiadas ca
pacidades dentro de una clase porque rara vez se necesitan m uchas de ellas al
m ism o tiem po. En cam bio, sí se pueden obtener todas esas capacidades superpo
niendo una clase sobre otra. P or ejem plo, en el capítulo 5 vim os que la clase
In p u tS tre a m no utiliza un buffer, sin em bargo, la clase B u ffe re d ln p u tS tre a m
añade un buffer a la clase In p u tS tre a m .
U n program a que cree un flujo de alguna de estas clases podrá leer o escribir
inform ación en algunos de los m edios especificados: una m atriz en m em oria, un
fichero en el disco o una tubería. U na tubería es un flujo que perm ite com unicar
dos subprocesos para transferencia de inform ación entre uno y otro. V erem os esto
con m ás detalle en el capítulo dedicado a hilos.
import j a v a . i o . * :
p u b lic c la s s Test
I
public static void m a in (S trin g [] args)
I
c h a r [ ] mi = new c h a r [ 8 0 ] ;
c h a r [ ] m2 = new c h a r [ 8 0 3 :
i n t c a r , i = 0:
// A l m a c e n a r d a t o s en l a m a t r i z mi
f o r ( c a r = ’ a ' ; c a r < = 'z ' : c a r + + )
m l[i++] = (char)car;
// A b r i r un f l u j o , f l u j o E , d e s d e l a m a t r i z mi
C h a r A r r a y R e a d e r f l u j o E = new C h a r A r r a y R e a d e r ( m l ):
// A b r i r un f l u j o , f l u j o S . h a c i a una m a t r i z t e m p o r a l
C h a r A r r a y W r i t e r f l u j o S = new C h a r A r r a y W r i t e r ( ) ;
try
I
// L e e r de f l u j o E y e s c r i b i r en f l u j o S
whi l e ( ( c a r = f l u j o E . r e a d ( ) ) I - -1)
■ f 1 u j o S . w r i t e ( c a r );
// C o p i a r en m2 l o s d a t o s e n v i a d o s al flujoS
m2 = f l u j o S . t o C h a r A r r a y ( ) ;
System .out.println(m 2);
I
catch ( I O E x c e p t i o n e)
I
System .out.pri ntln (e .g e tM e ssa ge ()):
I
f i n a l ly
I
II C e r r a r l o s f l u j o s
f l u j o E . c l o s e ( ):
f l u j o S . c l o s e ( );
424 JA VA : C U R SO DE PRO G R A M A CIÓ N
C oncatenación SequencelnputStream
S edación O bjectlnputStrcam
O bjeciO utputStream
C onversión D atalnputStream
(datos) D ataO utputStream
C ontar L ineN um berR eader LineN um berlnputStrcam
U n program a que cree un flujo de alguna de estas clases podrá leer o escribir
inform ación adem ás de ejecutar la operación para la que ha sido diseñado. Por
ejem plo, un flujo de la clase P u s h b a c k R e a d e r (derivada de F ilte r R e a d e r que a
su vez se d eriva R e a d e r) es útil cuando, por ejem plo, un analizador necesita mirar
el siguiente carácter en la entrada con el fin de determ inar qué hacer a continua
ción; para ello, el analizador leerá el carácter y después lo devolverá a la entrada
para que pueda ser leído por el código que tenga que ejecutarse a continuación.
M étodo Significado
cióse C ierra el flujo.
read Lee un único carácter, o bien una m atriz de caracteres.
un re ad D evuelve a la entrada un único carácter, o bien todo o parte
de una m atriz de caracteres.
ready D evuelve true si se puede leer del flujo porque hay carac
teres disponibles; en otro caso devuelve false. Este m étodo,
heredado de la clase R e ad e r, perm ite realizar operaciones
análogas al m étodo a v a ila b le de la clase In p u tStre a m .
C om o ejem plo, vam os a m odificar la últim a versión de la clase L eer que rea
lizam os en el capítulo anterior, con el fin de añadirla algunas capacidades más,
com o m irar cuál es el siguiente carácter en la entrada, leer un solo carácter o lim
piar el flujo de entrada.
Si echam os una ojeada al m étodo dato de la clase L eer observarem os que de
fine un flujo local. Pero los m étodos que ahora vam os a añadir necesitan leer de
ese m ism o flujo; por lo tanto, tendrem os que declararlo com o un m iem bro de la
clase; dicho m iem bro tendrá que ser declarado estático puesto que los m étodos
tam bién son estáticos. R ecuerde que esto lo hicim os así para poder utilizar las ca
pacidades que proporciona la clase L eer sin necesidad de tener que utilizar un
objeto de la misma.
import j a v a . i o . * :
II...
I
InputStreamReader i s r = new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) ;
Finalm ente, una sentencia com o str = L eer.dato() perm itirá leer de flu jo E ca
racteres proporcionados por isr resultantes de la conversión de los bytes que éste
obtiene del origen S ystem .in , según ilustra la figura siguiente:
C A PÍTU LO 12: T R A B A JA R C O N FICHEROS 4 2 7
Para lim piar el flujo de entrada definido por la clase L e e r añadirem os al mé
todo lim piar el código que se m uestra a continuación. L o único que hace este
m étodo es leer caracteres, uno a uno, m ientras haya caracteres disponibles, ya que
cada carácter leído es autom áticam ente elim inado del flujo de entrada.
El m étodo m irar perm itirá conocer cuál es el siguiente carácter que se puede
leer del flujo de entrada. Para ello, este m étodo leerá el prim er carácter disponible
en el flujo y a continuación lo devolverá al m ism o para que esté disponible para
u na siguiente lectura. El m étodo retom ará ese carácter para que quien lo invoque
pueda analizar cuál será el siguiente carácter que se leerá del flujo; si no hubiera
ningún carácter esperará a que el usuario realice una entrada.
try
I
c a r = f 1u j o E . r e a d ( ) ;
f 1u j o E . u n r e a d ( c a r );
1
catchílOException e)
[
System .err,print1n("Error: " + e .g e tM e ssa g e t));
I
return (char)car; // r e t o r n a r e l primer cará cter d is p o n ib le
428 JA VA: C U R SO DE PROGRAM A CIÓN
try
1
car - f 1u j o E . r e a d t );
1
catchtlOException e)
I
Sy ste m .e rr.p rin tln C 'E rro r: " + e .g e tM e ssa g e ()):
1
return (char)car; // d e v o l v e r el dato tecleado
1
El m étodo dato ha sido m odificado para que realice la m ism a función que
realizaba en su versión anterior; esto es, leer una línea de texto que devolverá en
un objeto de la clase S trin g , entendiendo por línea de texto la cadena form ada por
los caracteres que hay hasta encontrar uno de los siguientes: V , 'V f o am bos;
estos caracteres serán leídos pero no alm acenados. E sta nueva versión es com o
consecuencia de que la clase P u s h b a c k R e a d e r no tiene el m étodo re a d L in e c o
m o ocurría con la clase B u ffe re d R e a d e r.
try
I
// L e e r . La e n t r a d a f i n a l i z a al p u l s a r l a t e c l a E n t r a r
w hile ( ( c a r = f l u j o E . r e a d t )) ! - ’ \ r ‘ && c a r ! = - 1 )
s d a t o . a p p e n d ( ( c h a r l e a r );
1 i m p i a r ( );
1
c a t c h t l O E x c e p t i o n e)
I
S y s t e m . e r r . p r i n t l n ( " E r r o r : " + e , g e t M e s s a g e ( 1):
i f (c a r = - -1) re tu rn n u il :
r e t u r n s d a t o . t o S t r i n g t ) ; // d e v o l v e r e l dato tecleado
I
CA PÍTU LO 12: TRA B A JA R CON FIC H ER O S 4 2 9
p u b lic e l a s s Test
i
p u b lic s t a t ic void m a in (S trin g [] args)
I
c h a r c a r = 0, c e r o = ( c h a r ) ' O ' . nueve = ( c h a r ) ' 9 ' .menos = ( c h a r ) ' - ' :
S t r i n g s = n u l 1;
double d = 0.0;
S y s t e m . o u t . p r i n t l " d a t o : ” >:
i f ( ( c a r = L e e r . m i r a r ( ) ) >= c e r o && c a r <= nueve || c a r = menos)
d - L e e r . d a t o D o u b l e í );
el se
s = L e e r , d a t o ( );
if ( s 1= n u i l )
System .out.println(s):
el se
S y s t e m . o u t .p r i n t l n ( d ) :
La prim era vez que se utilice la clase L eer cuando se ejecute la aplicación, se
rá definido el flujo de entrada referenciado p o r su m iem bro flu jo E , que estará dis
ponible hasta que finalice dicha aplicación.
Este tipo de acceso generalm ente se u tiliza con ficheros de texto en los que se
escribe toda la inform ación desde el principio hasta el final y se lee de la misma
forma. En cam bio, los ficheros de texto no son los m ás apropiados para alm acenar
grandes series de núm eros, porque cada núm ero es alm acenado com o una secuen
cia de bytes; esto significa que un núm ero entero de nueve dígitos ocupa nueve
bytes en lugar de los cuatro requeridos para un entero. De ahí que a continuación
se expongan distintos tipos de flujos: de bytes y de caracteres para el tratam iento
de texto, y de datos para el tratam iento de núm eros.
Flujos de bytes
Los datos pueden ser escritos o leídos de un fichero byte a byte utilizando flujos
de las clases F ile O u tp u tS tr e a m y F ile ln p u tS tre a m .
FileOutputStream
F i 1 e O u t p u t S t r e a m t S t r i ng n o m b r e )
F i l e O u t p u t S t r e a m C S t r i n g nombre, b o o l e a n añadir)
F i l e O u t p u t S t r e a m ( F i 1 e fichero)
El siguiente ejem plo es una aplicación Java que lee una línea de texto desde el
teclado y la guarda en un fichero denom inado te.xto.lxt.
4. Escribe explícitam ente la línea de texto en el flujo (im plícitam ente la escribe
en el fichero). Esto se hace cuando el flujo recibe el m ensaje w rite, lo que
origina que se ejecute el m étodo w rite, en este caso con tres parám etros: el
prim ero es una referencia a la m atriz q ue contiene los bytes que deseam os es
cribir, el segundo es la posición en la m atriz del p rim er byte que se desea es
cribir y el tercero, el núm ero de bytes a escribir.
f s . w r i t e í b u f f e r , 0. nbytes);
import j a v a . i o . * :
public c la ss C EscribirB ytes
I
public static v o i d main (StringC] args)
I
FileOutputStream fs = n u il;
b y t e [ ] b u f f e r = new b y t e [ 8 1 ] :
int nbytes:
try
I
S y s t e m . o u t . p r i n 1 1n (
" E s c r i b a e l t e x t o q u e d e s e a a l m a c e n a r en e l fichero:"):
nbytes = S y ste m .in . r e a d (b u f f e r ) ;
f s = new F i l e O u t p u t S t r e a m C t e x t o . t x t " ) :
f s . w r i t e t b u f f e r . 0. n b y t e s ) :
I
catchíIOException e)
I
System .out.p r i n t í n ( " E r r o r : " + e . t o S t r i n g í ));
C uando ejecute la aplicación escriba una línea de texto y pulse la tecla Entrar.
A continuación, en la línea de órdenes del sistem a, teclee type texto.txt en W in
dow s, o bien cat texto.txt en UNIX, para m ostrar el texto del fichero y com probar
que todo ha funcionado com o esperaba.
En este caso, si el fichero no existe se crea y si existe, los datos que se escri
ban en él se añadirán al final.
432 JA V A: C U R SO D E PROGRAM A CIÓN
E s una buena costum bre cerrar un flujo cuando y a no se vaya a utilizar más.
A plicando esta idea en la aplicación anterior, el código quedaría así:
try
I
System .out.printlní
" E s c r i b a e l t e x t o que d e s e a a l m a c e n a r en e l fichero:"):
n b y t e s - S y s t e m . i n . r e a d í b u f f e r ):
f s - new F i 1e O u t p u t S t r e a m í " t e x t o . t x t " ) ;
f s . w r i t e í b u f f e r . 0, n b y t e s ) ;
I
catchíIOException e)
I
System .out.p rintlní"Error: ” + e.t o S t r i n g í )):
finally
I
try
I
// C e r r a r e l f i c h e r o
i f ( f s ! - n u l 1 ) f s . c l o s e í ):
1
c a t c h í I O E x c e p t i o n e)
(
System .out.p rintln ("Error: " + e . t o S t r i n g í )):
try
I
•System .out.printlní
" E s c r i b a e l t e x t o q u e d e s e a a l m a c e n a r en e l fichero:"):
n b y t e s = S y s t e m . i n . r e a d í b u f f e r ):
f s - new F i 1e O u t p u t S t r e a m í “ t e x t o . t x t " ):
f s . w r i t e í b u f f e r . 0, n b y t e s ) :
if (fs !- nu il) fs.c lo se í):
I
II...
A unque esta form a de proceder tam bién es válida no es tan eficiente com o la
anterior, porque ¿qué sucedería si el m étodo w rite lanzara una excepción? N o se
ejecutaría cióse, aunque finalm ente el sistem a se encargaría de cerrar el flujo.
C A PÍTU LO 12: TRA BA JA R CON FICHEROS 433
FilelnputStream
Un flujo de la clase F ile ln p u tS tr e a m perm ite leer bytes desde un fichero. A de
m ás de los m étodos que esta clase hereda de In p u tS tre a m , la clase proporciona
los constructores siguientes:
F i l e l n p u t S t r e a m í S t r i n g nombre)
F i 1 e l n p u t S t r e a m í F i l e fichero)
El siguiente ejem plo es una aplicación Java que lee el texto guardado en el fi
chero texto.txt creado por la aplicación anterior y lo alm acena en una m atriz de
nom inada buffer.
2. D efine un flujo f e desde un fichero denom inado texto.txt. T enga presente que
si el fichero no existe, se lanzará una excepción indicándolo.
3. Lee el texto desde el flujo y lo alm acena en buffer. Esto se hace cuando el
flujo recibe el m ensaje read. lo que origina que se ejecute el m étodo read, en
este caso con tres parám etros: el prim ero es una referencia a la m atriz que al
m acenará los bytes leídos, el segundo es la posición en la m atriz del prim er
byte que se desea alm acenar y el tercero, el núm ero m áxim o de bytes que se
leerán. El m étodo devuelve el núm ero de bytes leídos o -1 si no hay m ás datos
porque se ha alcanzado el final del fichero.
import j a v a . i o . * :
public c la s s CLeerBytes
434 JA VA : C U R SO DE PROGRAM A CIÓN
try
I
f e = new F i l e I n p u t S t r e a m C t e x t o . t x t " ) :
n b y t e s = f e . r e a d ( b u f f e r . 0. 8 1 ) :
S t r i n g s t r = new S t r i n g ! b u f f e r . 0. n b y t e s ) :
System .out.p rin tln (str);
I
catch!IOException e)
I
S y s te m .o u t .p r in t ln ("E r r o r : " + e . t o S t r i n g ! ));
I
f i n a 11 y
I
try
1
// C e r r a r e l f i c h e r o
i f (fe != n u i l ) f e . c i ó s e ! ) :
I
catch! IOException e)
I
System .out.p rintln !"Error: " + e.toString!)):
Clase File
El ejem plo anterior utiliza un S tr in g para referirse al fichero, pero tam bién podría
haber utilizado un objeto de la clase File. U n objeto de esta clase representa el
nom bre de un fichero o de un directorio que puede existir en el sistem a de fiche
ros de la m áquina; por lo tanto, sus m étodos perm itirán interrogar al sistem a sobre
todas las características de ese fichero o directorio. A dem ás de los m étodos a los
que nos referim os, la clase proporciona los constructores siguientes:
public F i l e ( S t r i n g ruta_completa)
public F i l e í S t r i n g ruta. S t r i n g n o m b r e )
public F i l e í F i l e ruta. S t r i n g n o m b r e )
Nombre d e l f i c h e r o : texto.txt
D i r e c t o r i o padre: proyecto
Ruta r e l a t i v a : proyecto\texto.txt
Ruta a b s o l u t a : C:\java\proyecto\texto.txt
El segundo constructor crea un objeto File a partir de una ruta (absoluta o re
lativa) y un nom bre de fichero separado. P or ejem plo, el objeto fic h e ro del ejem
plo anterior podría definirse tam bién así:
El tercer constructor crea un objeto F ile a partir de otro que represente una
ru ta (absoluta o relativa) y un nom bre de fichero separado. P or ejem plo, el objeto
fic h e ro del ejem plo anterior podría definirse tam bién así:
M étodo_____________ Significado______________________________________________
ge tN a m e D evuelve el nom bre del fichero especificado por el objeto
F ile que recibe este m ensaje.
ge tP are nt D evuelve el directorio padre.
g e tP a th D evuelve la ruta relativa del fichero.
g e tA b so lu te P a th D evuelve la ruta absoluta del fichero,
exists D evuelve true si el nom bre especificado p o r el objeto File
que recibe este m ensaje existe.
c a n W r ite r D evuelve true si se puede escribir en el fichero o directorio
especificado por el objeto File.
436 JA VA : CU R SO D E PROGRAM A CIÓN
M étodo Significado
canRead D evuelve true si se puede leer desde el fichero o directorio
especificado por el objeto File.
isFile D evuelve tru e si se trata de un fichero válido.
isD ire cto ry D evuelve true si se trata de un directorio válido.
isH id d c n D evuelve true si se trata de un fichero o directorio oculto.
length D evuelve el tam año del fichero (cuando se trate de un di
rectorio, el valor devuelto es cero).
list D evuelve una m atriz de objetos S t r in g q ue alm acena los
nom bres de los ficheros y directorios que hay en el directo
rio especificado por el objeto File.
m k d ir C rea el directorio especificado p o r el objeto File.
m k d irs C rea el directorio especificado por el objeto F ile incluyen
do los directorios que no existan en la ruta especificada.
delete B orra el fichero o directorio especificado por el objeto File.
C uando se trate de un directorio, éste debe de estar vacío.
d e le te O n E x it Igual que delete. pero cuando la m áquina virtual term ina.
create T e n ip F ile C rea el fichero vacío especificado por los argum entos pa
sados, en el directorio tem poral del sistem a.
re n am e T o R enom bra el fichero especificado por el objeto F ile que re
cibe este m ensaje, con el nom bre especificado p o r el objeto
File pasado com o argum ento.
se tR e a d O n ly M arcar el fichero o directorio especificado por el objeto
File de sólo lectura.
to S trin g D evuelve la ruta especificada cuando se creó el objeto File.
//
S t r i n g nombreFichero = n u i l :
F i l e f i c h e r o = n u l 1:
f e - new F i l e l n p u t S t r e a m ( f i c h e r o ) ;
n b y t e s = f e . r e a d í b u f f e r . 0. 8 1 ) ;
// ...
i
II ...
// ...
Strin g nombrenchero = n u il:
F i l e f i c h e r o = n u l 1;
try
1 _ _____
S y s t e m . o u t . p r i n t t " N o m b r e del f i c h e r o : “ ):
nbytes = Sy ste m .in . re a d ( b u f f e r ) ;
n o m b r e F i c h e r o = new S t r i n g t b u f f e r , 0 , nbytes-2); // menos C R+ L F
f i c h e r o = new F i 1e ( n o m b r e F i c h e r o ) ;
if (resp == ’ s *)
!
System .out.printlní
" E s c r i b a e l t e x t o q u e d e s e a a l m a c e n a r en e l f i c h e r o : ” ):
nbytes = S y ste m .in .r e a d (b u f f e r ) ;
f s = new F i 1 e O u t p u t S t r e a m ( f i c h e r o ) :
f s . w r i t e ( b u f f e r , 0. n b y t e s ) :
I
I
II...
Flujos de caracteres
Una vez que sabem os trabajar con flujos de bytes, hacerlo con flujos de caracteres
es prácticam ente lo m ism o. Esto nos será útil cuando necesitam os trabajar con
texto representado por un conjunto de caracteres A SC II o U nicode. Las clases que
definen estos flujos son subclases de R e ad e r, com o F ile W r if e r y F ileR ead er.
438 JA V A: C U R SO D E PROGRAM A CIÓN
FileW riter
F i 1e W r i t e r ( S t r i n g nombre)
F i l e W r i t e r t S t r i n g nombre, b o o l e a n añadir)
F i 1 e W r i t e r ( F i 1 e fichero)
import ja v a .i o . * ;
b y t e [ ] b u f f e r = new b y t e [ 8 1 ] ;
int nbytes;
S t r i n g nombreFichero = n u i l ;
F ile fichero = n u il;
try
if (resp == ’ s *)
I
System .out.printlní
" E s c r i b a e l t e x t o que d e s e a a l m a c e n a r en e l f i c h e r o : ” ):
n b y t e s = S y s t e m . i n . r e a d ( b u f f e r );
S t r i n g s t r - new S t r i n g ( b u f f e r . 0, n b y t e s ) :
f s = new F i l e W r i t e r ( f i c h e r o ) ;
f s . w r i t e í s t r . 0. s t r .1e n g t h í ) ) ;
catchíIOException e)
1
S y s t e m .o u t . p r i n t l n i " E r r o r : ” + e . t o S t r i n g ( ) ) ;
)
f i n a 11 y
1
try
I
// C e r r a r e l f i c h e r o
i f ( f s != n u i l ) f s . c l o s e í );
1
c a t c h í I O E x c e p t i o n e)
[
S y s t e m . o u t . p r i n t í n i " E r r o r : " + e . t o S t r i n g í ));
FileReader
U n flujo de la clase F ile R e a d e r perm ite leer caracteres desde un fichero. Adem ás
de los m étodos que esta clase hereda de R e a d e r, la clase proporciona los cons
tructores siguientes:
F i 1e R e a d e r í S t r i n g n o m b r e )
F i 1e R e a d e r í F i 1e f i c h e r o )
import j a v a . i o . * ;
440 JA V A: C U R S O DF. PROGRAM A CIÓN
try
I
do
I
S y s t e m . o u t . p r i n t ( " N o m b r e del f i c h e r o : " ) ;
n b y t e s = S y s t e m . i n . r e a d ( n o m F i c h ):
no m b r e F i c h e r o - new S t r i n g ( n o m F i c h , 0. n b y t e s - 2 ) ; // menos CR+LF
f i c h e r o - new F i l e ( n o m b r e F i c h e r o ) :
1
w h i 1e ( ! f i c h e r o . e x i s t s ( ) ) ;
f e - new F i 1 e R e a d e r ( f i c h e r o ) :
n c a r s - f e . r e a d ( b u f f e r , 0. 8 1 ) :
S y s t e m . o u t . p r i n t l n ( b u f f e r );
I
catch(IOException e)
[
System .out.p rintln!"Error: " + e . t o S t r i n g ( ));
I
finally
I
try
I
// C e r r a r e l f i c h e r o
i f ( f e ! = n u i l ) f e . c l o s e ( ):
I
catchíIOException e)
I
S y s t e m .o u t .p r in t ln ("E r r o r : " + e .t o S t r i n g ( )):
I
I
I
Flujos de datos
Seguram ente, en alguna ocasión desearem os escribir en un fichero datos de tipos
prim itivos (boolean. byte, double, float, long, in t y sh o rt) para posteriorm ente
C A P ÍT U L O 12: TRA BA JA R CON FIC H ER O S 4 4 1
recuperarlos com o tal. Para estos casos, el paquete ja v a .io proporciona las clases
D a ta ln p u tS tr e a m y D a ta O u tp u tS tr e a m , las cuales perm iten leer y escribir, res
pectivam ente, datos de cualquier tipo prim itivo. Entonces, ¿por qué no se han
analizado previam ente? Pues, sim plem ente porque no pueden utilizarse con los
dispositivos A SC II de E/S estándar. Un flujo D a ta ln p u tS tr e a m sólo puede leer
datos alm acenados en un fichero a través de un flujo D a ta O u tp u tS tr e a m .
O bserve que los flujos de estas clases actúan com o filtros; esto es, los datos
obtenidos del origen o enviados al destino son transform ados m ediante alguna
operación; en este caso, sufren una conversión a un form ato portable (UTF-8:
U nicode ligeram ente m odificado) cuando son alm acenados y viceversa cuando
son recuperados. El procedim iento para utilizar un filtro es básicam ente así:
DataOutputStream
T odos los m étodos proporcionados por esta clase están definidos en la inter
faz D a ta O u tp u t im plem entada p o r la m ism a.
V eam os un ejem plo. Las siguientes líneas de código definen un filtro que
perm itirá escrib ir datos de tipos prim itivos en un fichero datos.dat:
Un program a que quiera alm acenar datos en el fichero datos.dat, escribirá ta
les datos en el filtro dos, que a su vez está conectado al flujo fo s abierto hacia ese
fichero. La figura siguiente m uestra de form a gráfica lo expuesto:
El siguiente fragm ento de código m uestra cóm o utilizar el filtro anterior para
alm acenar los datos nom bre, dirección y teléfono en un fichero especificado por
nom bre Fichero:
442 JA V A: C U R SO DE PROGRAM A CIÓN
F i l e O u t p u t S t r e a m f o s = new F i l e O u t p u t S t r e a m ( n o m b r e F i c h e r o ) :
D a t a O u t p u t S t r e a m d o s = new D a t a O u t p u t S t r e a m ( f o s );
// A l m a c e n a r e l nombre l a d i r e c c i ó n y el t e l é f o n o en e l f i c h e r o
d o s.w rit e U T F í"u n nombre");
d o s . w r i t e U T F ( " u n a d i r e c c i ó n ” );
dos.w ri te Lo ng(942334455):
dos.closeO : fos.closeO ;
M étodo D escripción
w rite B oole an Escribe un valor de tipo boolean.
w riteByte Escribe un valor de tipo byte.
w rite Byte s Escribe un S t r in g com o una secuencia de bytes.
w rite C h a r Escribe un valor de tipo char.
w r ite C h a rs Escribe un S t r in g com o una secuencia de caracteres.
w rite S h o rt Escribe un valor de tipo short.
w rite ln t E scribe un valor de tipo int.
w r ite L o n g Escribe un valor de tipo long.
w rite Float E scribe un valor de tipo float.
w rite D o u b le E scribe un valor de tipo double.
w r ite U F T Escribe una cadena de caracteres en form ato U TF-8; los
dos prim eros bytes especifican el núm ero de bytes de da
tos escritos a continuación.
DatalnputStream
T odos los m étodos proporcionados por esta clase están definidos en la inter
faz D a t a ln p u t im plem entada p o r la m ism a.
V eam os un ejem plo. Las siguientes líneas de código definen un filtro que
perm itirá leer datos de tipos prim itivos desde un fichero datos.dat:
F i 1e l n p u t S t r e a m f i s = new F i 1e ! n p u t S t r e a m ( “d a t o s . d a t " );
DatalnputStream d i s = new D a t a I n p u t S t r e a m ( f i s ) :
Un program a que quiera alm acenar datos en el fichero datos.dat, escribirá ta
les datos en el filtro dis. que a su vez está conectado al flujo f i s abierto desde ese
fichero. La figura siguiente m uestra de form a gráfica lo expuesto:
CA PÍTU LO 12: T R A B A JA R C O N FIC H ER O S 4 4 3
El siguiente fragm ento de código m uestra cóm o utilizar el filtro anterior para
leer los datos nom bre, dirección y teléfono desde un fichero especificado por
nom breFichero:
Fi 1e I n p u t S t r e a m f i s = new F i l e l n p u t S t r e a m ( n o m b r e F i c h e r o ) :
D a t a l n p u t S t r e a m d i s = new D a t a I n p u t S t r e a m ( f i s ):
// L e e r e l nombre l a d i r e c c i ó n y e l t e l é f o n o d e l f i c h e r o
nombre = d i s . r e a d U T F í );
d i r e c c ió n = d i s .readUTF():
t e l é f o n o = d i s . r e a d L o n g í ):
dis.close O ; fis.close O ;
M étodo D escripción
re a d B o o le a n D evuelve un valor de tipo boolean.
read B yte D evuelve un valor de tipo byte.
re a d Sh o rt D evuelve un valor de tipo short.
read C har D evuelve un v alo r de tipo char.
re a d ln t D evuelve un valor de tipo int.
read Long D evuelve un valor de tipo long.
read F loat D evuelve un valor de tipo float.
re a d D o u b le D evuelve un valor de tipo double.
re a d U F T D evuelve una cadena de caracteres en form ato U TF-8; los
dos prim eros bytes especifican el núm ero de bytes de da
tos que serán leídos a continuación.
• A brim os un flujo desde el fichero del cual querem os leer los datos.
• Leem os los datos del fichero y los alm acenam os en variables de nuestro pro
gram a con el fin de trabajar con ellos. Este proceso se hace norm alm ente re
gistro a registro. Para ello, utilizarem os los m étodos proporcionados por la
interfaz del flujo.
• C erram os el ñujo.
El siguiente ejem plo lee de la entrada estándar grupos de datos (registros) de
finidos de la form a que se indica a continuación y los alm acena en un fichero.
S t r i n g n o mbr e , dirección:
long teléfono:
Para realizar este ejem plo, escribirem os una clase aplicación C rearListaTfnos
con dos m étodos: crearF ichero y m ain.
El m étodo crearF ichero recibe com o parám etro un objeto File que define el
nom bre del fichero que se desea crear y realiza las tareas siguientes:
• C rea un flujo hacia el fichero especificado por el objeto F ile que perm ite es
cribir datos de tipos prim itivos utilizando un buffer.
• Lee grupos de datos nom bre, dirección y teléfono de la entrada estándar y los
escribe en el fichero.
• Si durante su ejecución alguno de los m étodos invocados lanza una excep
ción. la vuelve a lanzar para que sea atrapada por el m étodo que le invocó.
• C rea un objeto F ile a partir del nom bre del fichero leído desde la entrada es
tándar.
• V erifica si el fichero existe.
C A PÍTU LO 12: TRA BA JA R C O N FIC H ER O S 4 4 5
import j a v a . i o . * ;
public c la ss CrearListaTfnos
I
pu b lic s t a t ic void c r e a r F ic h e r o íF ile fichero)
throws IOException
I
P r i n t S t r e a m f l u j o S = S y s t e m . o u t : // s a l i d a e s t á n d a r
D a t a O u t p u t S t r e a m d o s = n u i l : / / s a l i d a de d a t o s h a c i a el fichero
char resp;
try
I
// C r e a r un f l u j o h a c i a e l f i c h e r o q u e p e r m i t a e s c r i b i r
// d a t o s de t i p o s p r i m i t i v o s y q u e u t i l i c e un b u f f e r .
d o s = new D a t a O u t p u t S t r e a m í n e w B u f f e r e d O u t p u t S t r e a m í
new F i l e O u t p u t S t r e a m t f i c h e r o ) ) ) :
// D e c l a r a r l o s d a t o s a e s c r i b i r en e l fichero
S t r i n g n o mb r e , d i r e c c i ó n :
long te léfo n o:
// L e e r d a t o s de l a entrada estándar y e s c r ib ir lo s
// en e l f i c h e r o
do
1
f l u j o S . p r i n t í "nombre: "): no mb r e = L e e r . d a t o O :
f 1u j o S . p r i n t ( " d i r e c c i ó n : " ) : dirección - Leer.datoO :
flu joS.printí"teléfono: "); t e l é f o n o = L e e r . d a t o L o n g í );
// A l m a c e n a r un n o mb r e , una d i r e c c i ó n y un t e l é f o n o en
// e l f i c h e r o
dos.wri teUTFínombre);
dos.w riteUTFídi re c c ió n ) :
dos.w riteLongí t e lé f o n o ) :
if (dos != n u i l ) dos.closet);
try
I
// C r e a r un o b j e t o F i l e q u e i d e n t i f i q u e al fichero
f l u j o S . p r i n t t " N o m b r e del f i c h e r o : " ) ;
nombreFichero = L e e r . d a t o O ;
f i c h e r o = new F i 1e ( n o m b r e F i c h e r o ) ;
// V e r i f i c a r s i e l f i c h e r o existe
char resp = ' s ’ ;
i f ( f i c h e r o . e x i s t s ( ))
I
f l u j o S . p r i n t C E l f i c h e r o e x i s t e ¿ des ea s o b r e e s c r i b i r i o ? (s/n) " ) ;
r e s p = L e e r . c a r á c t e r ( );
L e e r . 1 i mpi a r ( ) ;
I
if (resp == 's ')
[
crearFichero(fichero);
I
I
catch(IOException e)
1
f l u j o S . p r i n t í n ( " E r r o r : " + e.getM essaget));
El m étodo m ostrarF ichero recibe com o parám etro un objeto S tr in g que al
m acena el nom bre del fichero que se desea leer y realiza las tareas siguientes:
• Lee un grupo de datos nom bre, dirección y teléfono desde el fichero y los
m uestra. C uando se alcance el final del fichero Java lanzará una excepción del
tipo E O F E x c e p tio n , instante en el que finalizará la ejecución de este método.
• Si durante su ejecución alguno de los m étodos invocados lanza una excepción
IO E x c e p tio n . la pasa para que sea atrapada por el m étodo que le invocó.
El m étodo m a in recibe com o parám etro el nom bre del fichero que se desea
crear y realiza las tareas siguientes:
import j a v a . i o . * ;
try
I
// C r e a r un o b j e t o F i l e q u e i d e n t i f i q u e al fichero
f i c h e r o = new F i l e t n o m b r e F i c h e r o ) ;
// V e r i f i c a r s i e l f i c h e r o existe
i f ( f i c h e r o . e x i s t s t ))
1
// S i e x i s t e , a b r i r un f l u j o d e s d e e l mismo
d i s = new D a t a I n p u t S t r e a m l n e w B u f f e r e d I n p u t S t r e a m t
new F i l e l n p u t S t r e a m ( f i c h e r o ) ) ) ;
// D e c l a r a r l o s d a t o s a l e e r desde el fichero
S t r i n g n o mb r e , d i r e c c i ó n :
long te léfono:
do
I
// L e e r un n o mbr e , una d i r e c c i ó n y un t e l é f o n o d e s d e el
// f i c h e r o . C u a n d o s e a l c a n c e e l f i n a l d e l f i c h e r o J a v a
// l a n z a r á una e x c e p c i ó n d e l t i p o E O F E x c e p t i o n .
nombre = d i s . r e a d U T F ( ) :
d i r e c c i ó n = d i s . r e a d U T F ( );
448 JA VA: C U R SO DE PROGRAM A CIÓN
t e l é f o n o = d i s . r e a d L o n g í );
SERIACIÓN DE OBJETOS
En el apartado anterior hem os aprendido cóm o escribir y leer grupos de datos a y
desde un fichero. Pero en un desarrollo orientado a objetos debem os pensar en
objetos; por lo tanto, ese grupo de datos al que nos hem os referido no lo tratare-
C A PÍTU LO 12: T R A B A JA R CON FICHEROS 4 4 9
P ara poder seriar los objetos de una clase, ésta debe de im plem entar la inter
faz Se rializable. Se trata de una interfaz vacía; esto es, sin ningún m étodo; su
propósito es sim plem ente identificar clases cuyos objetos se pueden seriar.
El siguiente ejem plo, define la clase C Persona com o una clase cuyos objetos
se pueden seriar:
import j a v a . i o . * ;
C om o la interfaz S e ria liz a b le está vacía no hay que escribir ningún m étodo
extra en la clase.
import j a v a . i o . * :
// S e u t i l i z a t a m b i é n la clase Leer, m odificada en e s t e c a p i t u l o
public c la ss CrearListaTfnos
I
p u b l i c s t a t i c v o i d c r e a r F i c h e r o ( F i 1e f i c h e r o )
th rows lO E x c e p t io n
• I
P r i n t S t r e a m f l u j o S = S y s t e m . o u t ; // s a l i d a e s t á n d a r
O b j e c t O u t p u t S t r e a m o o s = n u i l ; / / s a l i d a de d a t o s h a c i a e l fichero
char resp:
CA PÍTU LO 12: TR A B A JA R CON FICHEROS 451
try
[
// C r e a r un f l u j o h a c i a e l f i c h e r o q u e p e r m i t a e s c r i b i r
// o b j e t o s y d a t o s de t i p o s p r i m i t i v o s .
o o s = new O b j e c t O u t p u t S t r e a m ( n e w F i 1 e O u t p u t S t r e a m í f i c h e r o ) );
// D e c l a r a r l o s d a t o s a e s c r i b i r en e l f i c h e r o
S t r i n g n o mbr e , d i r e c c i ó n ;
long te léfo n o;
// L e e r d a t o s de l a entrada estándar y e s c r ib ir lo s
// en el f i c h e r o
do
I
flujoS.printí"nom bre: ” ); nombre = L e e r . d a t o í ) :
flu jo S .p rin t( "d irección: "); dirección = Leer.datoí):
flu j o S . p r in t ( "teléfono: "); t e l é f o n o = L e e r . d a t o L o n g í );
// C r e a r un o b j e t o C P e r s o n a y a l m a c e n a r l o en e l fichero
oos.w riteO bje ctíne w CPersonaínombre, d ire c c ió n , teléfono));
/ / ...
I
w h i 1e ( r e s p = = ' s '):
/ / ...
S t r i n g s t r = ( S t r i n g ) o i s . r e a d U T F Í );
C P e r s o n a p e r s o n a = ( C P e r s o n a ) o i s . r e a d O b j e c t í );
o i s . c l o s e í );
452 JA VA : C U R SO DE PRO G R A M A CIÓ N
import j a v a . i o . * ;
public c la ss M ostrarListaTfnos
I
p u b lic s t a t i c void mostrarl;i c h e r o ( S t r i n g nombreFichero)
th rows IO E x c e p tio n
I
P r i n t S t r e a m f l u j o S - S y s t e m . o u t ; // s a l i d a e s t á n d a r
O b j e c t l n p u t S t r e a m o i s - n u i l : / / e n t r a d a d e d a t o s d e s d e el f i c h e r o
F ile fichero = nu il: // o b j e t o q u e i d e n t i f i c a e l f i c h e r o
// C r e a r un o b j e t o F i l e q u e i d e n t i f i q u e al fichero
f i c h e r o = new F i 1e ( n o m b r e F i c h e r o ):
// V e r i f i c a r s i e l f i c h e r o existe
i f ( f i c h e r o . e x i s t s ( ))
I
// S i e x i s t e , a b r i r un f l u j o d e s d e e l mismo
o i s - new O b j e c t I n p u t S t r e a m ( n e w F i 1 e I n p u t S t r e a m ( f i c h e r o ) ) :
// D e c l a r a r l o s d a t o s a l e e r d e s d e e l f i c h e r o
CPersona persona:
S t r i n g n o mb r e , d i r e c c i ó n :
long teléfono:
do
I
// L e e r un o b j e t o C P e r s o n a d e s d e e l f i c h e r o . Cuando se
// a l c a n c e e l f i n a l d e l f i c h e r o J a v a l a n z a r á una
// e x c e p c i ó n d e l t i p o E O F E x c e p t i o n .
p e r s o n a = ( C P e r s o r i a ) o i s . r e a d O b j e c t ( ):
// ...
I
w hile (true):
I
el se
f l u j o S . p r i n t l n ( "El f i c h e r o no e x i s t e " ) :
I
c a t c h ( E O F E x c e p t 'i o n e)
1
f 1u j o S . p r i n t l n ( " F i n del listado"):
c a t c h ( C l a s s N o t F o u n d E x c e p t i o n e)
1
f l u j o S . p r i n t l n ( " E r r o r : " + e .g e t M e s s a g e í ));
C A P ÍT U L O 12: TRA B A JA R C O N FIC H ER O S 4 5 3
fi nally
I
// C e r r a r e l f l u j o
i f ( o í s != n u i l ) o i s . c l o s e O ;
import j a v a . i o . * :
public c la ss CListaTfnos i m p l e m e n t s S e r i a l i z a b l e
1
// ...
I
Pero seriar un objeto C ListaTfnos im plica seriar los objetos C Persona refe
renciados por el prim ero. P or lo tanto, esta segunda clase tam bién tiene que im-
plem entar la interfaz S erializab le:
import j a v a . i o . * :
p u b lic c l a s s CPersona i m p l e m e n t s S e r i a l i z a b l e
I
// ...
I
D espués de realizar estas m odificaciones, sólo queda cam biar la clase aplica
ción Test para que guarde la lista, sólo si ha sido m odificada, en un fichero deno
m inado “ listatfnos.dat” cuando la aplicación finalice, y la recupere cuando la
aplicación se inicie. A continuación se m uestra el código com pleto de la clase
Test, en el que se resaltan los añadidos m ás im portantes que se han realizado:
import j a v a . i o . * :
/////////////////////////////////////////////////////////////////
// A p l i c a c i ó n l i s t a d e t e l é f o n o s . C u a n d o l a a p l i c a c i ó n f i n a l i z a
// l a l i s t a e s s a l v a d a en un f i c h e r o " l i s t a t f n o s . d a t " y c u a n d o
// s e i n i c i a s e r e c u p e r a de e s e f i c h e r o .
//
public class Test
I
public static i n t menúí)
I
S y s t e m . o u t . p r i n t t " \ n \ n ” );
S y s t e m . o u t . p r i n t l n í "1. B u s c a r") :
S y s t e m . o u t . p r i n t l n ( ’ 2. B u s c a r s i g u i e n t e " ) :
S y s t e m . o u t . p r i n t l n í "3. A ñ a d ir ") ;
S y s t e m . o u t . p r i n t l n i " 4 . E l i m i n a r " ):
S y s t e m . o u t . p r i n t l n í "5. S a l i r " ) :
S y s t e m . o u t . p r i n t l n í ):
System .out.p rintí’ Opción: " ) :
i n t op:
do
op - L e e r . d a t o l n t í ):
w h i 1e ( o p < 1 || op > 5 ) :
r e t u r n op:
)
// D e f i n i r un f l u j o de c a r a c t e r e s de e n t r a d a : f l u j o E
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r í S y s t e m . i n ) :
B u f f e r e d R e a d e r f l u j o E = new B u f f e r e d R e a d e r í i s r );
// D e f i n i r una r e f e r e n c i a a l f l u j o e s t á n d a r de s a l i d a : f l u j o S
PrintStream f lu j o S = System.out;
CL1 s t a T f n o s l i s t a t f n o s 2
i n t o p c i ó n - 0 . p o s = - 1;
S t r in g cadenabuscar = nu il
S t r i n g n o mbr e , d i r e c c i ó n :
long teléfono;
boolean e lim ina d o = f a l s e ;
false:
try
// C r e a r un o b j e t o l i s t a de t e l é f o n o s v a c i o ( c o n 0 e l e m e n t o s )
// o c o n e l c o n t e n i d o d e l f i c h e r o l i s t a t f n o s . d a t s i e x i s t e .
F i l e f i c h e r o - new F i 1e ( "1 i s t a t f n o s . d a t " );
if ( ¡fiche ro.e x istsO )
i
l i s t a t f n o s - new C L i s t a T f n o s ( ) :
f l u j o S . p r i n t l n í " S e ha c r e a d o una l i s t a de t e l é f o n o s n u e v a " ) :
1
else
I
O b j e c t l n p u t S t r e a m o i s = new O b j e c t l n p u t S t r e a m í
new F i l e l n p u t S t r e a m ( "1 i s t a t f n o s . d a t " ) ) :
lista tfn o s - (CListaTfnos)ois.readO bjectO ;
o i s . c l o s e í ):
f l u j o S . p r i n t l n í " S e c a r g ó l a l i s t a de t e l é f o n o s c o n é x i t o " ) :
1
do
í
opción - m e n ú í ):
switch (opción)
I
c a s e 1: // b u s c a r
f l u j o S . p r i n t í " c o n j u n t o de c a r a c t e r e s a b u s c a r "):
c a d e n a b u s c a r = f 1 u j o E . r e a d L i n e í ):
pos - 1 i s t a t f n o s . b u s c a r í c a d e n a b u s c a r . 0 ) ;
i f (pos — -1)
i f (1 i s t a t f n o s . 1 o n g i t u d í ) ! - 0 )
f l u j o S . p r i n t í n ( " b ú s q u e d a f a l l i d a " );
el se
f lu jo S . p r in t ln ( * lis t a vacia"):
else
456 JA V A : C U R SO D E PRO G R A M A CIÓ N
f l u j o $ . p r i n t l n ( 1 i s t a t f n o s . v a l o r E n ( p o s ) . obten er No mbr e * ) ) ;
f l u j o S . p r i n t l n ( l i s t a t f n o s . v a l o r E n ( p o s ) . o b t e n e r D i r e c c i 6 n ( )):
f l u j o S . p r i n t l n d i s t a t f n o s . v a l o r E n ( p o s ) . o b t e n e r T e l é f o n o ( )):
1
break:
c a s e 2: // b u s c a r s i g u i e n t e
pos - 1i s t a t f n o s . b u s c a d c a d e n a b u s c a r . pos + l):
i f (pos — -1)
i f (1 i s t a t f n o s . 1 o n g i t u d ( ) ! - 0)
f 1u j o S . p r i n t l n ( " b ú s q u e d a f a l 1 i d a " ):
el se
flu jo S . p r in t ln ( "1 ista v a c ia ');
el se
I
flu jo S . p rin t ln d is ta t fn o s . v a lo rE n (p o s ) . o b te n e rN o m b re ( )):
f l u j o S . p r i n t l n ( 1i s t a t f n o s . v a l o r £ n ( p o s ) . o b t e n e r D ir e c c i ó n ( )):
f l u j o S . p r i n t l n d i s t a t f n o s . v a l o r E n ( p o s ) . o b t e n e r T e l é f o n o ( )):
1
break:
c a s e 3: // a ñ a d i r
flujoS.printí"nombre: " ) : nombre - f l u j o E . r e a d L i n e ( ):
f l u j o S . p r i n t d d i r e c c i ó n : ” ); d i r e c c i ó n - f l u j o E . r e a d L i n e ( ):
flujoS.print("teléfono: " ) ; t e l é f o n o - L e e r . d a t o L o n g ( );
l i s t a t f n o s . a ñ a d i r ( n e w CPersona( nombre, d i r e c c i ó n , t e l é f o n o ) ) :
lista M o d ific a d a - true:
break:
c a s e 4: // e l i m i n a r
f l u j o S . p r i n t ( " t e l é f o n o : " ) : t e l é f o n o - L e e r , d a t o L o n g ( ):
e lim in a d o - 1i s t a t f n o s . e l i m i n a r ( t e l é f o n o ) :
i f (elim inado)
i
f l u j o S . p r i n t l n ( " r e g i s t r o e l i mi n a d o " ) :
lista M o d ifica d a - true:
I
el se
if ( l i s t a t f n o s . l o n g i t u d í ) ! — 0)
f l u j o S . p r i n t l n ( " t e l é f o n o no e n c o n t r a d o " ) :
el s e
f lu j o S . p r in t ln C li s t a vacia"):
break;
c a s e 5: // s a l i r
// g u a r d a r 1 i s t a
i f (listaM odificada)
I
O b j e c t O u t p u t S t r e a m o u s - new O b j e c t O u t p u t S t r e a m (
new F i 1 e O u t p u t S t r e a m í "1 i s t a t f n o s . d a t " ) ) :
ous.w riteO bjectd is t a t f n o s ) ;
ous.closeO :
I
C A PÍTU LO 12: TRA BA JA R C O N FICHEROS 4 5 7
1 i s t a t f n o s = n u l 1:
I
I
w hile(opción != 5):
1
catch { IOException e )
[
System .out.p rintlnCError: " + e .g e t M e s s a g e ( ));
)
catch ( C l a s s N o t F o u n d E x c e p t i o n e)
[
S y s t e m . o u t . p r i n t l n í “E r r o r : " + e .g e tM e ssa g e ()):
Un fichero accedido aleatoriam ente es com parable a una m atriz. En una ma
triz para acceder a uno de sus elem entos utilizam os un índice. En un fichero acce
dido aleatoriam ente el índice es sustituido por un puntero de lectura o escritura
(L/E). Dicho puntero es situado autom áticam ente al principio del fichero cuando
éste se abre para leer y/o escribir. P or lo tanto, una operación de lectura o de es
critura com ienza en la posición donde esté el puntero dentro del fichero; final
mente, su posición coincidirá ju sto a continuación del últim o byte leído o escrito.
La clase RandomAccessFile
Un flujo de esta clase perm ite acceder directam ente a cualquier posición dentro
del fichero vinculado con él.
458 JA V A : C U R S O D E P R O G R A M A C IÓ N
R a n d o m A c c e s s F i l e ( S t r i n g n o m b r e - f i c h e r o . S t r i n g modo)
R a n d o m A c c e s s F i 1e ( F i 1e o b j e t o - F i l e . S t r i n g modo)
M odo Significado
r read. S ólo se perm iten realizar operaciones de lectura.
rw read/w rite. Se pueden realizar operaciones de lectura y de
escritura sobre el fichero.
Por ejem plo, el siguiente fragm ento de código construye un objeto F ile para
verificar si el nom bre especificado para el fichero existe com o tal. Si existe y no
corresponde a un fichero se lanza una excepción; si existe y se trata de un fichero,
se crea un flujo para escribir y leer a y desde ese fichero; y si no existe, tam bién
se crea el flujo y el fichero.
F i l e f i c h e r o - new F i l e C l i s t a t f n o s . d a t " ) :
i f ( f i c h e r o . e x i s t s ( ) && ! f i c h e r o . i s F i l e ( ) )
t h r o w new I O E x c e p t i o n ( f i c h e r o . g e t N a m e ( ) + ’ no e s un f i c h e r o ” ) :
R a n d o m A c c e s s F i l e l i s t a T e i é f o n o s - new R a n d o m A c c e s s F i l e í f i c h e r o . ” rw“ ):
gativos. El desplazam iento requerido puede ir m ás allá del final del fichero; esta
acción no cam bia la longitud del fichero; la longitud del fichero sólo cam biará si a
continuación, realizam os una operación de escritura.
Con esta clase no tenem os posibilidad de seriar objetos. Los datos deben
guardarse uno a uno utilizando el m étodo adecuado de la clase según su tipo. Por
ejem plo, las siguientes líneas de código escriben en el fichero “datos” a partir de
la posición d esp , los atributos no m b re, dirección y teléfono relativos a un objeto
CPersona:
CPersona o b j e to :
II...
R a n d o m A c c e s s F i l e f e s = new R a n d o m A c c e s s F i l e ( " d a t o s " . " r w " ) ¡
f e s .seek(desp);
fe s.w rite U T F ío b je to .o b te n e rN o m b re í)):
fes.w ri teUTFlobjeto.obtenerDi r e c c i ó n í ));
f e s . w r i t e L o n g ( o b j e t o . o b t e n e r l e 1é f o n o ( ) ) :
La clase CPersona
La clase C P ersona sólo se ve m odificada p o r el hecho de haber añadido un m éto
d o denom inado tam año que devuelve la longitud en bytes correspondiente a los
atributos de un objeto C Persona.
11111111111111111111111111111111111111111111111111111111111111111
II D e f i n i c i ó n de l a c l a s e CPersona
II
public c la ss CPersona
I
II A t r i b u t o s
p r i v a t e S t r i n g n o mbr e :
private S trin g dirección:
p riv a t e long teléfono:
// M é t o d o s
public CPersona() 11
public void a s i g n a r T e l é f o n o í 1o n g t e l )
I
teléfono = t e l :
)
public long o b te n e r T e lé fo n o í)
I
return teléfono:
I
La clase CListaTfnos
La interfaz de la clase C ListaTfnos será prácticam ente la m ism a. D e esta form a un
usuario no diferenciaría si está trabajando con una m atriz o con un fichero, ex
cepto en que ahora, al utilizar un fichero, los datos persisten de una ejecución a
otra de la aplicación.
Constructor CListaTfnos
C uando desde algún m étodo se cree un objeto C ListaTfnos ¿qué esperam os que
ocurra? L ógicam ente que se cargue la lista de teléfonos especificada, o bien que
se cree una nueva cuando el fichero especificado no exista. P or ejem plo:
F i l e f i c h e r o = new F i 1 e ( "1 i s t a t f n o s . d a t ” ):
C L i s t a T f n o s l i s t a t f n o s = new C L i s t a T f n o s ( f i c h e r o ) ;
/////////////////////////////////////////////////////////////////
// D e f i n i c i ó n de l a clase CListaTfnos.
//
import j a v a . i o . * :
pu blic c la ss CListaTfnos
private RandomAccessFile fe s : // f l u j o
private int nregs: // nú me r o d e r e g i s t r o s
private i n t tamañoReg - 140 ; // t a m a ñ o d e l r e g i s t r o en b y t e s
public v o i d c e r r a r O th rows IO E x c e p t io n I f e s . c l o s e O : 1
public i n t l o n g i t u d O 1 r e t u r n n r e g s : I // nú mero de r e g i s t r o s
// ..
// ...
II . ..
El m étodo p o n erV alorE n se ha diseñado para que perm ita escribir los atributos de
un objeto C Persona dentro del fichero a partir de una posición determ inada. Tiene
dos parám etros: el prim ero indica el núm ero de registro que se desea escribir, que
puede coincidir con un registro existente, en cuyo caso se sobreescribirá este úl
tim o, o bien con el núm ero del siguiente registro que se puede añadir al fichero; y
el segundo, hace referencia al objeto C Persona cuyos atributos deseam os escribir.
El m étodo devolverá un valor true si se ejecuta satisfactoriam ente y false en otro
caso.
Se observa que lo prim ero que hace el m étodo es verificar si el núm ero de re
gistro es válido (cuando i sea igual a nregs es porque se quiere añadir un registro
al final del fichero). El p rim er registro es el cero. D espués com prueba que el ta
m año de los atributos del objeto C Persona m ás 4, no superen el tam año estableci
do para el registro (m ás 4 porque cada vez que w rite U T F escribe un S trin g ,
añade 2 bytes iniciales p ara dejar constancia del núm ero de bytes que se escriben;
esto perm itirá posteriorm ente al m étodo re a d U T F saber cuántos bytes tiene que
464 JA V A : C U R S O DE PRO G R A M A CIÓ N
leer). Si el tam año está dentro de los lím ites perm itidos, sitúa el puntero de L /E en
la posición d e inicio correspondiente a ese registro dentro del fichero y escribe los
atributos del objeto uno a continuación de otro (vea la definición de seek).
El m étodo a ñ a d ir tiene com o m isión añadir un nuevo registro al final del fichero.
Para ello, invoca al m étodo ponerV alorE n pasando com o argum entos la posición
que ocupará el nuevo registro, que coincide con el valor de nregs, y el objeto cu
yos atributos se desean escribir.
Para leer un registro del fichero que alm acena la lista de teléfonos, la clase C Lis
taTfnos proporciona el m étodo valorEn. Este m étodo tiene un parám etro para
identificar el núm ero de registro que se desea leer y devuelve el objeto CPersona
creado a partir de los datos nom bre, dirección y teléfono leídos desde el fichero.
S t r i n g n o mbr e , d i r e c c i ó n :
long te léfo n o:
nombre = f e s . r e a d U T F ( ):
d i r e c c i ó n = f e s . r e a d U T F ( ):
t e l é f o n o = f e s . r e a d L o n g ( ):
Se observa que lo prim ero que hace el m étodo es verificar si el núm ero de re
gistro es válido (el prim er registro es el cero). Si el núm ero de registro está dentro
de los lím ites perm itidos, sitúa el puntero de L /E en la posición de inicio corres
C A P ÍT U L O 12: TRA BA JA R CON FICHEROS 4 6 5
pondiente a ese registro dentro del fichero y lee los datos nom bre, dirección y te
léfono (esto se hace enviando al flujo f e s vinculado con el fichero, el mensaje
r e a d U T F , u na vez por cada dato). F inalm ente, devuelve un objeto CPersona
construido a partir de los datos leídos (el valor devuelto será n u il si el núm ero de
registro está fuera de límites).
Puesto que el fichero m anipulado se corresponde con una lista de teléfonos, pare
ce lógico identificar el registro que se desee elim inar p o r el núm ero de teléfono,
ya que éste es único. Para este propósito escribirem os un m étodo elim inar con un
parám etro que alm acene el núm ero de teléfono a elim inar y que devuelva un valor
true si la operación se realiza con éxito, o false en caso contrario.
El proceso seguido por el m étodo elim inar es leer registros del fichero, em pe
zando por el registro cero, y com probar por cada uno de ellos si el teléfono coin
cide con el valor pasado com o argum ento (este proceso recibe el nom bre de
búsqueda secuencial). Si existe un registro con el núm ero de teléfono buscado, no
se borra físicam ente del fichero, sino que se m arca el registro poniendo un cero
com o núm ero de teléfono. E sta form a de proceder deja libertad al usuario de la
clase C ListaTfnos para elim inar d e una sola vez todos los registros m arcados al
finalizar su aplicación, lo que redunda en velocidad de ejecución, para restaurar
un registro m arcado para elim inar, para crear un histórico, etc.
p o r el nom bre del propietario de ese teléfono, aunque tam bién podría realizarse la
búsqueda p o r la dirección. El m étodo b u sca r que se expone a continuación per
m ite realizar la búsqueda por cualquier subcadena perteneciente al nom bre. Para
ello utiliza dos parám etros: la subcadena a buscar y a partir de qué registro del fi
chero se desea buscar. Si la búsqueda term ina con éxito, el m étodo devuelve el
núm ero del registro correspondiente; en otro caso devuelve el valor -1.
Se observa que el m étodo buscar, al igual que el m étodo elim inar, realiza una
búsqueda secuencial desde el registro p o s, com probando si el nom bre de alguno
de ellos contiene la subcadena str. L ógicam ente, al realizar una búsqueda secuen
cial, el resultado será el núm ero del p rim er registro que contenga en su nom bre la
subcadena pasada com o argum ento; pero tam bién es evidente que es posible con
tinuar la búsqueda a partir del siguiente registro, invocando de nuevo al m étodo
buscar, pasando com o argum entos la m ism a subcadena y el núm ero de registro
siguiente al devuelto en el proceso de búsqueda anterior.
un nuevo co nstructor con un parám etro de tipo F ile (el otro constructor lo elim i
nam os para no com plicar la clase, pero lo podíam os h aber conservado).
import j a v a . i o . * ;
//////////////////////////////////////////////////////////////////
// A p l i c a c i ó n para t r a b a j a r c o n un f i c h e r o accedido aleatoriamente
//
public c la ss Test
I
// D e f i n i r un a r e f e r e n c i a a l f l u j o e s t á n d a r de s a l i d a : flujoS
s t a t ic PrintStream f lu j o S = System.out;
s a no s
publi c s t a t ic i nt m en ú()
i1
// .
I
publi c s t a t i c vo id m a i n ( S t r i n g [ ] args)
(
i n t o p c i ón = 0 , p o s = - 1 ;
S t r in g cadenabuscar = n u il;
S t r i n g n o mbr e , d i r e c c i ó n :
long t e l é f o n o :
boolean e li m i n a d o = f a l s e ;
boolean m o d if ic a d o = f a l s e ;
b o o le a n 1 i s t a M o d i f i cada = f a l s e ;
try
I
// C r e a r un o b j e t o l i s t a de t e l é f o n o s v a c i o ( c o n 0 e l e m e n t o s )
// o c o n e l c o n t e n i d o d e l f i c h e r o l i s t a t f n o s . d a t s i e x i s t e .
468 JA V A : C U R SO D E PRO G R A M A CIÓ N
do
o p c i ó n = m e n ú ( );
switch (opción)
i
case 1 // b u s c a r
II .
case 2 // b u s c a r siguiente
// .
case 3 // m o d i f i c a r
// .
case 4 // a ñ a d i r
// .
case 5 // e l i m i n a r
// . ,
case 6 // s a l i r
II .
w h i 1e ( o p c i ó n != 6):
I
catch ( lOException e)
I
f 1u j o S . p r i n t l n ( " E r r o r : " + e . g e t M e s s a g e ( )):
int op:
do
o p = Leer . d a t o l n t í ) ;
E legida una opción del m enú presentado, una sentencia sw itch perm itirá eje
cutar el código que dará solución a la operación seleccionada. Las opciones Bus
car, B uscar siguiente. A ñ a d ir y E lim inar no han variado respecto a la versión de
las m ism as presentada en el apartado “ S eriar objetos que referencian a objetos"
expuesto anteriorm ente en este m ism o capítulo, a excepción de que cuando se
m uestra la inform ación de un registro, ahora tam bién se m uestra el núm ero del
m ismo, y de que al salir de la aplicación, los cam bios debidos a A ñ a d ir o Elim inar
ya han realizados sobre el fichero (ahora se trabaja directam ente sobre el fichero).
El código com pleto lo puede ver en el C D -R O M que acom paña al libro.
M odificar un registro
Una operación im portante en el trabajo con ficheros que se puede realizar de for
ma rápida y fácil cuando se perm ite el acceso aleatorio al m ism o es m odificar al
guna parte concreta de la inform ación alm acenada en él. En nuestro caso, el
objetivo es m odificar un registro. Para ello vam os a añadir a la clase aplicación,
un m étodo estático denom inado m odificar con un parám etro que identifique el
núm ero de registro del fichero que se desea m odificar. Si durante la ejecución no
sabem os con exactitud el núm ero del registro que se desea m odificar, podem os
utilizar las opciones B uscar y B uscar siguiente para obtenerlo.
• L eer el registro correspondiente al núm ero pasado com o argum ento y crear un
objeto C Persona a partir de los datos leídos. Esto perm itirá m anipular el re
gistro utilizando la interfaz del objeto.
• Presentar un m enú que perm ita m odificar el nom bre, la dirección o el teléfo
no, así com o salir del proceso guardando los cam bios efectuados, o bien salir
sin g u ardar los cam bios. Los nuevos datos serán solicitados desde el teclado.
• Una vez realizadas las m odificaciones, si se eligió salir guardando los cam
bios efectuados, el m étodo enviará al objeto C ListaTfnos el m ensaje poner-
ValorEn pasando com o argum ento el núm ero de registro que se está
m odificando y el objeto C Persona que aporta los nuevos atributos; el resulta
do es que se sobreescribe en el fichero el registro especificado.
470 JA V A : C U R S O D E P R O G R A M A C IÓ N
// M o d i f i c a r el registro
do
[
f l u jo S. pr i n t ( " \ n \n " ) ¡
f l u j o S . p r i n t l n C M o d i f i c a r el d a t o : " ) ;
f l u j o S . p r i n t l n C l . N o m b re");
f lu j oS . p r i n t l n ( " 2 . D i r e c c i ó n " ) ;
f l u j o S . p r i n t l n í "3. T e l é f o n o " ) ;
f lu j oS . p r i n t l n ( " 4 . Sal i r y s a l v a r los camb i o s " ) :
f lu j oS . p r i n t l n ( " 5 . S a l i r sin s a l v a r los c a m b i o s " ) ;
f l u j o S . p r i n t l n ( );
f l u j oS .p ri n t ( " O p c i ó n : ");
op = L e e r , d a t o ! n t ( ):
switch( op )
I
case 1: // m o d i f i c a r n o m b r e
flujoS.printí"nombre: ");
n o m br e = L e e r . d a t o í ):
obj.asignarNombreínombre):
break;
case 2: // m o d i f i c a r d i r e c c i ó n
f l u j o S . p r i n t ( " d i r e c c i ó n : ");
dirección - Leer.datoí):
obj.asignarDi recciónídi r e c c i ó n ) ;
break;
c a s e 3: // m o d i f i c a r t e l é f o n o
flu joS.printí"telé fono: ” );
t e l é f o n o = L e e r . d a t o L o n g í );
o b j .a sig n a rT e lé fo n o íte lé fo n o ):
break;
case 4: // g u a r d a r los c a m b i o s
break;
case 5: // s a l i r sin g u a r d a r los c a m b i o s
break;
whileí op != 4 && op !- 5 ) ;
i t (op = = 4)
C A PÍTU LO 12: TRA BA JA R CON FICHEROS 4 7 1
1istatfnos.ponerValorEnínreg. obj):
return true:
I
el se
return false;
:
Actualizar el fichero
Los datos del fichero con el que estam os trabajando pueden verse alterados por
tres procesos diferentes: m odificar, a ñ a d ir o elim inar un registro. En el caso de
m odificar o a ñ adir un registro los cam bios son realizados directam ente sobre el
fichero. Pero en el caso de elim inar un registro, éste sim plem ente es m arcado con
un núm ero de teléfono 0 para su posterior elim inación, si se cree conveniente. En
nuestro caso, vam os a escribir en la clase aplicación un m étodo a ctualizar que se
invoque cuando el usuario de la aplicación seleccione la opción Salir, con el o b
jeto de actualizar el fichero, elim inando físicam ente los registros m arcados.
c a s e 6: // s a l i r
// g u a r d a r 1 i s t a
i f (elim inado) a c t u a l i z a r ( f i c h e r o ) :
1 i s t a t f n o s = n u l 1;
i n t n r e g s - 1 i s t a t f n o s . 1 o n g i t u d f );
// C o p i a r en e l f i c h e r o t e m p o r a l t o d o s l o s r e g i s t r o s d e l
// f i c h e r o a c t u a l q u e en s u campo t e l é f o n o no t e n g a n un 0
CPersona o b j ;
f o r ( i n t re g _ i = 0: re g _ i < n r e g s : r e g _ i + + )
I
o bj = 1 i s t a t f n o s . v a l o r E n ( r e g _ i ):
i f ( o b j , o b t e n e r T e l é f o n o ( ) != 0)
f t e m p . a ñ a d i r ( o b j );
472 JA V A : C U R SO DE PRO G R A M A CIÓ N
1 i s t a t f n o s . c e r r a r ( );
f t e m p . c e r r a r í );
f A c t u a l . d e l e t e ( );
i f ( ! f i c h e r o T e m p . r e n a m e T o ( f A c t u a l))
t h r o w new l O E x c e p t i o n t " n o s e r e n o m b r ó e l fichero");
II C r e a r un f l u j o h a c i a l a i m p r e s o r a
F i l e W r i t e r f l u j o S = new F i 1e W r i t e r ( " L P T l " ) :
f l u j o S . w r i t e t " E s t a l i n e a s e e s c r i b e en l a i m p r e s o r a \ r \ n " );
f l u j o S . w r i t e ( " \ r \ n " ) ; // s a l t a r una l i n e a
L o n g n - new L o n g t 1 2 3 4 5 6 7 8 9 ) :
flujoS.w ritet"Valor: " + n.toStringt) + "\r\n ");
f l u j o S . w r i t e t " \ f ” ); // s a l t a r a l a p á g i n a s i g u i e n t e
f 1u j o S . c l o s e t ): II c e r r a r e l f l u j o
El flujo creado es de la clase F ile W rite r, pero se podría haber creado de otra
clase que perm ita definir flujos de salida, com o F ile O u tp u tS tre a m , D a ta O u t-
p u tS tre a m , R a n d o m A c c e ssF ile , etc. Se puede observar que para obtener datos
im presos legibles se envían cadenas de caracteres a la im presora, ya que se trata
de un dispositivo A SC II. En general podem os enviar datos de tipo c h a r o byte.
c a s e 6 : // i m p r i m i r
i m p r i m i r L i s t a T f n o s t );
break:
c a s e 7: // s a l i r
II ...
CA PÍTU LO 12: TRA BA JA R C O N FICHEROS 4 7 3
p u b l i c s t a t i c v o i d i m p r i m í r L i s t a l f n o s í ) th rows IOException
1
// C r e a r un f l u j o h a c i a l a i m p r e s o r a
F i l e W r i t e r f l u j o S = new F i l e W r i t e r ( " L P T 1 " );
String c r l f = ” \ r \ n " ; // c a m b i a r a l a s i g u i e n t e l i n e a
S t r i n g f f = ”\ f “ ; // s a l t a r a l a s i g u i e n t e p á g i n a
Integer i; // r e f e r e n c i a a un o b j e t o Integer
L on g 1; // r e f e r e n c i a a un o b j e t o Long
i n t n r e g s = 1 i s t a t f n o s . 1 o n g i t u d ( ) ; // nú mero de r e g i s t r o s
EJERCICIOS RESUELTOS
1. E scribir una clase aplicación denom inada C opiarF ichero que perm ita copiar el
contenido de un fichero en otro. La aplicación será invocada de la form a siguien
te:
Este ejem plo utilizará la clase F ile para asegurarse de que el fichero fuente existe
y no está protegido contra lectura. Tam bién utilizará esta clase para asegurarse de
que el fichero destino existe y no está protegido contra escritura, o bien se trata de
un directorio no protegido contra escritura, destino del fichero.
474 JA V A : C U R S O D E P R O G R A M A C IÓ N
Para leer los bytes del fichero fuente y escribirlos en el destino, este ejem plo utili
zará las clases F ile ln p u tS tr e a m y F ile O u tp u tS tre a m , respectivam ente.
Este m étodo, básicam ente chequea la existencia y perm isos d e los ficheros fuente
y destino y copia el fichero origen en el destino; si el fichero destino existe pre
gunta si se desea sobreescribir. E n el caso de que ocurra algún error, este m étodo
lanzará una excepción del tipo EC opiarF ichero indicando lo ocurrido. Finalm en
te, utilizará un bloque fin a lly para cerrar los flujos abiertos.
import j a v a . i o . * :
// D e f i n i c i o n e s de v a r i a b l e s , r e f e r e n c i a s y o b j e t o s
F i l e f i c h F u e n t e = new F i 1e ( f u e n t e );
F i l e f i c h D e s t i n o = new F i 1e ( d e s t i n o ) ;
F ile ln p u tS tre a m fFuente = n u il:
FileOutputStream fD e stin o = n u il;
byte[] buffer:
in t nbytes:
try
I
// A s e g u r a r s e de que " f u e n t e " e s un f i c h e r o , e x i s t e
// y s e p u e d e l e e r .
i f ( ¡ f i c h F u e n t e . e x i s t s t ) || ! f i c h F u e n t e . i s F i l e ( ))
t h r o w new E C o p i a r F i c h e r o i " N o e x i s t e e l f i c h e r o ” + f u e n t e ) :
i f ( ! f i c h F u e n t e . c a n R e a d ( ))
t h r o w new E C o p i a r F i c h e r o ( " E l f i c h e r o ” + f u e n t e +
” no s e puede l e e r " ) :
II S i " d e s t i n o " e x i s t e , a s e g u r a r s e de q u e e s un f i c h e r o que
// s e p u e d e e s c r i b i r y p r e g u n t a r s i s e q u i e r e s o b r e e s c r i b i r .
C A PÍTU LO 12: TRA BA JA R CON FICHEROS 4 7 5
if ( f i c h D e s t i n o . e x i s t s ( )) // ¿ e x i s t e el destino?
í
if ( f i c h D e s t i n o . i s F i 1e ( )) II ¿ e s un f i c h e r o ?
I
if ( ! f i c h D e s t i n o . c a n W r i t e ( ))
t h r o w new E C o p i a r F i c h e r o t " N o s e pu ede e s c r i b i r en ” +
"el fiche ro " + d e s t in o ) ;
// I n d i c a r que e l f i c h e r o e x i s t e y p r e g u n t a r s i s e d e s e a
// s o b r e e s c r i b i r .
System .out.p rin tt"El fichero " + destino + " existe. " +
"¿Desea s o b r e e s c r ib i r l o ? (s/n ): ");
// L e e r l a r e s p u e s t a
c h a r r e s p = ( c h a r ) S y s t e m . i n . r e a d t ):
S y s t e m , i n . s k i p( S y s t e m , i n . a v a i l a b l e O ) ;
i f ( r e s p = = ’ n ' || r e s p — ’N’ )
t h r o w new E C o p i a r F i c h e r o t " C o p i a c a n c e l a d a " ) ;
1
el se
t h r o w new E C o p i a r F i c h e r o ( d e s t i no + " no e s un f i c h e r o " ) ;
I
else // s i " d e s t i n o " no e x i s t e v e r i f i c a r que e l directorio
// p a d r e e x i s t e y no e s t á protegido contra escritura
I
F ile dirPadre = d ire c t o rio P a d re t fic h D e st in o );
if ( ! d i r P a d r e . e x i s t s ( ))
t h r o w new E C o p i a r F i c h e r o t " E l d i r e c t o r i o " + d e s t i n o +
" no e x i s t e " ) :
i f ( ! d i r P a d r e .c a n W r i t e ( ))
t h r o w new E C o p i a r F i c h e r o ( " N o s e p u e d e e s c r i b i r en e l " +
"d ire c to rio " + destino):
// P a r a r e a l i z a r l a c o p i a , a b r i r un f l u j o d e e n t r a d a d e s d e
// e l f i c h e r o f u e n t e y o t r o de s a l i d a h a c i a e l d e s t i n o .
f F u e n t e = new F i l e l n p u t S t r e a m ( f i c h F u e n t e ) ;
f D e s t i n o = new F i l e O u t p u t S t r e a m ( f i c h D e s t i n o ) ;
b u f f e r = new b y t e [ 1 0 2 4 ] :
// C o p i a r el fichero f u e n t e en e l destino
w hile (true)
I
nbytes = fF u e n t e .re a d (b u ffe r);
i f ( n b y t e s = = - 1 ) b r e a k : // s e l l e g ó al final del fichero
f D e s t i n o . w r i t e t b u f f e r . 0. n b y t e s ) :
// C e r r a r c u a l q u i e r f l u j o que e s t é a b i e r t o
f i n a 11 y
476 JA VA : C U R SO DE PROGRAM A CIÓN
try
[
if ( f F u e n t e != n u i l ) f F u e n t e . c l o s e í ):
if ( f D e s t i n o ! = n u i l ) f D e s t i n o . e l o s e ( );
I
catchfIOException e)
I
System .out.p r i n t í n < " E r r o r : ” + e .t o S t r in g ());
// F i l e . g e t P a r e n t d e v u e lve n u i l s i el f i c h e r o se e s p e c i f i c a sin
// un d i r e c t o r i o . El m é t od o s i g u i e n t e t r a t a e s t e caso.
S t r i n g n o m b r e D i r = f . g e t P a r e n t ( ):
i f ( n o m b r e D i r = = n u l 1)
// El m ét o d o g e t P r o p e r t y c o n e l p a r á m e t r o " u s e r . d i r ” d e v u e l v e
// e l d i r e c t o r i o a c t u a l de t r a b a j o ,
r e t u r n new F i 1e ( S y s t e m . g e t P r o p e r t y ( " u s e r . d i r " ) ) ;
else
// D e v o l v e r e l d i r e c t o r i o p a d r e d e l f i c h e r o
r e t u r n new F i 1 e ( n o m b r e D i r );
// m a i n d e b e r e c i b i r d o s p a r á m e t r o s : e l f i c h e r o f u e n t e y
// e l d e s t i n o ,
i f ( a r g s . l e n g t h ! = 2)
S y s t e m . e r r . p r i n t í n ( " S i n t a x i s : java C o p ia rF ich ero " +
" < f ic h e r o fuente> < f ic h e r o d e s t i n o s " ) :
else
I
ry
catch(IOException e)
// S i s e p r o d u c e un e r r o r d u r a n t e l a c o p i a , se lanzará
// e l s i g u i e n t e t i p o de e x c e p c i ó n :
class ECopiarFichero extends lOException
I
public E C o p i a r F i c h e r o ( S t r i n g mensaje)
(
s u p e r ( m e n s a j e );
2. Q uerem os escribir una aplicación denom inada G rep que perm ita buscar palabras
en uno o m ás ficheros de texto. C om o resultado se visualizará, por cada uno de
los ficheros, su nom bre, el núm ero de línea y el contenido de la m ism a para cada
una de las líneas del fichero que contenga la palabra buscada.
a) B uscarC adena para buscar una cadena de caracteres dentro de otra. El prototi
po de este m étodo será:
c) m a in para que utilizando los m étodos anteriores perm ita buscar una palabra en
uno o m ás ficheros.
Observe que los datos obtenidos del fichero fuente son filtrados dos veces para
poder llegar a utilizar el m étodo re a d L in e de B u ffe red R ea d er. Recuerde que este
m étodo perm ite leer líneas de texto. C oncretam ente lo que se ha hecho ha sido:
478 JA VA : C U R SO D E PROGRAM A CIÓN
import j a v a . i o . * ;
ciass Grep 9 H H H H H H H H H H 1 IH 9
i
public static boolean B u s c a r C a d e n a ( S t r i n g ca denal. S t r i n g cadena2)
I
// ¿ c a d e n a 2 e s t á c o n t e n i d a en c a d e n a l ?
i f ( c a d e n a l.in d e x O f( cadena2) > -1)
return true; // s i
el se
r e t u r n f a l s e ; // no
try
I
II A s e g u r a r s e de q u e e l f i c h e r o , e x i s t e y s e p u e d e leer
i f ( ¡ f i c h F u e n t e . e x i s t s t ) || ! f i c h F u e n t e . i s F i 1e ( ) )
I
System .err.println("N o existe el fichero ” + nom brefich);
return;
I
if ( ! f i c h F u e n t e . c a n R e a d f ))
I
S y s t e m . e r r . p r i n t l n C E l f ic h e r o * + nombrefich +
" no s e p u e d e l e e r ” );
return;
// A b r i r un f l u j o de e n t r a d a d e s d e e l f i c h e r o f u e n t e
F i l e l n p u t S t r e a m f i s = new F i l e l n p u t S t r e a m ( f i c h F u e n t e ) ;
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r ( f i s ) ;
f l u j o E - new B u f f e r e d R e a d e r ! i s r ) ;
II B u s c a r c a d e n a en e l fichero fuente
S t r i ng 1 i n e a :
i n t n r o L i n e a = 0;
w hile ((linea = f 1u j o E . r e a d L i n e ( )) != n u il )
CA PÍTU LO 12: TR A B A JA R CON FIC H ER O S 4 7 9
// S i s e a l c a n z ó e l f i n a l d e l f i c h e r o ,
// r e a d L i n e d e v u e l v e n u i l
n r o L i n e a + + ; // c o n t a d o r de l í n e a s
i f tB uscarCad enat1 i n e a . cadena))
Sy ste m .out.p rin tln tn o m b refich + " " + nroLinea + " ” +
1 i nea );
catchíIOException e)
I
System .out.p r i n t l n ( " E r r o r : " + e.getM essaget));
I
finally
I
// C e r r a r e l flujo
try
I
if (flujoE != n u il ) f l u j o E . c l o s e ( );
1
c a t c h í I O E x c e p t i o n e)
1
S y ste m .o u t.p r i n t í n ( " E r r o r : " + e . t o S t r i n g í ));
if ( a r g s . l e n g t h < 2)
S y s t e m . e r r . p r i n t l n ( " S i n t a x i s : j a v a Grep " + "<cad e n a> " +
" < f i c h e r o 1> < f i c h e r o 2 > . . . " ) ;
el se
1
f o r ( i n t i = 1; i < a r g s . l e n g t h : i + + )
// B u s c a r a r g s C O ] en a r g s [ i ]
B u s c a r E n F i c h ( a r g s [ i ]. a r g s C O ] ) :
3. R ealizar un program a que perm ita crear un fichero nuevo, abrir uno existente,
añadir, m odificar o elim inar registros, y visualizar el contenido del fichero. El
nom bre del fichero será introducido a través del teclado. C ada registro del fichero
480 JA V A : C U R S O D E P R O G R A M A C IÓ N
estará form ado p o r los datos referencia y precio. A sí m ism o, para que el usuario
pueda elegir cualquiera de las operaciones enunciadas, el program a visualizará en
pantalla un m enú sim ilar al siguiente:
1. F i c h e r o nuevo
2. Ab ri r f i ch ero
3. Añadi r r e g i s t r o
4. M odificar re g istro
5. Elim inar regi stro
6. V isu alizar registros
7. Salir
Opción:
No se perm itirá crear un F ichero nuevo cuando exista, ni A b rir un fic h e ro que
no exista. C uando se intente A b rir un fic h e ro que no exista, se ofrecerá la posibi
lidad de m ostrar un listado del directorio actual. F inalm ente, la opción Visualizar
registros perm itirá m ostrar aquellos registros cuya referencia sea una especifica
da, o bien contenga una subcadena especificada.
Se deberá realizar al m enos un m étodo para cada una de las opciones, excepto
para Salir.
A partir de un análisis del enunciado se deduce que, adem ás del objeto aplica
ción (objeto de una clase que denom inarem os Test), potencialm ente existen dos
clases de objetos más: una que represente al fichero y otra que represente a los re
gistros del fichero.
E scribirem os entonces una clase C Registro p ara m anipular cada uno de los
registros de un fichero y otra C B aseD eD atos con una interfaz pública que perm ita
realizar las operaciones habituales de trabajo sobre un fichero.
• Un constructor sin parám etros y otro con parám etros para poder crear objetos
con unos atributos determ inados.
• Los m étodos obtenerR eferencia y obtenerP recio para obtener los valores de
los cam pos de un registro (atributos del objeto CRegistro).
• Los m étodos asignarR eferencia y asignarP recio p ara asignar nuevos valores
a los cam pos de un registro (atributos del objeto CRegistro).
• Y el m étodo tam año que devolverá el tam año en bytes de los atributos.
C A P ÍT U L O 12: TRA BA JA R CON FICHEROS 4 8 1
/////////////////////////////////////////////////////////////////
// D e f i n i c i ó n de l a clase CRegistro
//
public class CRegistro
I
// A t r i b u t o s
private S trin g referencia:
p riv a t e double p re cio :
II M é t o d o s
p u b l i c C R e g i s t r o t ) {)
public C R e gistro tStrin g ref. double pre)
I
referen cia - ref:
p r e c i o = pre:
referencia = ref:
public S trin g o b t e n e r R e f e r e n c i a ()
I
return referencia:
p u b lic double o b t e n e r P r e c io t )
I
return precio:
dotarem os a esta clase de cuatro atributos, un objeto F ile que encapsule el nom bre
del fichero actual de trabajo, un flujo vinculado con el fichero, el núm ero de re
gistros del fichero y la longitud estim ada para cada registro; y de los siguientes
m étodos:
• Un constructor que adm ita com o argum ento un objeto F ile que proporcione el
nom bre de la base de datos.
• El m étodo elim inar para m arcar un registro del fichero com o elim inado.
• Y el m étodo actualizar para elim inar físicam ente del fichero los registros
m arcados p o r el m étodo elim inar.
/////////////////////////////////////////////////////////////////
// D e f i n i c i ó n de l a c l a s e CBaseDeDatos.
//
import j a v a . i o . * ;
p u b l i c c l a s s CBaseDeDatos
I
// A t r i b u t o s
p rivate F ile ficheroActua 1; // o b j e t o F i l e ( n o mb r e d e l f i c h e r o )
p r i v a t e R a n d o m A c c e s s F i 1e f e s ; // f l u j o h a c i a / d e s d e el f i c h e r o
private in t nregs; // nú m e r o de r e g i s t r o s
p r i v a t e i n t tamañoReg = 50; // t a ma ñ o d e l r e g i s t r o en b y t e s
// M é t o d o s
pu b lic CBaseDeDatos( F i l e fichero) th rows IOException
I
// ¿ E x i s t e e l f i c h e r o ?
i f ( f i c h e r o . e x i s t s ( ) && ! f i c h e r o . i s F i 1e ( ))
t h r o w new I O E x c e p t i o n ( f i c h e r o . g e t N a m e ( ) + " no es un f i c h e r o ” );
II A s i g n a r v a l o r e s a l o s a t r i b u t o s
ficheroActual = fichero:
f e s = new R a n d o m A c c e s s F i 1e ( f i c h e r o , " r w " ) :
C A PÍTU LO 12: T R A B A JA R C O N FIC H ER O S 4 8 3
// El ú l t i m o r e g i s t r o no o c u p a e l ta maño e s p e c i f i c a d o .
// P o r e s t a c a u s a u t i l i z a m o s c e i l , p a r a r e d o n d e a r p o r e n c i m a ,
nregs = ( in t ) M a t h . c e i1 ( ( d o u b l e ) f e s . l e n g t h í) / (double)tamañoReg):
S trin g referencia:
double p re cio :
484 JA V A : C U R SO DE PROGRAM A CIÓN
// L e e r l a i n f o r m a c i ó n c o r r e s p o n d i e n t e a l registro i.
r e f e r e n c i a = f e s . r e a d U T F í );
p r e c i o = f e s . r e a d D o u b l e ( );
// D e v o l v e r e l o b j e t o C R e g i s t r o c o r r e s p o n d i e n t e ,
r e t u r n new C R e g i s t r o ( r e f e r e n c i a . p r e c i o ) ;
I
el s e
I
S y s t e m . o u t . p r i n t l n( " n ú m e r o de r e g i s t r o fuera de l i m i t e s " ) ;
r e t u r n n u l 1;
// G r a b a r l o
ponerValorEn! re g _ i . obj );
return true:
return false:
// C o p i a r en e l f i c h e r o t e m p o r a l t o d o s l o s r e g i s t r o s del
// f i c h e r o a c t u a l que no e s t é n m a r c a d o s p a r a " b o r r a r "
CRegi s t r o o b j ;
f o r ( i n t r e g _ i = 0; r e g _ i < n r e g s ; r e g _ i + + )
I
o bj = v a l o r E n ! r e g _ i );
i f (obj.obtenerReferencia() .com pareTo!"borrar") != 0)
f t e m p . a ñ a d i r ( o b j );
I
// B o r r a r e l f i c h e r o a c t u a l y r e n o m b r a r e l t e m p o r a l c o n el
// no mb r e d e l a c t u a l . P a r a h a c e r e s t a s o p e r a c i o n e s l o s f i c h e r o s
// no p u eden e s t a r en u s o .
t h i s . c e r r a r t ): // c e r r a r el fic h e r o actual
f t e m p . c e r r a r ! ); // c e r r a r el f i c h e r o tem poral
f i c h e r o A c t u a l . d e l e t e ( ) : II b o r r a r el fic h e ro actual
i f ( ! f i c h e r o T e m p . r e n a m e T o ! f i c h e r o A c t u a l ) ) // r e n o m b r a r
t h r o w new I O E x c e p t i o n ! " n o s e a c t u a l i z ó el f i c h e r o " ) :
V olviendo al enunciado del program a, éste tiene que perm itir a través de un
m enú, crear un fichero nuevo, abrir un fichero existente, añadir, m odificar o eli
m inar un registro del fichero y visualizar un conjunto determ inado de registros. El
m étodo m enú presentará todas estas opciones en pantalla y devolverá com o re
sultado un entero { 1 , 2 , 3 , 4 , 5 , 6 ó 7 ) correspondiente a la opción elegida por el
usuario. Este m enú ju n to con el esqueleto de la clase aplicación se m uestra a con
tinuación:
import j a v a . i o . * :
;/////////////////////////////////////////////////////////////////
// A p l i c a c i ó n p a r a t r a b a j a r c o n un f i c h e r o a c c e d i d o a l e a t o r i a m e n t e
// U t i l i z a l a c l a s e L e e r p a r a l e e r d e l a e n t r a d a e s t á n d a r c a d e n a s
// y d a t o s de t i p o s p r i m i t i v o s ,
p u b lic c l a s s Test
486 JA V A : C U R S O D E P R O G R A M A C IÓ N
// D e f i n i r una r e f e r e n c i a a l f l u j o e s t á n d a r d e s a l i d a : flujoS
s t a t i c PrintStream f lu j o S = System.out:
s t a t i c CBaseDeDatos a r t í c u l o s ;
s t a t i c boolean f i c h e r o A b i e r t o = f a l s e ;
// ...
// ...
II...
// ...
II...
op = L e e r . d a t o I n t ( );
i f ( o p < 1 || op > 7)
f l u j o S . p r i r»t ( " O p c i ó n no v á l i d a . Elija otra: ");
1
w h i 1e ( op < 1 || op > 7 ) ;
whi l e ( o p c i ó n != 7 );
488 JA V A : C U R S O D E P R O G R A M A C IÓ N
catch (IOException e)
I
flujoS.printlní"Error: " + e.ge tM essage()):
Se puede observar que la clase aplicación T est define tres atributos: un flujo
hacia la salida estándar, una referencia a la base de datos (fichero) con la que se
va a trabajar y una variable fich ero A b ierto de tipo b o o lean para saber en todo
m om ento si hay o no un fichero abierto (su valor será tr u e si el fichero está
abierto y false en caso contrario). E sta variable será utilizada para no crear o abrir
un fichero cuando ya haya uno abierto, y para no intentar añadir, m odificar, eli
m inar o visualizar registros cuando no haya un fichero abierto.
C ada una de las opciones del m enú, excepto la opción Salir, se resuelve eje
cutando un m étodo de los expuestos a continuación.
Nuevo fichero
El m étodo nuevoF ich tiene com o m isión crear un fichero vacío cuyo nom bre es
pecificarem os a través del teclado, sólo si dicho fichero no existe; si existe, se so
licitará un nuevo nom bre de fichero. F inalm ente, a partir del fichero especificado
creará un o bjeto artículos de la clase C B aseD eD atos cu y a interfaz nos permitirá
operar sobre ese fichero.
f i c h e r o A b i ert.o = t r u e :
A brir fichero
El m étodo abrirFich tiene com o m isión abrir un fichero existente cuyo nom bre
especificarem os a través el teclado. Si el nom bre especificado para el fichero no
se localiza en el directorio actual de trabajo, se dará la posibilidad de visualizar el
contenido de este directorio y de introducir un nuevo nom bre. F inalm ente, a partir
del fichero especificado creará un objeto artículos de la clase C B a s e D e D a t o s c u
ya interfaz nos perm itirá operar sobre ese fichero.
F i l e obj = n u i l :
char resp:
while ( ! o b jF ic h e r o .e x i s t s ())
!
f l u j o S . p r i n t l n ( " E s t e f i c h e r o no e x i s t e . " ) ;
f l u j o S . p r i n t t " ¿ D e s e a v e r l a l i s t a de f i c h e r o s ? s / n : " ) ;
resp = L e e r . c a r á c t e r t );
L e e r . 1 i m p i a r t ):
i f ( resp == ’n ') return :
// O b t e n e r un l i s t a d o d e l d i r e c t o r i o a c t u a l de t r a b a j o
o b j = new F i 1e ( S y s t e m . g e t P r o p e r t y ( " u s e r . d i r " ) ) :
S t r i n g [] nombresDir = o b j . l i s t O ;
f o r ( i n t i = 0: i < n o m b r e s D i r . 1 e n g t h ; i + + )
flu jo S .p r in t (n o m b r e s D ir [ i ] + ". ");
flu joS.p rintln("\n");
o b j F i c h e r o = new F i 1e ( L e e r . d a t o ( ) ) :
)
a r t í c u l o s = new C B a s e D e D a t o s ( o b j F i c h e r o ):
ficheroAbierto = true;
El m étodo añadi r R e g tiene com o m isión añadir un registro al final del fichero. Pa
ra ello, solicitará los datos a través del teclado y enviará al objeto artículos el
490 JA V A : C U R S O D E P R O G R A M A C IÓ N
flujoS.printl"Referencia: ");
referencia = Leer.dato!):
flu joS.printl"Pre cio: ");
p r e c i o = L e e r . d a t o D o u b l e ! );
artículos.añadir(new C R e g istro (re fe re n cia . precio)):
El m étodo m odificarR eg tiene com o finalidad perm itir m odificar cualquier regis
tro del fichero actual con el que estam os trabajando. Para ello, solicitará el núm e
ro de registro a m odificar, lo leerá, visualizará los cam pos correspondientes, y
presentará un m enú que perm ita m odificar cualquiera de esos cam pos:
M o d i f i c a r el dato:
1. R e f e r e n c i a
2. P r e c i o
3. S a l i r y s a l v a r l o s cambios
4. S a l i r s i n s a l v a r l o s cambios
Opción:
// S o l i c i t a r e l nú me r o de r e g i s t r o a m o d i f i c a r
f l u j o S . p r i n t ( " N ú m e r o de r e g i s t r o e n t r e 0 y ” +
( a r t í c u l o s . l o n g i t u d ! ) - 1) + " : " ) :
n r e g = L e e r . d a t o l n t ! );
// L e e r e l r e g i s t r o
C R e g i s t r o o b j = a r t i c u l o s . v a l o r E n ! n r e g ):
i f ( o bj = nul1 ) return:
CA PÍTU LO 12: TRA B A JA R C O N FIC H ER O S 4 9 1
// V i s u a l i z a r l o
f l u j o S . p r i n t l n ( o b j .o b t e n e r R e f e r e n c i a ( ) ) ;
f 1u j o S . p r i n t l n ( o b j .o b t e n e r P r e c i o ( ));
// M o d i f i c a r e l registro
do
(
f l u j o S . p r i n t ( " \ n \ n " );
f l u j o S . p r i n t l n C ' M o d i f i c a r el d a t o : " ) :
f l u j o S . p r i n t l n ( " l . R eferen cia''):
flu jo S .p rin tln C '2 . Precio"):
f 1 u j o S . p r i n t l n ( " 3 . S a l i r y s a l v a r l o s c a m b i o s ” );
f 1u j o S . p r i n t l n ( " 4 . S a l i r s i n s a l v a r l o s c a m b i o s " ) :
f l u j o S . p r i n t l n í );
flu joS.p rintC Opción: " ) ;
op = L e e r . d a t o I n t ( ) ;
switchí op )
I
c a s e 1: // m o d i f i c a r r e f e r e n c i a
f 1u j o S . p r i n t ( " R e f e r e n c i a : ");
referencia = Lee r.d atoO ;
o b j . a s i g n a r R e f e r e n c i a ( r e f e r e n c i a );
break;
c a s e 2: II m o d i f i c a r p r e c i o
flujoS.print("Precio: ");
p r e c i o = L e e r . d a t o D o u b l e í );
obj.asignarPrecio(precio);
break;
c a s e 3: // g u a r d a r l o s c a m b i o s
break;
c a s e 4 : II s a l i r s i n g u a r d a r l o s c a m b i o s
break;
w h i 1e ( op ! = 3 && op != 4 );
El m étodo elim inarR eg perm ite m arcar un registro del fichero com o borrado. Para
m arcar un registro se enviará al objeto artículos el m ensaje elim inar (se ejecuta el
m étodo elim inar de su clase) pasando com o argum ento su referencia, la cual se
solicitará a través del teclado. E ste m étodo devolverá el m ism o valor retom ado
por el m étodo elim inar, tr u e si la operación se realiza satisfactoriam ente y false
en caso contrario.
492 J A V A : C U R S O D E P R O G R A M A C IÓ N
do
I
nreg = a r t íc u lo s . b u s c a r t s t r . nreg+1);
i f (nreg > -1)
I
obj = a r t í c u l o s . v a l o r E n ( n r e g ) :
f lu j o S . p r in t ln ( "R e g is t r o : " + nreg);
f 1u j o S . p r i n t l n ( o b j . o b t e n e r R e f e r e n c i a ( ) ) ;
f l u j o S . p r i n t l n ( o b j . o b t e n e r P r e c i o ( ));
f 1u j o S . p r i n t l n ( );
if (obj == n u il)
f 1u j o S . p r i n t l n ( " n o se encontró ningún registro");
EJERCICIOS PROPUESTOS
1. E scribir una aplicación que perm ita escribir p o r la im presora un fichero de texto.
La aplicación se ejecutará de la form a siguiente: ja v a im prim ir fich ero , donde im
p rim ir e s el nom bre de la aplicación y fic h e ro el nom bre del fichero de texto que
se desea im prim ir.
2. R ealizar un program a que perm ita trabajar sobre un fichero que alm acena los
resultados obtenidos después de m edir las tem peraturas en un punto geográfico
durante un intervalo de tiem po. El fichero constará de una cabecera definida se
gún la siguiente estructura de datos:
1. F i c h e r o nuevo
2. Ab ri r f i ch ero
3. A ñ a d i r temperatura
4. M o d if i c a r temperatura
5. V i s u a l i z a r l a t e m p e r a t u r a medi a
6. Sal i r
O pci ó n :
494 JA VA : C U R S O D E PRO G R A M A CIÓ N
3. S uponga que disponem os de un fichero en disco llam ado alum nos, donde cada
registro se corresponde con los atributos de una clase com o la siguiente:
4. S uponga que disponem os en el disco dos ficheros denom inados alum nos y modi
fica cio n es. La estructura de cada uno de los registros para am bos ficheros se co
rresponde con los atributos de una clase com o la siguiente:
public c la ss CRegistro
I
// A t r i b u t o s
p r i v a t e S t r i n g n o mbr e :
p r iv a t e f l o a t nota:
// M é t o d o s
// . . .
I
Suponga tam bién que am bos ficheros están clasificados ascendentem ente por el
cam po nom bre.
• R egistros q ue tam bién están en el fichero alum nos pero que han variado en su
cam po nota.
• R egistros nuevos; esto es, registros que no están en el fichero alum nos.
• R egistros que tam bién están en el fichero alum nos y que deseam os eliminar.
Estos registros se distinguen porque su cam po nota vale -1.
Se pide realizar un program a que perm ita obtener a partir de los ficheros alumnos
y m odificaciones un tercer fichero siguiendo los criterios de actualización ante
riorm ente descritos. El fichero resultante term inará llam ándose alumnos.
CA PÍTU LO 13
© F . 1. C c h a U o s / R A -M A
ESTRUCTURAS DINÁMICAS
La principal característica de las estructuras dinám icas es la facultad que tienen
para variar su tam año y hay m uchos problem as que requieren de este tipo de es
tructuras. E sta propiedad las distingue claram ente de las estructuras estáticas fun
dam entales com o las m atrices. C uando se crea una m atriz su núm ero de elem entos
se fija en ese instante y después no puede agrandarse o dism inuirse elem ento a
elem ento, conservando el espacio actualm ente asignado; en cam bio, cuando se
crea una estructura dinám ica eso sí es posible.
P o r tanto, no es posible asignar una cantidad fija de m em oria para una es
tructura dinám ica, y com o consecuencia un com pilador n o puede asociar direc
ciones explícitas con las com ponentes de tales estructuras. La técnica que se
utiliza m ás frecuentem ente p ara resolver este problem a consiste en realizar una
asignación dinám ica p ara las com ponentes individuales, al tiem po que son creadas
durante la ejecución del program a, en vez de hacer la asignación de una sola vez
para un núm ero de com ponentes determ inado.
C uando se trabaja con estructuras dinám icas, el com pilador asigna una canti
dad fija de m em oria para m antener la dirección del com ponente asignado dinám i
cam ente, en vez de hacer una asignación para el com ponente en sí. Esto im plica
que debe haber una clara distinción entre datos y referencias a datos, y que conse
cuentem ente se deben em plear tipos de datos cuyos valores sean referencias a
otros datos.
C uando se asigna m em oria dinám icam ente para un objeto de un tipo cualquie
ra, se devuelve una referencia a la zona de m em oria asignada. Para realizar esta
operación disponem os en Java del operador new (vea en el capítulo 4, el apartado
“C rear un objeto de una clase” ).
496 JA V A : C U R S O D E P R O G R A M A C IÓ N
LISTAS LINEALES
H asta ahora hem os trabajado con m atrices que com o sabem os son colecciones de
elem entos todos del m ism o tipo, ubicados en m em oria uno a continuación de otro;
el núm ero de elem entos es fijado en el instante de crear la m atriz. Si m ás adelante,
d urante la ejecución del program a, necesitáram os m odificar su tam año para que
contenga m ás o m enos elem entos, la única alternativa posible sería asignar un
nuevo espacio de m em oria del tam año requerido y adem ás, copiar en él los datos
q ue necesitem os conservar d e la m atriz original. La nueva m atriz pasará a ser la
m atriz actual y la m atriz origen se destruirá, si ya no fuera necesaria.
Es evidente que cada vez que necesitem os añadir o elim inar un elem ento a
una colección de elem entos, la solución planteada en el párrafo anterior no es la
m ás idónea; seguro que estam os pensando en algún m ecanism o que nos perm ita
añadir un único elem ento a la colección, o bien elim inarlo. E ste m ecanism o es
viable si en lugar de trabajar con m atrices lo hacem os con listas lineales. U na lista
lineal es una colección, originalm ente vacía, de elem entos u objetos de cualquier
tipo no necesariam ente consecutivos en m em oria, que durante la ejecución del
program a puede crecer o decrecer elem ento a elem ento según las necesidades
previstas en el m ism o.
Según la definición dada surge una pregunta: si los elem entos no están conse
cutivos en m em oria ¿cóm o pasam os desde un elem ento al siguiente cuando reco
rram os la lista? La respuesta es que cada elem ento debe alm acenar inform ación de
dónde está el siguiente elem ento o el anterior, o bien am bos. E n función de la in
form ación que cada elem ento de la lista alm acene respecto a la localización de sus
antecesores y/o predecesores, las listas pueden clasificarse en: listas sim plem ente
enlazadas, listas circulares, listas doblem ente enlazadas y listas circulares doble
m ente enlazadas.
Lista lineal
Para co n struir una lista lineal, prim ero tendrem os que definir el tipo de los
elem entos que van a form ar parte de la m ism a. Por ejem plo, cada elem ento de la
lista puede definirse com o una estructura de datos con dos m iem bros: una refe
rencia al elem ento siguiente y una referencia al área de datos. El área de datos
puede ser de un tipo predefinido o de un tipo definido p o r el usuario. Según esto,
el tipo de cada elem ento de una lista puede venir definido de la form a siguiente:
class CElementoLse
1
// A t r i b u t o s
// D e f i n a a q u í l o s d a t o s o l a s r e f e r e n c i a s a l o s d a t o s
// . . .
C E l e m e n t o L s e s i g u i e n t e : // r e f e r e n c i a a l s i g u i e n t e e l e m e n t o
// M é t o d o s
CE1ementoLse() I) II c o n s t r u c t o r sin parámetros
II...
í
Se puede observar que la clase C E lem entoLse definirá una serie d e atributos
correspondientes a los datos que deseem os m anipular, adem ás de un atributo es
pecial, denom inado siguiente, para perm itir que cada elem ento pueda referenciar a
su sucesor form ando a sí una lista enlazada.
public e la ss Test
I
public static void m a in {S trin g [] args)
I
C E l e m e n t o L s e p : II r e f e r e n c i a a un e l e m e n t o
// A s i g n a r memo r i a p a r a un e l e m e n t o
p = new C E l e m e n t o L s e ! ) :
// E s t e e l e m e n t o no t i e n e un s u c e s o r
p .siguiente = n u i l ;
// O p e r a c i o n e s c u a l e s q u i e r a
// P e r m i t i r q u e s e l i b e r e l a me m o r i a o c u p a d a por el elemento p
p = nuil:
498 JA V A : C U R S O D E P R O G R A M A C IÓ N
El valor nuil, referencia nula, perm ite crear estructuras de datos finitas. A sí
m ism o, suponiendo que p hace referencia al principio de la lista, direm os que di
ch a lista está vacía si p vale nuil. Por ejem plo, después de ejecutar las sentencias:
p = n u 11: / / l i s t a vacia
p = new C E 1 e m e n t o L s e t ) : // e l e m e n t o p
p.siguiente = n u il; // no h a y s i g u i e n t e elemento
nuil
q = new C E 1 e m e n t o L s e ( ) ; // c r e a r un n u e v o e l e m e n t o
q . s i g u i e n t e = p: // a l m a c e n a r l a l o c a l i z a c i ó n d e l e l e m e n t o s i g u i e n t e
p = q ; // p h a c e r e f e r e n c i a a l p r i n c i p i o de l a l i s t a
donde q es una referencia a un objeto de tipo C Elem entoLse. A hora tenem os una
lista de dos elem entos. O bserve que los elem entos nuevos se añaden al principio
de la lista.
P ara verlo con claridad analicem os las tres sentencias anteriores. Partim os de
que tenem os una lista referenciada por p con un solo elem ento. La sentencia q =
n ew C E lem entoL sef) crea un nuevo elem ento:
nuil
L a sentencia q. siguiente = p hace que el sucesor del elem ento creado sea el
anteriorm ente creado. O bserve que ahora q.siguiente y p tienen el m ism o valor;
esto es, la m ism a dirección, p o r lo tanto, referencian el m ism o elem ento:
CA PÍTU LO 13: ESTRU C TU R A S D IN Á M ICA S 4 9 9
/ ■
nui l
/ ■
n u il
q = q.siguiente;
¿Q uién es q.sig u ien te! Es el atributo siguiente del objeto referenciado por q que
contiene la dirección de m em oria donde se localiza el siguiente elem ento al refe
renciado p o r p . Si este valor se lo asignam os a q, entonces q referenciará al m ismo
elem ento que referenciaba q. siguiente. El resultado es que q referencia ahora al
siguiente elem ento com o se puede ver en la figura m ostrada a continuación:
n u il
E sto nos da una idea de cóm o avanzar elem ento a elem ento sobre una lista. Si
ejecutam os de nuevo la m ism a sentencia:
q = q.siguiente:
Operaciones básicas
Las operaciones que podem os realizar con listas incluyen fundam entalm ente las
siguientes:
500 JA V A : C U R S O D E P R O G R A M A C IÓ N
class CElementoLse
I
II A t r i b u t o s
in t dato:
CElementoLse s i g u i e n t e : // referencia al siguiente elemento
II M é t o d o s
C E 1 e m e n to L s e () I) // c o n s t r u c t o r s i n p a r á m e t r o s
CE1ementoLset i n t d ) // c o n s t r u c t o r c o n p a r á m e t r o s
I
d a t o = d:
vam os a ex poner en los siguientes apartados cóm o realizar cada una de las opera
ciones básicas. O bserve que p o r sencillez vam os a trabajar con una lista de ente
ros.
Supongam os una lista lineal referenciada por p. Para insertar un elem ento al prin
cipio de la lista, prim ero se crea el elem ento y después se reasignan las referen
cias, tal com o se indica a continuación:
q - new C E 1 e m e n t o L s e t ):
/ /
nuil
C A PÍTU LO 13: ESTRU C TU R A S D IN Á M ICA S 5 0 i
q . d a t o = n; // a s i g n a c i ó n d e v a l o r e s
q . s i g u i e n t e = p: // r e a s i g n a c i ó n d e r e f e r e n c i a s
P = q;
Esta operación básica nos sugiere cóm o crear una lista. Para ello, y partiendo
de una lista vacía, no tenem os m ás que repetir la operación de insertar un ele
mento al com ienzo de una lista, tantas veces com o elem entos deseem os que tenga
dicha lista. V eám oslo a continuación:
//////////////////////////////////////////////////////////////////
// C r e a r una l i s t a lineal sim plemente enlazada
//
public class Test
I
public static void main( S t r i n g [] args)
1
C E 1 e m e n t o L s e p , q : // r e f e r e n c i a s
i n t n, e o f = I n t e g e r . M I N _ V A L U E ;
// C r e a r una l i s t a de e n t e r o s
S y s t e m , o u t . p r i n t l n ( " 1n t r o d u c i r d a t o s . Finalizar c o n C t r l + Z . ’’ ):
p = n u l 1 : // 1 i s t a vacia
N o tar que el orden de los elem entos en la lista, es inverso al orden en el que
han llegado. A sí m ism o, com o es ya habitual, utilizam os la clase L e e r diseñada en
el capítulo 5 y revisada en el 11 y 12, para leer datos desde el teclado.
502 JA V A: C U R S O DE PROGRAM A CIÓN
q - new C E 1 e m e n t o L s e ( ):
q . d a t o = x ; // v a l o r i n s e r t a d o
q .siguiente = r.siguiente:
r . s i g u i e n t e - q;
il
q - new C E 1 e m e n t o L s e ( ):
q.dato = r.dato: // c o p i a r m i e m b r o a m i e m b r o un o b j e t o en o t r o
q .siguiente - r.siguiente:
r . d a t o = x; // v a l o r i n s e r t a d o
r .s i gui ente = q :
1
— X 1 27
1
1
¡ i \ /
13 27 1 13 X
/ / 1 /
1
q = r . siguiente: / / q r e f e r e n c i a el elemento a b o r r a r
r.sigu ie n te = q.siguiente: II e n l a z a r l o s e l e m e n t o s a n t e r i o r
// y p o s t e r i o r a l b o r r a d o
q = nu il: // o b j e t o re f e re n c ia d o por q a la basura (b o r r a r )
O bserve que para acceder a los m iem bros de un elem ento, éste tiene que estar
referenciado por una variable. Por esta razón, lo prim ero que hem os hecho ha sido
referenciar el elem ento a borrar p o r q.
P ara borrar un elem ento referenciado por r, las operaciones a realizar son las
siguientes:
q = r. sig u ie n te :
r . d a t o = q . d a t o ; // c o p i a r m i e m b r o a m i e m b r o uri o b j e t o en o t r o
r.sig u ie n te = q.siguiente:
q = nu il; // o b j e t o r e f e r e n c i a d o p o r q a l a b a s u r a ( b o r r a r )
S upongam os que hay que realizar una operación con todos los elem entos de una
lista, cu y o p rim er elem ento está referenciado p o r p. P or ejem plo, escribir el valor
de cada elem ento de la lista. La secuencia de operaciones sería la siguiente:
q = p : // s a l v a r l a referencia al p r i m e r e l e m e n t o de l a lista
w h i l e (q ! = n u i l )
1
System .out.print(q.dato + " ");
q = q .sig u í e n te :
I
B orrar todos los elem entos de una lista equivale a enviar a la basura a cada uno de
los elem entos de la m ism a. S upongam os que querem os borrar una lista, cuyo pri
m er elem ento está referenciado por p . La secuencia de operaciones es la siguiente:
q = p: // q r e f e r e n c i a el p r i m e r e l e m e n t o de l a l i s t a
w hile ( q != n u il )
I
p = p . s i g u i e n t e : // p r e f e r e n c i a al s i g u i e n t e elemento
q = nu il: // o b j e t o r e f e r e n c i a d o p o r q a l a b a s u r a
q = p: // q h a c e r e f e r e n c i a a l m i s mo e l e m e n t o q u e p
I
O bserve que antes de borrar el elem ento referenciado por q, hacem os que p
referencie al siguiente elem ento, porque si no perderíam os el resto de la lista; la
referenciada p o r q.siguiente. Y ¿por qué perderíam os la lista? Porque se pierde la
única referencia que nos d a acceso a la m ism a. Entonces, para borrar un lista cuyo
prim er elem ento está referenciado p o r p bastaría con hacer:
E videntem ente, el proceso anterior no es necesario. Para elim inar una lista
basta con po n er a n u il la variable que hace referencia al p rim er elem ento de la
m ism a, porque esto im plica que todos los elem entos de ella queden desreferencia-
dos y sean enviados a la basura para ser recogidos por el recolector de basura.
S upongam os que querem os buscar un determ inado elem ento en una lista cuyo
prim er elem ento está referenciado p o r p . La búsqueda es secuencial y termina
cuando se encuentra el elem ento, o bien cuando se llega al final de la lista.
C A P ÍT U L O 13: ESTR U C TU R A S DINÁM ICA S 5 0 5
q = p¡ // q r e f e r e n c i a e l p r i m e r e l e m e n t o de l a lista
S y s t e m . o u t . p r i n t í " d a t o a b u s c a r : " ) ; x = L e e r . d a t o l n t í ):
w h i l e ( q ! = n u i l && q . d a t o ! = x )
q = q . s i g u i e n t e : // q r e f e r e n c i a al s i g u i e n t e e l e m e n t o
O bserve el orden de las expresiones que form an la condición del bucle while.
Sabem os que en una operación & & (A N D ), cuando una d e las expresiones es fal
sa la condición ya es falsa, por lo que el resto de las expresiones no necesitan ser
evaluadas. De ahí que cuando q valga n u il si la expresión q.dato fuera evaluada.
Java lanzaría una excepción N u llP o in te rE x c e p tio n .
La clase la denom inarem os C ListaL inealSE (G a s e Lista L ineal Sim plem ente
Enlazada). D icha clase incluirá un atributo p para alm acenar de form a perm anente
una referencia al prim er elem ento de la lista, y una clase interna C E lem ento que
definirá la estructura de un elem ento d e la lista, que según hem os indicado ante
riorm ente será así:
// M é t o d o s
p r i v a t e CElementoí) II // c o n s t r u c t o r
I
El constructor dará lugar a una lista vacía. El m étodo añadirA lP rincipio per
m itirá añ adir un nuevo elem ento al principio de la lista, en nuestro caso un valor
de tipo d o u b le recibido com o parám etro por el m étodo, y m ostrarTodos perm itirá
visualizar p o r pantalla todos los elem entos de la lista, en nuestro caso la lista de
valores de tipo d o u b le que alm acena.
//////////////////////////////////////////////////////////////////
// Lista lineal sim plemente enlazada
//
public class CListaLinealSE
I
// p: r e f e r e n c i a a l p r i m e r e l e m e n t o d e l a lista
p r i v a t e CElemento p = n u i l ;
p u b li c C L i s t a L i n e a l S E () II // c o n s t r u c t o r
// A ñ a d i r un e l e m e n t o al p r i n c i p i o de l a lista
p u b l i c v o i d a ñ a d i r A l P r i n c i p i o t d o u b l e n)
I
C E l e m e n t o q = new C E l e m e n t o O :
q . d a t o = n; // a s i g n a c i ó n de v a l o r e s
q . s i g u i e n t e = p; // r e a s i g n a c i ó n de r e f e r e n c i a s
P - q:
I
//////////////////////////////////////////////////////////////////
/// // // // / / / // // / / / // // / / / // // / / / // // / / / // // / / / // // / / // / / / // // / / / /
// C r e a r una l i s t a l i n e a l s i m p l e m e n t e e n l a z a d a
//
public c la s s Test
I
public static void m a in ( S t r in g [ ] args)
I
// C r e a r una l i s t a l i n e a l vacia
CListaLinealSE Ise = new C L i s t a L i n e a l S E ( ):
// L e e r d a t o s r e a l e s y a ñ a d i r l o s a la lista
d o u b l e n;
boolean eof = true:
S y s t e m . o u t . p r i n t l n ( " I n t r o d u c i r d a t o s . F i n a l i z a r c o n C t r l + Z . ” ):
System .out.pri n t ( "dato: "):
w h ile ( D o u b le . is N a N ( n = L e e r .d a t o D o u b le ( )) != eof)
I
1s e . a ñ a d i r A l P r i n c i p i o ( n ) :
System .out.printt"dato: ");
// M o s t r a r l a l i s t a de d a t o s
System .out.pri n t l n ();
1 s e . m o s t r a r T o d o s t ):
Si en un instante determ inado necesitara borrar todos los elem entos de la lista,
bastaría con escrib ir Ise = nuil.
El m étodo o btener recibirá com o parám etro la posición del elem ento que se
desea obtener (la prim era posición es la cero) y devolverá com o resultado el dato
alm acenado p o r este elem ento, o bien el valor N aN si la lista está vacía o la posi
ción especificada está fuera de lím ites.
508 J A V A : C U R S O D E P R O G R A M A C IÓ N
C E l e m e n t o q = p: // r e f e r e n c i a al p rim e r elemento
if (i > = 0 )
I
// P o s i c i o n a r s e en e l e l e m e n t o i
f o r ( i n t n = 0 ; q ! = n u i l && n < i ; n++)
q = q.siguiente;
// R e t o r n a r el dato
i f (q != n u i l ) return q.dato:
I
// í n d i c e f u e r a de l i m i t e s
r e t u r n Double.NaN;
I
// M o s t r a r l a l i s t a de d a t o s
S y s t e m . o u t . p r i n t l n C );
i n t i = 0;
d o u b l e d = 1s e . o b t e n e r í i ) ;
w hile ( ¡Double.isNaN(d))
I
System .out.printíd + " “ );
i ++;
d = 1 s e . o b t e n e r t i );
L o que hace el segm ento de código m ostrado es obtener y visualizar los valo
res de los elem entos 0 , 1 ,2 ,... de la lista Ise h asta que el m étodo obtener devuelva
el valor N aN , señal de que se ha llegado al final de la lista.
C A P ÍT U L O 13: ESTR U C TU R A S D IN Á M ICA S 5 0 9
Sabem os que O b je ct es la superclase de todas las clases; esto es, cuando im-
plem entam os una clase y no se especifica explícitam ente su superclase, dicha cla
se está derivada de O bject. Esto significa que las dos definiciones de clase
siguientes son equivalentes:
T am bién sabem os que Java perm ite convertir im plícitam ente una referencia a
un objeto de una subclase en una referencia a su superclase directa o indirecta.
Por ejem plo, la siguiente línea convierte una referencia a un objeto de la clase
D o u b le a una referencia a su superclase O bject. E ste ejem plo puede extenderse a
cualquier clase de la biblioteca de Java o definida por el usuario.
O b j e c t d a t o s = new D o u b l e ( n ) ;
Según esto, para que la clase C L istaL inealSE perm ita listas de objetos de
cualquier tipo, basta con que su clase interna C E lem ento (clase de cada uno de los
elem entos de la lista) tenga un atributo que sea una referencia de tipo O bject. Un
atributo así definido puede referenciar cualquier objeto de cualquier clase.
E sta m odificación im plica dos cam bios m ás: el parám etro del m étodo añadir-
A lP rincipio tiene que ser ahora de tipo O bject, y el m étodo obtener tiene que de
volver ahora una referencia de tipo O bject.
//////////////////////////////////////////////////////////////////
// L i s t a lineal sim plemente en laz ada
//
public class CListaLinealSE
I
II p: r e f e r e n c i a al p r i m e r e l e m e n t o de l a lista
p r i v a t e CElemento p - n u i l :
public C L ist a L in e a lS E () II // c o n s t r u c t o r
// A ñ a d i r un e l e m e n t o a l p r i n c i p i o d e l a l i s t a
p u b lic void añadí r A l P r i n c i p ió (O b je c t obj)
I
C E l e m e n t o q = new C E l e m e n t o O :
q .d atos - obj: // a s i g n a c i ó n de v a l o r e s
q . s i g u i e n t e = p; // r e a s i g n a c i ó n de r e f e r e n c i a s
p = q:
I
C E l e m e n t o q = p: II r e f e r e n c i a al prim er elemento
if (i >- 0)
I
// P o s i c i o n a r s e en e l e l e m e n t o i
f o r ( i n t ri = 0: q ! = n u i l && n < i : n + + )
q - q.siguiente:
// R e t o r n a r l o s d a t o s
i f (q ! - n u i l ) r e t u r n q . d a t o s :
I
// í n d i c e f u e r a de l i m i t e s
r e t u r n n u l 1;
//////////////////////////////////////////////////////////////////
V eam os ahora en qué se m odifica la aplicación Test que creaba una lista de
valores de tipo d o u b le introducidos desde el teclado. Igual que antes, el método
m a in de T est realizará tres cosas:
2. Solicitará datos de tipo d o u b le del teclado y los añadirá a la lista, para lo cual
enviará al objeto h e el m ensaje añadirA IP rincipio por cada dato que añada.
Pero com o añadirA IP rincipio tiene un parám etro de tipo O bject, el argu
m ento pasado tiene que ser un objeto; en este caso un objeto que encapsule un
valor de tipo double. Estos objetos son construidos a partir de la clase D o u b le
del paquete ja v a.lan g.
I se . a ñ a d í rAl P r i nci pi o(new D o u b l e ( n ));
//////////////////////////////////////////////////////////////////
// C r e a r una lista lineal simplemente enlazada
//
public class Test
I
public static void m a in (S trin g [] args)
I
II C r e a r una l i s t a l i n e a l v a c i a
C L i s t a L i n e a l S E l s e = new C L i s t a L i n e a l S E ( ) :
// L e e r d a t o s r e a l e s y a ñ a d i r l o s a l a l i s t a
d o u b l e n;
boolean eof = true:
S y s t e m . o u t . p r i n t l n ( " l n t r o d u c i r d a t o s . F i n a l i z a r con C t r l + Z . " ) :
System .out.printí"dato: "):
w h ile (D o u b le . is N a N ín = L e e r . d a t o D o u b le í )) ! - eof)
1
lse.añadi rA lP rin cip io (n e w D o u b le (n )):
S y s t e m . o u t . p r i n t l " d a t o : ” );
// M o s t r a r l a l i s t a de d a t o s
S y s t e m . o u t . p r i n t l n ( ):
512 JA VA: C U R SO DE PRO G R A M A CIÓ N
D o u b l e d = ( D o u b l e ) l s e . o b t e n e r ( i );
wh i l e (d != n u i l )
1
S y s t e m . o u t . p r i n t ( d . d o u b l e V a l u e ( ) + " ” ):
i ++;
d = ( D o u b l e ) l s e . o b t e n e r ( i );
1
1
if
Para finalizar, vam os a com pletar la clase C L istaL inealSE con otros métodos
de interés que especificam os en la tabla siguiente:
M é to d o S ig n ific a d o
tam año D evuelve el núm ero de elem entos de la lista. N o tiene pa
rám etros.
a ñ adir A ñade un elem ento en la posición /. T iene dos parám etros:
posición i y una referencia al objeto a añadir. D evuelve
true si la operación se ejecuta satisfactoriam ente y false en
caso contrario.
añadirA lP rincipio A ñade un elem ento al principio. T iene un parám etro que es
una referencia al objeto a añadir. D evuelve true o false.
igual que añadir.
a ñadirA lF inal A ñade un elem ento al final. T iene un parám etro que es una
referencia al objeto a añadir. D evuelve true o false, igual
que añadir.
borrar B orra el elem ento de la posición i. T iene un parám etro que
indica la posición i del objeto a borrar. D evuelve una refe
rencia al objeto borrado o n u il si la lista está vacía o el ín
dice está fuera de lím ites.
borrarP rim ero Borra el p rim er elem ento. N o tiene parám etros. D evuelve
una referencia al objeto borrado o n u il si la lista está vacía.
borrarÚ ltim o B orra el últim o elem ento. N o tiene parám etros. D evuelve
una referencia al objeto borrado o n u il si la lista está vacía.
obtener D evuelve el elem ento de la posición /, o bien n u il si la lista
está vacía o el índice está fuera de límites. T iene un pará
m etro que se corresponde con la posición i del objeto que
se desea obtener.
obtenerP rim ero D evuelve el p rim er elem ento, o bien n u il si la lista está va
cía.
obtenerÚ ltim o D evuelve el últim o elem ento, o bien n u il si la lista está va
cía.
CA PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 1 3
//////////////////////////////////////////////////////////////////
// L i s t a lineal simplemente enlazada
//
public c la ss CListaLinealSE
I
// p: r e f e r e n c i a a l p r i m e r e l e m e n t o de l a lista.
// E s e l e l e m e n t o de c a b e c e r a ,
p r i v a t e CElemento p = n u i l ;
public C ListaLinealSE!) II // c o n s t r u c t o r
return false;
// C r e a r e l e l e m e n t o a a ñ a d i r
C E l e m e n t o q - new C E 1 e m e n t o ! o b j . n u i l ) ;
// S i l a l i s t a no e s t á v a c i a , e n c o n t r a r e l p u n t o de i n s e r c i ó n
C E l e m e n t o e l e m A n t e r i o r = p;
C E l e m e n t o e l e m A c t u a l = p;
// P o s i c i o n a r s e en e l e l e m e n t o i
f o r ( i n t n - 0: n < i : n++)
[
e lem Anterior = elemActual;
ele m Actual = e l e m A c t u a l . s i g u i e n t e ;
I
// Do s c a s o s :
// 1) I n s e r t a r a l p r i n c i p i o d e l a l i s t a
// 2 ) I n s e r t a r d e s p u é s d e l a n t e r i o r ( i n c l u y e i n s e r t a r a l f i n a l )
i f ( elem Anterior = e l e m A c t u a l ) // i n s e r t a r a l p r i n c i p i o
I
q . s i g u i e n t e = p:
p = q ; // c a b e c e r a
I
else // i n s e r t a r d e s p u é s d e l anterior
(
q . s ig u i e n t e = elemActual:
e l e m A n t e r i o r . s i g u i e n t e = q;
I
return true:
p u b lic Object b o r r a r ( i n t i)
I
// B o r r a r e l e l e m e n t o de l a p o s i c i ó n i
i n t númeroDeElementos = tamaño();
i f ( i > = n ú m e r o D e E l e m e n t o s || i < 0)
r e t u r n n u l 1;
// E n t r a r en l a l i s t a y e n c o n t r a r el I n d i c e del elemento
C E l e m e n t o e l e m A n t e r i o r - p;
C E l e m e n t o e l e m A c t u a l - p;
// P o s i c i o n a r s e en e l e l e m e n t o i
f o r ( i n t n = 0: n < i : n + + )
I
elem Anterior = elemActual;
ele mActual = e l e m A c t u a l . s i g u i e n t e ;
I
// D o s c a s o s :
// 1) B o r r a r e l p r i m e r e l e m e n t o de l a l i s t a
// 2 ) B o r r a r e l s i g u i e n t e a e l e m A n t e r i o r ( e l e m A c t u a l )
i f ( e l e m A c t u a l = = p ) // 1)
p = p . s i g u i e n t e ; // c a b e c e r a
e l s e // 2)
e le m A n te rio r.sig u ie n te = elem Actual. s ig u i e n t e ;
r e t u r n e l e m A c t u a l . d a t o s ; II r e t o r n a r el elemento borrad o.
// El e l e m e n t o r e f e r e n c i a d o p o r e l e m A c t u a l s e r á e n v i a d o a l a
// b a s u r a ( b o r r a d o ) al q u e d a r d e s r e f e r e n c i a d o . p o r s e r
// e l e m A c t u a l una v a r i a b l e l o c a l .
pu b lic Object b o r r a r ú lt im o ( )
I
// B o r r a r e l ú l t i m o e l e m e n t o
re tu rn b o r r a r ! t a m a ñ o t ) - 1);
C E l e m e n t o q = p: // r e f e r e n c i a a l prim er elemento
// P o s i c i o n a r s e en e l e l e m e n t o i
f o r ( i n t n = 0: n < i : n + + )
q = q.siguiente:
// R e t o r n a r l o s datos
return q.datos:
//////////////////////////////////////////////////////////////////
C om o ejercicio, supongam os que deseam os crear una lista lineal sim plem ente
enlazada con la intención de alm acenar los nom bres de los alum nos de un deter
m inado curso y sus notas de la asignatura de P rogram ación. S egún este enunciado
¿a qué tipo de objeto liará referencia cada elem ento de la lista? Pues, a objetos
cuya estructura interna sea capaz de alm acenar un nom bre (dato de tipo S t r in g ) y
una nota (dato de tipo d o u b le). A dem ás, estos objetos podrán recibir una serie de
m ensajes con la intención de extraer o m odificar su contenido. L a clase represen
tativa de los objetos descritos la vam os a denom inar C D atos y puede escribirse de
la form a siguiente:
public c la ss CDatos
I
// A t r i b u t o s
p r i v a t e S t r i n g nombre:
p r i v a t e double nota:
// M é t o d o s
p u b lic CDatosC) I I // c o n s t r u c t o r s i n p a r á m e t r o s
public CDatostString nom, d o u b l e n ) // c o n s t r u c t o r c o n p a r á m e t r o s
I
nombre = nom:
n o t a = n:
I
nombre = nom:
)
public v o i d a s i g n a r N o t a ( d o u b l e n)
I
nota - n;
I
public double o b t e n e r N o t a ()
I
return nota:
Sólo nos queda realizar una aplicación que utilizando las clases C ListaLineal
S E y C D atos cree una lista lineal y ponga en práctica las distintas operaciones que
sobre ella pueden realizarse. La figura siguiente m uestra de form a gráfica la es
tructura de datos que querem os construir. O bserve que. en realidad, la lista lo que
m antiene son referencias a los datos (objetos C D atos) y no los datos en sí, aun
que, p o r sencillez, tam bién resulta aceptable pensar que éstos form an parte de la
lista lineal. La variable p es una referencia (ref_e0) al elem ento de índice 0; este
elem ento m antiene una referencia (ref_e¡) al elem ento de la lista de índice 1 y una
referencia (ref_d,¡) al objeto de datos correspondiente, y así sucesivam ente.
p = ref_e 0
//////////////////////////////////////////////////////////////////
// C r e a r una lista lineal sim plemente en laz ada
//
5 18 JA VA : C U R SO DE PRO G R A M A CIÓ N
public c la ss Test
I
public static void m o st ra rL is ta íC L is ta L in e a lS E lse)
I
// M o s t r a r t o d o s l o s e l e m e n t o s d e l a lista
i n t i = 0 , tam = l s e . s i z e í ) ;
CDatos obj;
w h i l e ( i < tam)
(
o b j = ( C D a t o s ) l s e . o b t e n e r ( i );
System .out.p rintlníi + ".- " + o b j . obte nerNombreí ) + " " +
ob j.o b te n e rN o taí)):
i++;
II L e e r d a t o s y a ñ a d i r l o s a l a l i s t a
S t r i n g n o mbr e ;
double nota;
int i = 0 ;
S y s t e m . o u t .p rin t ln ( " In tr o d u c ir datos. F i n a li z a r con C t r l + Z . " ) :
System .out.printí"nom bre: "):
w h i le ((nombre = L e e r . d a t o í ) ) != n u i l )
I
System .out.p rin tí"nota: ” );
n o t a = L e e r . d a t o D o u b l e ( ):
1s e . a ñ a d i r A lF in a l( n e w CDatosínombre, nota));
System .out.printí"nom bre: ");
// A ñ a d i r un o b j e t o a l p r i n c i p i o
ls e . a ñ a d ir A lP r in c ip io ín e w C D a t o s í" a b c d " . 10));
II A ñ a d i r un o b j e t o en l a p o s i c i ó n 1
1 s e . a ñ a d i r ( 1. new C D a t o s í " d e f g " , 9 . 5 ) ) ;
S y s t e m . o u t . p r i n t l n i " \ n " );
// M o s t r a r e l p r i m e r o
C D a t o s o b j = ( C D a t o s )1 s e . o b t e n e r P r i m e r o í ):
Sy ste m .o u t.p rin tln i"P rim e ro : " + obj.obtenerNom breí) + " " +
ob j.o b te n e rN o taí));
// M o s t r a r e l ú l t i m o
o b j = ( C D a t o s ) l s e . o b t e n e r Ú l t i m o í );
CA PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 1 9
// B o r r a r e l e l e m e n t o de Í n d i c e 2
obj = ( C D a t o s ) l s e . b o r r a r { 2 ) :
i f ( o b j = = n u l 1)
S y s t e m . o u t . p r i n t l n ( " E r r o r : e l e m e n t o no b o r r a d o ” ) :
// M o d i f i c a r e l e l e m e n t o de I n d i c e 1
obj = ( C D a t o s ) 1 s e . o b t e n e r ( 1 ) ;
obj,asignarNota(9);
// M o s t r a r t o d o s
System .out.pri n t l n ( " L i s t a : " ) :
m ostrarLista(lse);
Clase LinkedList
L a clase L in k e d L is t pertenece a la biblioteca de Java; está incluida en el paquete
java.util. T iene unas características sim ilares a nuestra clase C ListaLinealSE . La
tabla siguiente m uestra algunos de los m étodos proporcionados por esta clase:
M é to d o S ig n ific a d o
LinkedList Es el constructor de la clase. T iene uno sin parám etros.
size D evuelve un valor de tipo in t correspondiente al núm ero de
elem entos de la lista. N o tiene parám etros.
add A ñade un elem ento en la posición i. T iene d os parám etros:
posición i y una referencia de tipo O b je c t al objeto a aña
dir. N o devuelve ningún valor.
addFirst A ñade un elem ento al principio. T iene un parám etro que es
una referencia de tipo O b je c t al objeto a añadir. N o de
vuelve ningún valor.
addLast A ñade un elem ento al final. T iene un parám etro q ue es una
referencia de tipo O b je ct al objeto a añadir. N o devuelve
ningún valor.
remove B orra el elem ento de la posición i. T iene un parám etro de
tipo int que se corresponde con la posición i del objeto a
borrar. D evuelve una referencia de tipo O b je c t al objeto
borrado o lanza una excepción si la lista está vacía o el ín
dice está fuera de lím ites.
520 JA V A : C U R S O D E P R O G R A M A C IÓ N
M é to d o S ig n ific a d o
rem oveFirst B orra el prim er elem ento. N o tiene parám etros. Devuelve
una referencia de tipo O b je c t al objeto borrado o lanza una
excepción si la lista está vacía.
rem oveLast B orra el últim o elem ento. N o tiene parám etros. Devuelve
una referencia de tipo O b je c t al objeto borrado o lanza una
excepción si la lista está vacía.
get D evuelve una referencia de tipo O b je c t correspondiente al
elem ento de la posición i, o bien lanza una excepción si la
lista está vacía o el índice está fuera de lím ites. T iene un
parám etro de tipo in t correspondiente a la posición i del
objeto que se desea obtener.
getF irst D evuelve una referencia de tipo O b je c t correspondiente al
p rim er elem ento o lanza una excepción si la lista está vacía.
N o tiene parám etros.
getLast D evuelve una referencia de tipo O b je c t correspondiente al
últim o elem ento o lanza una excepción si la lista está vacía.
N o tiene parám etros. ________________
// L e e r d a t o s y a ñ a d i r l o s a l a l i s t a
S t r i n g n o mbr e ;
double nota;
i n t i = 0:
System .out.p r i n t l n ( " In tro d u c ir datos. F in a liz a r con C t r l + Z . " ) :
S y s t e m . o u t . p r i n t ( " n o m b r e : ” ):
w h i le ((nombre = L e e r . d a t o O ) != n u i l )
I
System .out.print( "n o ta: ” ):
n o t a = L e e r . d a t o D o u b l e í );
lse .a d d L a st(n e w CDatos(nombre. nota));
S y s t e m . o u t . p r i n t ( " n o m b r e : ” );
// A ñ a d i r un o b j e t o al p r i n c i p i o
I s e .addFirst(new C D a to s ( "a b c d ". 10)):
II A ñ a d i r un o b j e t o en l a p o s i c i ó n 1
l s e . a d d d , new C D a t o s ( " d e f g ” . 9 . 5 ) ) ;
S y s t e m . o u t . p r i n t l n ( ”\ n " );
// M o s t r a r e l p r i m e r o
C D a t o s o bj = ( C D a t o s ) 1 s e . g e t F i r s t ( );
S y s te m .o u t .p rin tln í"P rim e r o : " + o b j .obtenerNombre() + " ” +
obj.obtenerN ota());
// M o s t r a r e l ú l t i m o
o bj = ( C D a t o s ) l s e . g e t L a s t í ):
S y s t e m . o u t . p r i n t l n ( ”Ú 1 t i m o : ” + o b j .obtenerNombre() + " ” +
obj.obtenerNota());
// M o s t r a r t o d o s
S y s t e m . o u t . p r i n t l n ( " L i s t a : ” ):
m o stra rL i s t a ( 1s e ) ;
// B o r r a r el e l e m e n t o de I n d i c e 2
o b j = ( C D a t o s ) 1 s e . r e m o v e ( 2 );
// M o d i f i c a r e l e l e m e n t o de i n d i c e 1
o bj = ( C D a t o s ) 1 s e . g e t ( 1 ) ;
obj.asignarN ota(9);
// M o s t r a r t o d o s
S y ste m .o u t.p r i n t l n ( " Li s t a : " ) :
m o stra rL i s t a ( 1s e ) :
522 J A V A : C U R S O D E P R O G R A M A C IÓ N
LISTAS CIRCULARES
U na lista circular es una lista lineal en la que el últim o elem ento apunta al prim e
ro. E ntonces es posible acceder a cualquier elem ento de la lista desde cualquier
punto dado. L as operaciones sobre una lista circular resultan m ás sencillas, ya que
se evitan casos especiales. P or ejem plo, el m étodo a ñ a d ir de la clase CListaLi-
n ea lS E expuesta anteriorm ente contem pla dos casos: insertar al principio de la
lista e insertar a continuación de un elem ento. C on una lista circular, estos dos ca
sos se reducen a uno. La siguiente figura m uestra cóm o se ve una lista circular
sim plem ente enlazada.
C uando recorrem os una lista circular, direm os que hem os llegado al final de
la m ism a cuando nos encontrem os de nuevo en el punto de partida, suponiendo,
desde luego, que el punto de partida se guarda d e alguna m anera en la lista; por
ejem plo, con una referencia fija al m ism o. E sta referencia puede ser al primer
elem ento de la lista; tam bién puede ser al últim o elem ento, en cuyo caso también
es conocida la dirección del p rim er elem ento. O tra posible solución sería poner un
elem ento especial identificable en cada lista circular com o lugar de partida. Este
elem ento especial recibe el nom bre de elem ento de cabecera de la lista. Esto pre
senta, adem ás, la ventaja de que la lista circular no estará nunca vacía.
Com o ejem plo, vam os a construir una lista circular con una referencia fija al
últim o elem ento. U na lista circular con una referencia al últim o elem ento es equi
valente a una lista lineal recta con dos referencias, una al principio y otra al final.
Para co n struir una lista circular, prim ero tendrem os que definir la clase de
objetos que van a form ar parte de la m ism a. P or ejem plo, cada elem ento de la lista
puede definirse com o una estructura de datos con dos m iem bros: una referencia al
elem ento siguiente y otra al área de datos. El área de datos puede ser de un tipo
predefinido o de un tipo definido por el usuario. S egún esto, el tipo de cada ele
m ento de la lista puede venir definido de la form a siguiente:
// M é t o d o s
p r í v a t e C E l e m e n t o O I I // c o n s t r u c t o r
p r i v a t e C E l e m e n t o t O b j e c t d, C El em en to s ) // c o n s t r u c t o r
I
d a t o s = d;
s i g u i e n t e = s;
V em os que p o r tratarse de una lista lineal sim plem ente enlazada, aunque sea
circular, la estructura de sus elem entos no varían con respecto a lo estudiado ante
riorm ente.
Podem os autom atizar el proceso de im plem entar una lista circular diseñando
una clase C L istaC ircularSE (C lase Lista C ircular Sim plem ente Enlazada) que
proporcione los atributos y m étodos necesarios para crear cada elem ento de la
lista, así com o para perm itir el acceso a los m ism os. E sta clase nos perm itirá pos
teriorm ente d eriv ar otras clases que sean m ás específicas; p o r ejem plo, una clase
para m anipular p ila s o una clase para m anipular colas. E stas estructuras de datos
las estudiarem os un poco m ás adelante.
Clase CListaCircularSE
La clase C ListaC ircularSE que vam os a im plem entar incluirá un atributo últim o
que valdrá n u il cuando la lista esté vacía y cuando no, referenciará siem pre a su
último elem ento; una clase interna, C E lem ento, que definirá la estructura de los
elem entos; y los m étodos indicados en la tabla siguiente:
M é to d o S ig n ific a d o
obtener D evuelve el elem ento de la posición i, o bien n u il si la lista
está vacía o el índice está fuera de lím ites. Tiene un pará
m etro correspondiente a la posición / del objeto que se de
sea obtener.
lllllllllllllllllllllllllllllllllllllllllllllllllllllllltlllllllll
II L i s t a lineal circular sim plemente en laza da
//
public c la ss CListaCircularSE
I
// ú l t i m o : r e f e r e n c i a e l ú l t i m o e l e m e n t o .
// ú l t i m o . s i g u i e n t e r e f e r e n c i a a l p r i m e r e l e m e n t o de l a lista,
p r i v a t e CElemento ú l t i m o = n u i l ;
public in t tamañoO
I
// D e v u e l v e el nú me r o de e l e m e n t o s de l a l i s t a
i f ( ú l t i m o = = n u i l ) r e t u r n 0:
C E l e m e n t o q = ú l t i m o . s i g u i e n t e : // p r i m e r e l e m e n t o
i n t n — 1: // nú me r o d e e l e m e n t o s
w h i 1e ( q ! = ú l t i m o )
I
n++;
q = q. s i g u i e n t e :
)
return n;
I
C A PÍTU LO 13: ESTRU C TU R A S DINÁM ICA S 5 2 5
p u b lic Object b o r r a r O
I
// D e v u e l v e una r e f e r e n c i a a l o s d a t o s del p r i m e r e l e m e n t o de
// l a l i s t a y b o r r a e s t e e l e m e n t o ,
i f ( últim o == n u l1 )
I
System .err.printlní "Lista vac1a\n" ):
return n u il:
CElemento q = ú l t i m o . s i g u i e n t e ;
O b je ct obj = q . d a t o s :
526 JA VA: C U R SO D E PROGRAM A CIÓN
i f ( q == último )
último = n u il;
el se
últim o.siguiente = q.siguiente;
re tu rn obj;
// El e l e m e n t o r e f e r e n c i a d o p o r q e s e n v i a d o a l a b a s u r a , al
// q u e d a r d e s r e f e r e n c i a d o c u a n d o f i n a l i z a e s t e mé t o d o p o r s e r
// q una v a r i a b l e 1 o c a l .
C E l e m e n t o q = ú l t i m o . s i g u i e n t e ; // p r i m e r e l e m e n t o
// P o s i c i o n a r s e en e l e l e m e n t o i
f o r ( i n t n = 0; n < i ; n + + )
q = q.siguiente;
// R e t o r n a r l o s datos
return q.datos:
llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
//////////////////////////////////////////////////////////////////
// C r e a r un a lista lineal circular simplemente enlazada
//
public c la ss Test
I
public static void m o st ra rL is t a ( C L is t a C irc u la rS E Icse)
I
// M o s t r a r t o d o s l o s e l e m e n t o s de l a lista
i n t i = 0. tam = 1 e s e . t a m a ñ o ( );
CDatos obj;
// L e e r d a t o s y a ñ a d i r l o s a l a l i s t a
S t r i n g n o mbr e :
double nota:
i n t i = 0:
System .out.p r i n t í n ( " In t r o d u c ir datos. F i n a li z a r con C t r l + Z . " ) :
System .out.printí"nom bre: ");
w h i l e ( ( n o m b r e = L e e r . d a t o O ) !*= n u i l )
I
System .out.p rintí"nota: ” );
n o t a = L e e r . d a t o D o u b l e í ):
1e s e . a ñ a d i r A l F i n a l ( n e w C D a t o s ( n o m b r e . nota)):
System .out.print("nom bre: ");
I
// A ñ a d i r un o b j e t o a l p r i n c i p i o
lcse .a ñ a d irA lP rin c ip io (n e w C D a to s("a b e d ", 10)):
S y s t e m . o u t . p r i n t l n ( “\ n " ) ;
// M o s t r a r l a 1 i s t a
S y s t e m . o u t . p r i n t í n ( " L i s t a : “ );
m o s t r a r L i s t a ( 1e s e ) :
// B o r r a r e l e l e m e n t o p r i m e r o
C D a t o s o bj = ( C D a t o s ) l c s e . b o r r a r ( );
// M o s t r a r l a l i s t a
S y s te m .o u t.p rin tln ("L is ta :");
m o strarLista(lcse);
I
PILAS
Una p ila es una lista lineal en la que todas las inserciones y supresiones se hacen
en un extrem o de la lista. U n ejem plo de esta estructura es una pila de platos. En
ella, el añadir o q uitar platos se hace siem pre p o r la parte superior de la pila. Este
528 JA V A: C U R SO DF. PRO G R A M A CIÓ N
tipo de listas recibe tam bién el nom bre de listas U F O (last in fir s t out - últim o en
entrar, prim ero en salir).
Las operaciones de m eter y sacar en una pila son conocidas en los lenguajes
ensam bladores com o p u sh y p o p , respectivam ente. La operación de sacar un ele
m ento de la pila suprim e dicho elem ento de la misma.
Para trabajar con pilas podem os diseñar una clase C P ila (C lase P ila) derivada
de la clase base C ListaC ircularSE , que soporte los siguientes m étodos:
//////////////////////////////////////////////////////////////////
// P i l a : l i s t a en l a q u e t o d a s l a s i n s e r c i o n e s y s u p r e s i o n e s se
// h a c e n en un e x t r e m o de l a mi s ma.
//
public class CPila extends CListaCircularSE
(
publ i c C P i l a O I)
p u b l i c O b j e c t s a c a r D e P i 1a ( )
(
return b o r r a r t );
1
I
//////////////////////////////////////////////////////////////////
Para m eter el elem ento referenciado por el parám etro obj en la pila, el m étodo
m eterE nP ila invoca al m étodo añadírA lP rincipio de la clase base C ListaCircu-
la rS E ; y para sacar el elem ento de la cim a de la pila y elim inarlo de la m ism a, el
m étodo sacarD eP ila invoca al m étodo borrar de la clase base.
COLAS
U na cola es una lista lineal en la que todas las inserciones se hacen por un extre
m o de la lista (por el final) y todas las supresiones se hacen p o r el otro extrem o
(por el principio). Por ejem plo, una fila en un banco. E ste tipo de listas recibe
tam bién el nom bre de listas F IF O (first in ftr s t out - prim ero en entrar, prim ero en
salir). Este orden es la única form a de insertar y recuperar un elem ento de la cola.
U na cola no perm ite acceso aleatorio a un elem ento específico. Tenga en cuenta
que la operación de sacar elim ina el elem ento de la cola.
Para trabajar con colas podem os diseñar una clase C Cola (C lase Cola) deri
vada de la clase base C ListaC ircularSE , que soporte los siguientes m étodos:
//////////////////////////////////////////////////////////////////
// C o l a : l i s t a en l a que t o d a s l a s i n s e r c i o n e s s e h a c e n p o r un
// e x t r e m o de l a l i s t a ( p o r e l f i n a l ) y t o d a s l a s s u p r e s i o n e s se
530 J A V A : C U R S O D E P R O G R A M A C IÓ N
P ara m eter el elem ento referenciado por el parám etro o b j en la cola, el méto
d o m eterE nC ola invoca al m étodo añadirA lF inal de la clase b ase CListaCircular-
S E ; y para sacar el elem ento de la cola y elim inarlo de la m ism a, el método
sacarD eC ola invoca al m étodo borrar de la clase base.
EJEMPLO
El siguiente ejem plo m uestra cóm o u tilizar la clase C ListaC ircularSE y sus deri
vadas C Pila y C Cola. P rim eram ente cream os una pila y una cola de objetos de la
clase C D atos y a continuación cream os una lista circular. Para com probar que las
listas se han creado correctam ente, m ostram os a continuación los contenidos de
las m ism as. A dem ás, para certificar que cuando se saca un elem ento de una pila o
de una cola éste es elim inado, intentam os m ostrar por segunda vez el contenido de
las m ism as; el resultado es un m ensaje d e que están vacías.
En este ejem plo aparece tam bién por prim era vez el operador in s ta n c e o f cuya
sintaxis es la siguiente:
CA PÍTU LO 13: ESTRU C TU R A S D IN Á M ICA S 5 3 1
//////////////////////////////////////////////////////////////////
/ / P i l a s y c o l a s
//
p u b l i c c l a s s T e s t
I
p u b l i c s t a t i c v o i d m o s t r a r L i s t a í C L i s t a C i r c u l a r S E l i s t a )
(
/ / M o s t r a r t o d o s l o s e l e m e n t o s d e l a l i s t a
i n t i - 0 . t a m = 1 i s t a . t a m a ñ o t ) :
C D a t o s o b j :
w h i l e ( i < t a m )
I
if (lista i n s t a n c e o f CPila)
obj - ( C D a t o s ) ( ( C P i l a ) l i s t a ) . s a c a r D e P i l a ( ) :
e l s e if ( l i s t a i n s t a n c e o f CCola)
obj = ( C D a t o s ) ( ( C C o l a ) l i s t a ) . s a c a r D e C o l a ( ) :
e l s e
I
i++:
c o n t i n u e ;
S y s t e m . o u t . p r i n t l n ( i + " + o b j . o b t e n e r N o m b r e í ) + " ” +
o b j . o b t e n e r N o t a í ) ) ;
i ++;
I
i f ( t a m = 0 ) S y s t e m . o u t . p r i n t l n í " 1 i s t a v a c i a ” ) :
p u b l i c s t a t i c v o i d m a i n í S t r i n g [ ] a r g s )
I
/ / C r e a r u n a p i l a y u n a c o l a v a c i a s
C P i la pila - n e w C P ilaí):
CCola cola = n e w C C o l a í );
II L e e r d a t o s y a ñ a d i r l o s a a m b a s
S t r i n g n o m b r e :
d o u b l e n o t a :
i n t i = 0 :
532 JA V A : C U R SO DE PRO G R A M A CIÓ N
// M o s t r a r l a p i l a
S y s t e m . o u t . p r i n t í n ( " \ n P i 1a : " ) :
m ostrarLista(pila):
// M o s t r a r l a p i l a p o r s e g u n d a vez
S y s t e m . o u t . p r i n t l n ( " \ n P i 1a : “ );
mostrarLi s t a ( p ila ) :
// M o s t r a r l a c o l a
Syste m .o u t.p rin tln ("\n C o la :");
m ostrarLista(cola);
// M o s t r a r l a c o l a p o r s e g u n d a vez
System .out.pri n t ln ( "\ n C o la : ") ;
m ostrarLista(cola);
// C r e a r una l i s t a c i r c u l a r
C L i s t a C i r c u l a r S E l c s e = new C L i s t a C i r c u l a r S E ( ) ;
1 e s e . a ñ a d i r A l F i n a l ( n e w C D a t o s ( "1 e s e " , 1 0 ) ) :
// M o s t r a r l a l i s t a c i r c u l a r
System .out.pri n t l n í " \ n lc s e : "):
mostrarLi s t a ( lc s e );
1
I '
se m ostrarán los siguientes resultados, los cuales indican que el operador in sta n -
ceo f ha funcionando correctam ente discrim inando la clase de objeto que se desea-
CA PÍTU LO 13: ESTRU C TU R A S D IN Á M ICA S 5 3 3
ba m ostrar en cada instante, y que las operaciones de sacar en las pilas y colas
elim inan el objetado sacado de las m ism as.
Pila:
0.- A l u mn o 3 9 . 5
1. - A l u mn o 2 8 . 5
2.- Al umno 1 7. 5
Pila:
lista vacia
Col a :
0.- A l u mn o 1 7.5
1 . • A l u m n o 2 8.5
2 .- Alu m n o 3 9.5
Col a :
l i s t a vacia
1e s e :
T am bién se puede observar en estos resultados, que en las pilas el últim o ob
jeto en entrar es el prim ero en salir y en las colas, el prim ero en entrar es el prim e
ro en salir.
Las operaciones sobre una lista doblem ente enlazada norm alm ente se realizan
sin ninguna dificultad. S in em bargo, casi siem pre es m ucho m ás fácil la m anipu
lación de las m ism as cuando existe un doble enlace entre el últim o elem ento y el
prim ero, estructura que recibe el nom bre de lista circular doblem ente enlazada.
Para m overse sobre una lista circular, es necesario alm acenar de alguna m anera
un punto de referencia; p o r ejem plo, m ediante una referencia al últim o elem ento
de la lista.
Para co n struir una lista de este tipo, prim ero tendrem os que definir la clase de
objetos q ue van a form ar parte de la m ism a. P or ejem plo, cada elem ento de la lista
puede definirse com o una estructura de datos con tres m iem bros: una referencia al
elem ento siguiente, otra al elem ento anterior y o tra al área de datos. El área de
datos puede ser de un tipo predefinido o de un tipo definido p o r el usuario. Según
esto, el tipo de cada elem ento de la lista puede venir definido de la form a si
guiente:
// M é t o d o s
p rivate CElementoO 11 // c o n s t r u c t o r
I
P odem os autom atizar el proceso de im plem entar una lista circu lar doblem ente
enlazada, diseñando una clase C ListaC ircularD E (C lase Lista C ircular D oble
m ente Enlazada) que proporcione los atributos y m étodos necesarios para crear
cada elem ento de la lista, así com o para perm itir el acceso a los m ism os. La clase
que diseñam os a continuación cubre estos objetivos.
Clase CListaCircularDE
La clase C ListaC ircularD E que vam os a im plem entar incluirá los atributos últi
m o , actual, núrneroD eElem entos y posición. El atributo últim o valdrá n u il cuando
la lista esté vacía y cuando no, referenciará siem pre a su últim o elem ento; actual
hace referencia al últim o elem ento accedido; núrneroD eElem entos es el núm ero
de elem entos que tiene la lista y posición indica el índice del elem ento referencia-
CA PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 3 5
do p o r actual. A sim ism o, incluye una clase interna, C Elem ento, que definirá la
estructura de los elem entos, y los m étodos indicados en la tabla siguiente:
M éto d o S ig n ificad o
C ListaC ircularD E Es el constructor. Inicia últim o y actual a nuil, núm eroD e
Elem entos a 0 y posición a -1 (la posición del prim er ele
m ento de la lista es la 0).
tam año D evuelve el núm ero de elem entos de la lista. No tiene pa
rám etros.
insertar A ñade un elem ento a continuación del referenciado por
actual. El elem ento añadido p asa a ser el elem ento actual.
T iene un parám etro que es una referencia de tipo O b je c t al
objeto a añadir. N o devuelve ningún valor.
borrar B orra el elem ento referenciado p o r actual. N o tiene pará
m etros. D evuelve una referencia al objeto borrado o n u il si
la lista está vacía.
irA lSiguiente A vanza la posición a ctu a l al siguiente elem ento. Si esta po
sición coincide con núm eroD eE lem entos-1, perm anece en
ella. N o tiene parám etros y no devuelve ningún valor.
irA lA nterior R etrasa la posición actual al elem ento anterior. Si esta p o
sición coincide con la 0, perm anece en ella. N o tiene pará
m etros y no devuelve ningún valor.
irAlPrincipio Hace que la posición actual sea la 0. N o tiene parám etros y
no devuelve ningún valor.
irA lF inal H ace que la posición actual sea la núm eroD eE lem entos-1.
N o tiene parám etros y no devuelve ningún valor.
ir A l A vanza la posición a ctu a l al elem ento de índice i (el prim er
elem ento tiene índice 0). N o tiene parám etros y devuelve
tr u e si la operación de m over se realiza con éxito o false si
la lista está vacía o el índice está fuera de límites.
obtener D evuelve el elem ento referenciado p o r actual, o bien nuil
si la lista está vacía. No tiene parám etros.
obtener(i) D evuelve el elem ento de la posición i, o bien n u il si la lista
está vacía o el índice está fuera de lím ites. T iene un pará
m etro correspondiente a la posición i del objeto q ue se de
sea obtener.
m odificar Establece nuevos datos para el elem ento actual. T iene un
parám etro que es una referencia de tipo O b je c t al nuevo
objeto. N o devuelve ningún valor.
/////////////////////////////////////////////////////////////////
// La c l a s e l i s t a c i r c u l a r d o b l e m e n t e e n l a z a d a p e r m i t e m a n i p u l a r
// l o s e l e m e n t o s q u e componen una l i s t a de e s t e t i p o .
//
public class CListaCircularDE
I
p r i v a t e CElemento ú l t i m o ;
// r e f e r e n c i a a l ú l t i m o e l e m e n t o de l a l i s t a
p r i v a t e CElemento a c t u a l :
// r e f e r e n c i a a l e l e m e n t o a c t u a l en e l q u e e s t a m o s
p r i v a t e long númeroDeElementos:
// nú mero de e l e m e n t o s q u e t i e n e l a l i s t a
p riv a t e long p o sic ió n :
// p o s i c i ó n d e l e l e m e n t o a c t u a l
// M é t o d o s
p riva te CElementoO II // c o n s t r u c t o r
p u b l i c 1o ng t a m a ñ o ( )
I
// P e r m i t e s a b e r e l t a ma ñ o d e l a lista
re tu rn númeroDeElementos:
// L a s d o s l i n e a s s i g u i e n t e s i n i c i a n una l i s t a c i r c u l a r .
ú ltim o .a n te rio r = último:
ú ltim o .sig u ie n te = último:
últim o.datos = obj: // a s i g n a r d a t o s .
actual = último:
posición = 0L : II ya h a y un e l e m e n t o en l a l i s t a .
:
else II e x i s t e una l i s t a
I
q = new C E 1 e m e n t o t ):
// I n s e r t a r e l n u e v o e l e m e n t o d e s p u é s d e l actual,
a c t u a l . s i g u i e n t e . a n t e r i o r = q:
q .sig u ie n te = a c t u a l.s ig u ie n t e :
a c t u a l . s i g u i e n t e = q:
q .a n te rio r = actual:
q.datos = o b j ;
// A c t u a l i z a r p a r á m e t r o s .
posic1ón++;
// S i e l e l e m e n t o a c t u a l e s e l ú l t i m o , el nuevo elemento
// p a s a a s e r e l a c t u a l y e l ú l t i m o ,
i f ( actual = último )
ú l t i m o = q;
n ú m e r o D e E l e m e n t o s + + : // in c re m e n t a r el nú me r o de e l e m e n t o s .
p u b lic Object b o r r a r t )
I
// El mé t od o b o r r a r d e v u e l v e l o s d a t o s d e l e l e m e n t o
// r e f e r e n c i a d o p o r a c t u a l y l o e l i m i n a de l a l i s t a
// ( a l q u e d a r d e s r e f e r e n c i a d o e s e n v i a d o a l a b a s u r a )
C E l e m e n t o q:
Object obj:
i f ( ú l t i m o == n u i l ) r e t u r n ( n u i l ): II l i s t a v a c i a ,
i f ( a c t u a l = = ú l t i m o ) // s e t r a t a del ú l t i m o elemento.
I
if( númeroDeElementos — 1L ) // h a y un s o l o e l e m e n t o
I
obj = ú l t i m o . d a t o s :
ú l t i m o = a c t u a l - n u l 1:
númeroDeElementos - 0 L :
posición = -1L :
I
538 J A V A : C U R S O D E P R O G R A M A C IÓ N
else // ha y más de un e l e m e n t o
I
actual = ú ltim o .a n te rio r;
ú ltim o .s ig u ie n te .a n t e rio r = actual:
actual .sig u ie n te = ú ltim o .sig u ie n te :
obj = ú l t i m o . d a t o s ;
último = actual ;
p o sició n --;
n ú m e r o D e E l e m e n t o s - -;
I // f i n d e l b l o q u e e l s e
I // f i n d e l b l o q u e i f ( a c t u a l = = ú l t i m o )
e l s e // el e l e m e n t o a b o r r a r n o e s e l ú l t i m o
I
q = actual.siguiente:
a c t u a l . a n t e r i o r . s i g u i e n t e = q:
q.a nterio r = a c t u a l.a n t e r io r ;
obj = a c t u a l . d a t o s :
a c t u a l = q:
nú m eroDeEle m e ntos--;
I
return obj:
1
public void i r A l P r i n c i p i o ()
I
// Hace q u e l a p o s i c i ó n a c t u a l sea el principio de l a lista,
actual = ú ltim o .sig u ie n te :
p o s i c i ó n = 0L:
C A PÍTU LO 13: ESTRU C TU R A S DINÁM ICA S 5 3 9
public void i r A l F i n a l ()
(
// El f i n a l de l a l i s t a e s a h o r a l a posición actual,
actual = último:
p o s i c i ó n - n ú m e r o D e E l e m e n t o s - 1:
i r A l P r i nci p i o ( );
// P o s i c i o n a r s e en e l e l e m e n t o i
f o r ( l o n g n = 0; n < i ; n++)
i r A l S i g u i e n t e ( );
return true:
return a c tu a l.d a to s:
a c t u a l. d a t o s = pNuevosDatos:
//////////////////////////////////////////////////////////////////
• C rea una lista vacía (últim o = actual = nuil). En todo m om ento, el últim o
elem ento de la lista está apuntado por últim o, y a ctu a l apunta al elem ento
sobre el que se realizará la siguiente operación.
Ejemplo
El siguiente ejem plo m uestra cóm o utilizar la clase C ListaC ircularD E . Prim era
m ente cream os un objeto Icile, correspondiente a una lista circular doblem ente
enlazada, en la que cada elem ento alm acenará un referencia a un objeto C D atos', y
a continuación realizam os varias operaciones de inserción, m ovim iento y borrado,
para finalm ente visualizar los elem entos de la lista y com probar si los resultados
son los esperados.
//////////////////////////////////////////////////////////////////
II L i s t a circular doblemente en la za da
//
public class Test
I
public static void m o s t r a r L is t a ( C L is t a C ir e u la r D E lista)
I
// M o s t r a r todos l o s e l e m e n t o s de l a lista
CA PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 4 1
l o n g i - 0, tam - 1 i s t a . ta m añ o ():
CDatos obj:
w h i l e ( i < tam)
(
o b j - ( C O a t o s )1 i s t a . o b t e n e r t i );
System .out.println(i + ” + obj.obtenerNom bre() + ” ” +
o b j.o b t e n e rN o ta ()):
i++:
1
if (tam — 0) S y s t e m . o u t . p r i n t l n ( "1 i s t a v a c i a ” ):
1
// I n s e r t a r e l e m e n t o s
lcde.insertar(new CDatos("alum nol” . 7.8)):
lcde.insertartnew C 0atos("a lum no2". 6.5)):
lcde.insertarfnew CDatos("alum no3". 10)):
1c d e . in s e r t a r ( n e w C D a t o s ( " a l u m n o 4 " , 8.6)):
// I r a l e l e m e n t o de l a posición 2
1cd e .i r A l (2):
// B o r r a r e l e l e m e n t o a c t u a l (posición 2)
l c d e . b o r r a r ( );
// I r al a n t e r i o r
lcde.i r A l (1);
1c d e . i n s e r t a r ( n e w C D atos("nuevo alumno3", 9.5)):
// I r a l f i n a l
l c d e . i r A l F i n a K );
lcde.insertarínew CDatosf"alum no5". 8 .5 )):
// I r a l a n t e r i o r
l c d e . i r A l A n t e r i o r ( ):
l c d e . m o d i f i c a r í n e w C O a t o s ( ”a l u m n o 4 ” . 5 . 5 ) ) :
// M o s t r a r l a l i s t a
S y s t e m . o u t . p r i n t l n ( " \ n l i s t a : ” ):
m o strarlista(lcd e );
542 JA V A : C U R S O D E P R O G R A M A C IÓ N
ARBOLES
Un árbol es una estructura no lineal form ada por un conjunto de nodos y un con
ju n to de ramas.
En un árbol existe un nodo especial denom inado raíz. A sí m ism o, un nodo del
que sale alguna ram a, recibe el nom bre de nodo de bifurcación o nodo ram a y un
nodo que no tiene ram as recibe el nom bre de nodo term inal o nodo hoja.
nivel 0
nivel 1
nivel 2
A rbol
De un modo más form al, direm os que un árbol es un conjunto finito de uno o
m ás nodos tales que:
El núm ero de ram as de un nodo recibe el nom bre de grado del nodo.
El nivel de un nodo respecto al nodo raíz se define diciendo que la raíz tiene
nivel 0 y cu alquier o tro nodo tiene un nivel igual a la distancia de ese nodo al no
do raíz. El m áxim o de los niveles se denom ina p rofu n d id a d o altura del árbol.
Es útil lim itar los árboles en el sentido de que cada nodo sea a lo sum o de
grado 2. De esta form a cabe distinguir entre subárbol izquierdo y subárbol dere
cho de un nodo. Los árboles así form ados, se denom inan árboles binarios.
CA PITU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 4 3
Árboles binarios
Un árbol binario es un conjunto finito de nodos que consta de un nodo raíz que
tiene dos subárboles binarios denom inados subárbol izquierdo y subárbol d ere
cho.
Las expresiones algebraicas, debido a que los operadores que intervienen son
operadores binarios, nos dan un ejem plo de estructura en árbol binario. La figura
siguiente nos m uestra un árbol que corresponde a la expresión aritm ética:
(a + b * c ) / ( d - e / f )
Expresión algebraica
// Nodo d e un á r b o l b i n a r i o
p r i v a t e c l a s s CNodo
I
// A t r i b u t o s
p riv a t e Object datos; // r e f e r e n c i a a l o s d a t o s
p r i v a t e CNo do i z q u i e r d o : // r a i z d e l s u b á r b o l izquierdo
p r i v a t e CNo do d e r e c h o ; // r a i z d e l s u b á r b o l derecho
// M é t o d o s
public CNodoO II // c o n s t r u c t o r
544 JA V A : C U R S O D E P R O G R A M A C IÓ N
// Nodo de un á r b o l b i n a r i o
p r i v a t e c l a s s CNodo
1
// A t r i b u t o s
p r iv a t e Object datos; // r e f e r e n c i a a l o s d a t o s
p r i v a t e CNodo i z q u i e r d o : // r a í z d e l s u b á r b o l izquierdo
p r i v a t e CNodo d e r e c h o : II r a í z d e l s u b á r b o l derecho
// M é t o d o s
public CNodoO II // c o n s t r u c t o r
II M é t o d o s d e l árbol binario
Si el árbol está vacío, raíz es igual a n u il; en otro caso, raíz es una referencia
al nodo raíz del árbol y según se puede observar en el código anterior, este nodo
tiene tres atributos: una referencia a los datos y dos referencias m ás, una a su su
bárbol izquierdo y otra a su subárbol derecho.
B ásicam ente se pueden u tilizar tres form as para recorrer un árbol binario:
prcarden, inorden y postorden. C uando se utiliza la form a preorden, prim ero se
visita la raíz, después el subárbol izquierdo y por últim o el subárbol derecho; en
cam bio, si se utiliza la form a inorden, prim ero se visita el subárbol izquierdo,
después la raíz y por últim o el subárbol derecho; y si se utiliza la form a postor-
den, prim ero se visita el subárbol izquierdo, después el subárbol derecho y por úl
tim o la raíz.
C A PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 4 5
R: r a í z p r e o r d e pnre o rd e: n : R , I, D
I: s u b á rb o l i z q u i e r d o i n o r d e in onrd e :n : I, R , D
D : s u b á rb o l d e re c h o p o s to rd
r d e n : I, D , R
E videntem ente, las definiciones dadas son definiciones recursivas, y a que, re
correr un árbol utilizando cualquiera de ellas, im plica recorrer sus subárboles em
pleando la m ism a definición.
Preorden: / + a * b c - d / e f
Inorden: a + b * c / d - e / f
Postorden: a b c * + d e f / - /
Illllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
II C l a s e á r b o l binario.
//
public class CArbolBinario
I
// A t r i b u t o s d e l á r b o l binario
p r i v a t e CNodo r a í z : // r a i z del árbol
// Nodo de un á r b o l b i n a r i o
p r i v a t e c l a s s CNodo
II A t r i b u t o s
p riva te Object datos; II r e f e r e n c i a a l o s d a t o s
p r i v a t e C No do i z q u i e r d o : 1/ r a í z d e l s u b á r b o l i z q u i e r d o
p r i v a t e C No do d e r e c h o : // r a í z d e l s u b á r b o l d e r e c h o
II M é t o d o s
p u b l i c C N o d o í ) 11 II c o n s t r u c t o r
546 J A V A : C U R S O D E P R O G R A M A C IÓ N
// M é t o d o s d e l á r b o l b i n a r i o
public C A rb o lB in a rio () II // c o n s t r u c t o r
p u b lic vo id preorden(CNodo r)
I
if ( r 1- n u il )
(
// E s c r i b i r a q u í l a s o p e r a c i o n e s a r e a l i z a r
// c o n e l n o d o r e f e r e n c i a d o r
p r e o r d e n ( r . i z q u i e r d o ) ; // s e v i s i t a e l s u b á r b o l izquierdo
preorden( r.d e re ch o ): // s e v i s i t a e l s u b á r b o l derecho
/////////////////////////////////////////////////////////////////
da; esto es, si la clave buscada es m enor que la clave del nodo en el que estam os,
pasam os al subárbol izquierdo de este nodo para continuar la búsqueda, y si es
mayor, pasam os al subárbol derecho. Este proceso continúa hasta encontrar la
clave o hasta llegar a un subárbol vacío, árbol cuya raíz tiene un valor nuil.
Clase CArbolBinB
La clase C A rbolB inB que vam os a im plem entar incluirá un atributo protegido raíz
para referenciar la raíz del árbol y cuatro constantes públicas relacionadas con los
posibles errores que se pueden d ar relativos a un nodo: C O RREC TO . NO _D A-
TO S, YA_EX1STE y N O _E X ISTE . El atributo raíz valdrá n u il cuando el árbol esté
vacío. A sim ism o, incluye una clase interna, C N odo, que definirá la estructura de
los nodos, y los m étodos indicados en la tabla siguiente:
/////////////////////////////////////////////////////////////////
// C l a s e a b s t r a c t a : á r b o l b i n a r i o de b ú s q u e d a . P a r a u t i l i z a r l o s
// métodos p r o p o r c i o n a d o s p o r e s t a c l a s e , te ndrem os que c r e a r
// una s u b c l a s e de e l l a y r e d e f i n i r l o s m é t o d o s :
// comparar, p r o c e s a r y v i s i t a r l n o r d e n .
II
public abstract class CArbolBinB
CA PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 4 9
// A t r i b u t o s d e l á r b o l b i n a r i o
p r o t e c t e d CNodo r a í z = n u i l ; // r a í z del árbol
// Nodo de un á r b o l b i n a r i o
p r i v a t e c l a s s CNodo
// A t r i b u t o s
p riv a te Object datos; // referen cia a lo s datos
p r i v a t e CNodo i z q u i e r d o ; // r a í z del su b á r b o l i z q u i e r d o
p r i v a t e C No do d e r e c h o ; // r a í z del su b á r b o l derecho
// M é t o d o s
p u b lic CNodoí) I ) // c o n s t r u c t o r
// M é t o d o s d e l á r b o l b i n a r i o
publi c CArbolBi nB() I I // c o n s t r u c t o r
// El m ét o d o s i g u i e n t e d e b e s e r r e d e f i n i d o en una s u b c l a s e p a r a
// q u e p e r m i t a c o m p a r a r d o s n o d o s d e l á r b o l p o r e l a t r i b u t o
// q u e n e c e s i t e m o s en c a d a momento.
p u b li c a b s t r a c t i n t com p a ra r(0 b ject o b j l , O bject o b j 2);
// El m ét o d o s i g u i e n t e d e b e s e r r e d e f i n i d o en una s u b c l a s e p a r a
// q u e p e r m i t a e s p e c i f i c a r l a s o p e r a c i o n e s q u e s e d e s e e n
// r e a l i z a r con e l n o do v i s i t a d o ,
p u b lic a b str a c t void p ro c e sa r(0 b je c t o b j):
// El m ét o d o s i g u i e n t e d e b e s e r r e d e f i n i d o en una s u b c l a s e p a r a
// que i n v o q u e a " i n o r d e n " c o n l o s a r g u m e n t o s d e s e a d o s ,
p u b l i c a b s t r a c t v o i d v i s i t a r l n o r d e n ( );
CNo do a c t ú a 1 = n u i l ;
if ( nodoRaiz )
a c t u a l = r a í z ; // p a r t i r d e l a r a í z
el se
a c t u a l = r; // p a r t i r de un n o d o c u a l q u i e r a
i f ( a c t u a l != n u i l )
I
i n o r d e n ( a c t u a l . i z q u i e r d o , f a l s e ) : // v i s i t a r s u b á r b o l i z q .
// P r o c e s a r l o s d a t o s d e l n o d o v i s i t a d o
p r o c e s a r ( a c t u a l . d a t o s );
i n o r d e n ( a c t u a l . d e r e c h o , f a l s e ) : II v i s i t a r s u b á r b o l d c h o .
/////////////////////////////////////////////////////////////////
El parám etro obj se refiere al objeto de datos, que suponem os apuntado por
un nodo del árbol, al que deseam os acceder. Este m étodo devuelve un valor nuil
si el objeto referenciado p o r o b j no se localiza en el árbol, o bien una referencia al
objeto de datos del nodo localizado.
P o r definición de árbol de búsqueda, sabem os que sus nodos tienen que estar
ordenados utilizando com o clave alguno de los atributos de obj. Según esto, el
m étodo buscar se escribe aplicando estrictam ente esa definición; esto es, si la cla
ve buscada es m enor que la clave del nodo en el que estam os, continuam os la
búsqueda en su subárbol izquierdo y si es m ayor, entonces continuam os la bús
queda en su subárbol derecho. E ste proceso continúa hasta encontrar la clave, o
bien hasta llegar a un subárbol vacío (subárbol cu y a raíz tiene un v alor n u il), en
CA PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 5 1
cuyo caso se inserta en ese lugar un nuevo nodo que alm acenará la referencia obj
al objeto de datos.
Para saber si una clave es igual, m enor o m ayor que otra invocarem os al mé
todo com parar pasando com o argum entos los objetos de datos que contienen los
atributos que se desean com parar. C om o tales atributos, dependiendo de la aplica
ción, pueden ser bien de algún tipo num érico, o bien de tipo alfanum érico o alfa
bético, la im plem entación de este m étodo hay que posponerla al diseño de la
aplicación que utilice esta clase, razón p o r la que com parar ha sido definido com o
un m étodo abstracto. Para ello, com o verem os un poco m ás adelante, derivarem os
una nueva clase de ésta, y redefinirem os este m étodo.
El proceso realizado por este m étodo lo prim ero que hace es verificar si ya
hay un nodo con estos datos en el árbol (para realizar esta operación se sigue el
552 JA V A : C U R SO DE PROGRAM A CIÓN
// C o m i e n z a l a b ú s q u e d a p a r a ve rificar si ya h a y un n o d o con
// e s t o s d a t o s en e l á r b o l
w h i l e ( a c t u a l ! = n u l 1)
1
if ((nComp = co m p a r a r ( o b j , a c t u a l . d a t o s )) = = 0)
b r e a k : // s e e n c o n t r ó e l n o do
el se
I
últim o = actual :
i f ( nComp < 0 ) // b u s c a r en e l subárbol izquierdo
actual = a ctu a l.izq u ie rd o :
else // b u s c a r en e l subárbol derecho
actual = actual.derecho;
En el ejem plo que m uestra la figura anterior, se desciende por el árbol hasta
encontrar el nodo a borrar (17). La variable actual representa la raíz del subárbol
en el que continúa la búsqueda; inicialm ente su valor es raíz. La variable m arcado
apunta al nodo a borrar una vez localizado, el cual es sustituido por su sucesor a
la derecha (21) pasando su subárbol izquierdo (13) a serlo ahora del nodo m ás a la
izquierda (18) en el subárbol derecho (21). C uando finalice la ejecución del m éto
do, el nodo que queda referenciado por m arcado será enviado a la basura, ya que
m arcado es una variable local y desaparecerá. El proceso detallado, se presenta a
continuación y contem pla los casos m encionados:
R esum iendo, el m étodo borrar prim ero localiza el nodo a borrar y lo marca
(queda referenciado por m arcado). D espués analiza si éste tiene descendientes y
cuántos; en función de esto obtiene su sucesor (queda referenciado p o r sucesor).
Finalm ente, rehace los enlaces dejando fuera el nodo m arcado para borrar. Dicho
nodo será enviado a la basura en el instante en que finaliza el m étodo borrar.
if ( actual != nu il ) // s e e n c o n t r ó e l nodo
I
marcado = a c t u a l ;
i f (( a c t u a l . i z q u i e r d o = n u i l && a c t u a l . d e r e c h o = n u i l ))
// s e t r a t a de un n o d o t e r m i n a l ( n o t i e n e d e s c e n d i e n t e s )
sucesor = n u l1 ;
e l s e i f ( a c t u a l . i z q u i e r d o = = n u i l ) // n o d o s i n s u b á r b o l i z q .
sucesor = actual.derecho;
e l s e i f ( a c t u a l . d e r e c h o — n u i l ) // n o d o s i n s u b á r b o l d e r e c h o
sucesor = a c t u a l. iz q u ie rd o ;
else // n o do con s u b á r b o l i z q u i e r d o y d e r e c h o
I
// R e f e r e n c i a d e l s u b á r b o l d e r e c h o d e l nodo a b o r r a r
sucesor = actual = a c t u a l.derecho;
CA PÍTU LO 13: ESTRU C TU R A S DINÁM ICA S 5 5 5
// D e s c e n d e r a l n o d o más a l a i z q u i e r d a en e l s u b á r b o l
// d e r e c h o de e s t e n o d o ( e l de v a l o r más p e q u e ñ o ) y h a c e r
// que e l s u b á r b o l i z q u i e r d o d e l n o d o a b o r r a r s e a a h o r a
// e l s u b á r b o l i z q u i e r d o de e s t e n o do ,
w h i l e ( a c t u a l . i z q u i e r d o != n u i l )
actual = a c t u a l. izquierdo:
a c t u a l. izquierdo = m arcado.izquierdo:
I
// E l i m i n a r e l n o d o y r e h a c e r lo s enlaces
i f ( ú l t i m o != nul 1 )
I
if ( nAnteriorComp < 0 )
ú ltim o.izquierdo = sucesor:
el se
último.derecho = sucesor:
I
el se
ra iz = sucesor:
r e t u r n m a r c a d o . d a t o s : ; // CORRECTO
// " m a r c a d o " s e r á e n v i a d o a l a b a s u r a
I
e l s e // e l n odo b u s c a d o no e s t á en e l árbol
r e t u r n n u i l ; // N 0 _ E X I S T E
i
Pensem os ahora en el proceso que deseam os realizar con cada nodo accedido.
En el ejem plo, sim plem ente nos lim itarem os a m ostrar los datos nom bre y nota.
556 JA V A : C U R S O D E P R O G R A M A C IÓ N
Según esto, el m étodo p ro cesa r obtendrá los datos nom bre y nota del objeto
C D atos pasado com o argum ento y los m ostrará.
Finalm ente, escribirem os el m étodo visitarlnorden para que perm ita recorrer,
en nuestro caso, el árbol en su totalidad.
/////////////////////////////////////////////////////////////////
// C l a s e d e r i v a d a de l a c l a s e a b s t r a c t a C A r b o l B i n B . Redefine los
// m é t o d o s : c o m p a r a r , p r o c e s a r y v i s i t a r l n o r d e n .
//
public class CArbolBinarioDeBusqueda extends CArbolBinB
I
// P e r m i t e c o m p a r a r d o s n o d o s d e l á r b o l p o r e l atributo n o mbr e ,
p u b l i c i n t c o m p a r a r ( O b j e c t o b j l . O b j e c t o b j 2>
I
String s t r l = new S t r i n g ( ( ( C D a t o s ) o b j l ) . o b t e n e r N o m b r e ( ) ) :
String s t r 2 = new S t r i n g t ( ( C D a t o s ) o b j 2 ) . o b t e n e r N o m b r e í ) ) :
return strl.com pareTo(str2):
// P e r m i t e m o s t r a r l o s d a t o s d e l nodo v i s i t a d o ,
p u b lic void p ro ce sa r(O b je c t obj)
I
S t r i n g nombre = new S t r i n g l ( ( C D a t o s ) o b j ) . o b t e n e r N o m b r e ( ) ) :
d o u b l e n o t a = ( ( C D a t o s ) o b j ) . o b t e n e r N o t a í );
System .out.println(nom bre + " " + nota):
// V i s i t a r l o s n o d o s d e l á r b o l ,
p u b lic void vi s i t a r I n o r d e n ( )
I
// S i e l s e g u n d o a r g u m e n t o e s t r u e , l a v i s i t a c o m i e n z a
// en l a r a í z i n d e p e n d i e n t e m e n t e d e l p r i m e r a r g u m e n t o .
inorden(nul1 , t r u e ) ;
)
I
/////////////////////////////////////////////////////////////////
A hora puede com probar de una form a clara que los m étodos com parar, p ro
cesar y visitarlnorden dependen del tipo de objetos que alm acenem os en el árbol
q ue construyam os. Por esta razón no pudieron ser im plem entados en la clase
C ArbolBinB, sino que hay que im plem entarlos para cada caso particular.
O bserve que com o los parám etros de los m étodos com parar y p ro cesa r son
genéricos, referencias de tipo O b je c t, deben convertirse explícitam ente en refe
rencias a la clase de objetos que realm ente representan; en nuestro caso a referen
cias a objetos de la clase CD atos, de lo contrario no tendrem os acceso a los
m étodos explícitos de esta clase.
C A PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 5 7
F inalm ente, escribirem os una aplicación Test que, utilizando la clase C Ar
bolB inarioD eB usqueda, cree un objeto arbolbb correspondiente a un árbol binario
de búsqueda en el que cada nodo haga referencia a un objeto C D atos que encap-
sule el nom bre de un alum no y la nota de una determ inada asignatura que está
cursando. C on el fin de probar que todos lo m étodos proporcionados por la clase
funcionan adecuadam ente (piense en los m étodos heredados y en los redefinidos),
la aplicación realizará las operaciones siguientes:
2. Solicitará parejas de datos nom bre y nota, a partir de las cuales construirá los
objetos C D atos que añadirem os com o nodos en el arbolbb.
4. Finalmente, m ostrará los datos alm acenados en el árbol para com probar que to
do ha sucedido com o esperábam os.
//////////////////////////////////////////////////////////////////
// C r e a r un á r b o l binario de b ú s q u e d a
II
p u b lic c l a s s Test
I
public static void m a in íS t r in g [ ] args)
i
CArbolBinarioDeBusqueda a r b o l b b = new C A r b o l B i n a r i o D e B u s q u e d a í ):
II L e e r d a t o s y a ñ a d i r l o s al árbol
S t r i n g nombre:
double nota:
i n t i - 0 , c o d;
if (cod = = C A rb o lB i n a r i o D e B u s q u e d a . Y A _ E X I S T E )
I
// S i y a e x i s t e , d i s t i n g u i m o s d o s c a s o s :
// 1. n o t a n u e v a > = 0 : c a m b i a m o s l a n o t a
// 2. n o t a n u e v a < 0 : b o r r a m o s e l n o do
C D a t o s d a t o s = ( C D a t o s ) a r b o l b b . b u s c a r í n e w C Da t o s t n o mb r e , n o t a ) ) :
i f ( n o t a > - 0)
datos.asignarN ota(nota);
el se
I
if ( a r b o lb b . b o r r a r ín e w CDatostnombre, n o t a )) = nu il)
S y s t e m . o u t . p r i n t l n í " n o b o r r a d o p o r q u e no e x i s t e ” ):
el se
S y s t e m . o u t . p r i n t l n í " n o d o b o r r a d o " );
// M o s t r a r l o s n o d o s d e l á r b o l
S y s t e m . o u t . p r i n t l n ( " \ n A r b o l : “ ):
á r b o l b b . v i s i t a r i ñorden ( ) :
I
E sto puede realizarse fácilm ente distribuyendo los nodos, según se leen,
equitativam ente a la izquierda y a la derecha de cada nodo. El proceso recursivo
que se indica a continuación, es la m ejor form a de realizar esta distribución. Para
un núm ero dado n de nodos y siendo n i (nodos a la izquierda) y n d (nodos a la de
recha) d o s enteros, el proceso es el siguiente:
C ada nodo del árbol consta de los siguientes m iem bros: d a to s, referencia al
subárbol izquierdo y referencia al subárbol derecho.
// A t r i b u t o s
p riv a t e Object datos: // r e f e r e n c i a a l o s d a t o s
p r i v a t e CNodo i z q u i e r d o : r a i z del s u b á r b o l i z q u i e r d o
p r i v a t e CNodo d e r e c h o : // r a í z d e l s u b á r b o l d e r e c h o
// M é t o d o s
p u b lic CNodoí) I 1 // c o n s t r u c t o r
Clase CArboIBinE
La clase C ArboIB inE que vam os a im plem entar incluirá un atributo protegido raíz
para referenciar la raíz del árbol. El atributo raíz valdrá n u il cuando el árbol esté
vacío. A sim ism o, incluye la clase interna C N odo que define la estructura de los
nodos, y los m étodos indicados en la tabla siguiente:
/////////////////////////////////////////////////////////////////
// C l a s e a b s t r a c t a : á r b o l b i n a r i o p e r f e c t a m e n t e e q u i l i b r a d o .
// P a r a u t i l i z a r l o s m é t o d o s p r o p o r c i o n a d o s p o r e s t a c l a s e .
C A P ÍT U L O 13: ESTRU C TU R A S D IN Á M ICA S 5 6 1
II t e n d r e m o s que c r e a r una s u b c l a s e de e l l a y r e d e f i n i r l o s
// m é t o d o s : l e e r D a t o s . c o m p a r a r , p r o c e s a r y v i s i t a r í n o r d e n .
//
public abstract class CArbolBinE
I
// A t r i b u t o s d e l á r b o l b i n a r i o
p r o t e c t e d C Nod o r a i z = n u i l ; // r a i z del árbol
// Nodo d e un á r b o l b i n a r i o
p r i v a t e c l a s s CNodo
I
II A t r i b u t o s
p riv a t e Object datos: // r e f e r e n c i a a l o s d a t o s
p r i v a t e C Nod o i z q u i e r d o ; // r a í z d e l s u b á r b o l i z q u i e r d o
p r i v a t e CNodo d e r e c h o : // r a i z d e l s u b á r b o l d e r e c h o
// M é t o d o s
publi c CNodoí) i I // c o n s t r u c t o r
// M é t o d o s d e l á r b o l b i n a r i o
public C A rb o lB inE O II // c o n s t r u c t o r
// El mé t od o s i g u i e n t e d e b e s e r r e d e f i n i d o en l a s u b c l a s e p a r a
// q u e p e r m i t a l e e r l o s d a t o s q u e s e r á n r e f e r e n c i a d o s p o r un
// n o d o d e l á r b o l . D e v u e l v e e l o b j e t o d e d a t o s ,
p u b lic a b stra c t Object le e r D a t o s í) ;
// El m ét od o s i g u i e n t e d e b e s e r r e d e f i n i d o en una s u b c l a s e p a r a
// q u e p e r m i t a c o m p a r a r d o s n o d o s d e l á r b o l p o r e l a t r i b u t o
// q u e n e c e s i t e m o s en c a d a momento.
p u b lic a b stra c t in t comparar(Object o b jl. Object obj2):
II El m ét od o s i g u i e n t e d e b e s e r r e d e f i n i d o en l a s u b c l a s e p a r a
// q u e p e r m i t a e s p e c i f i c a r l a s o p e r a c i o n e s que s e d e s e e n
// r e a l i z a r c o n el n o d o v i s i t a d o ,
pu b lic ab stract void procesaríO bject obj):
// El m ét o d o s i g u i e n t e d e b e s e r r e d e f i n i d o en l a s u b c l a s e para
// q u e i n v o q u e a " i n o r d e n " c o n l o s a r g u m e n t o s d e s e a d o s ,
p u b l i c a b s t r a c t v o i d v i s i t a r l n o r d e n í ):
CNodo n o d o = n u l 1:
i n t n i = 0 . nd = 0:
if { n — 0 )
r e t u r n n u l 1:
562 JA VA: C U R SO D E PROGRAM A CIÓN
el se
I
ni - n / 2: // n o d o s d e l s u b á r b o l i z q u i e r d o
nd - n - n i - 1; // n o d o s d e l s u b á r b o l d e r e c h o
n o d o - new C N o d o ( ) ;
nodo.datos = le e rD a t o sO ;
n o d o . i z q u i e r d o = c o n s t r u í ' r A r b o l ( n i ):
nodo.derecho = c o n s t r u í r A r b o l( n d ) :
r e t u r n nodo;
public v o id c o n s t r u í r A r b o l E q u i 1i b r a d o ( i n t n)
I
raíz - construirA rbol(n);
if ( actual != n u il && d a t o s [ 0 ] == nu il )
!
buscar(obj, a ctu a l.izq u ie rd o , datos, pos):
i f ( comparar! o bj. a c t u a l . d a t o s ) = = 0 )
if (pos[0]-- “ 0)
datos[0] = actual.datos: // n o d o e n c o n t r a d o
buscar(obj, actual.derecho, datos, pos):
I
if ( nodoRaiz )
a c t u a l = r a í z : // p a r t i r de l a r a i z
el se
a c t u a l - r; // p a r t i r de un n o d o c u a l q u i e r a
i f ( a c t u a l != n u l 1 )
I
i n o r d e n ( a c t u a l . i z q u i e r d o , f a l s e ) ; // v i s i t a r s u b á r b o l i z q .
// P r o c e s a r l o s d a t o s d e l n o d o v i s i t a d o
p r o c e s a r e a c t u a l . d a t o s );
i n o r d e n ( a c t u a l . d e r e c h o , f a l s e ) : // v i s i t a r s u b á r b o l d e h o .
/////////////////////////////////////////////////////////////////
CNodo c o n s t r u í r A r b o l ( i n t n)
Este m étodo tiene un parám etro entero que se corresponde con el núm ero de
nodos del árbol y devuelve una referencia al nodo raíz del árbol construido. En
realidad direm os que devuelve una referencia a cada subárbol construido lo que
perm ite realizar los enlaces entre nodos. O bserve que para cada nodo se ejecutan
las dos sentencias siguientes:
n o d o . i z q u i e r d o = c o n s t r u í r A r b o l ( n i );
nodo.derecho - c o n s t r u i r A r b o l ( n d ) :
que asignan a los atributos izquierdo y derecho de cada nodo, las referencias de
sus subárboles izquierdo y derecho, respectivam ente.
tro a un objeto que encapsule los datos a buscar, y otra con un parám etro m ás, la
posición del nodo a partir de la cual se q uiere realizar la búsqueda. D e esta form a
se puede bu scar un nodo aunque su clave de búsqueda esté repetida.
El m étodo leerD atos obtendrá los datos nom bre y n o ta , a p artir de ellos cons
truirá un objeto C D atos y devolverá el objeto construido p ara su inserción en el
árbol. Los m étodos com parar, p ro cesa r y visitarlnorden se definen igual que en
la clase C A rbolBinarioD eBusqueda.
/////////////////////////////////////////////////////////////////
// C l a s e d e r i v a d a de l a c l a s e a b s t r a c t a C A r b o I B i n E . R e d e f i n e los
// m é t o d o s : l e e r D a t o s . c o m p a r a r , p r o c e s a r y v i s i t a r I n o r d e n .
//
public c la ss C A rb o lB in a rio E q u i1ibrado extends CArboIBinE
I
// L e e r l o s d a t o s q u e s e r á n referenciados p o r un n o d o d e l árbol,
p u b lic Object lee rD atost)
I
S t r i n g nombre:
double nota;
S y s t e m . o u t . p r i n t ( " n o m b r e : " ) ; no mb r e = L e e r . d a t o O :
System .out.printf"nota: "); n o t a = L e e r . d a t o D o u b l e ( );
r e t u r n ( O b j e c t M n e w C D a t o s ( nombre. n o t a ) ) :
)
// P e r m i t e c o m p a r a r d o s n o d o s d e l á r b o l p o r e l atributo no mbre,
p u b lic i n t comparar(Object o b j l. Object obj2)
I
String strl = new S t r i n g ( ( ( C D a t o s ) o b j 1 ) . o b t e n e r N o m b r e í ) ) :
C A PÍTU LO 13: ESTRU C TU R A S D IN Á M ICA S 5 6 5
String s t r 2 = new S t r i n g í ( ( C D a t o s ) o b j 2 ) . o b t e n e r N o m b r e í ) ) :
return stri.com pareTo(str2):
I
// P e r m i t e m o s t r a r l o s d a t o s d e l n o do v i s i t a d o ,
p u b lic void p ro ce sa r(O b je c t obj)
I
S t r i n g nombre = new S t r i n g t ( ( C D a t o s ) o b j ) . o b t e n e r N o m b r e í ) ) :
d o u b l e n o t a = ( ( C D a t o s ) o b j ) . o b t e n e r N o t a ( ):
System .out.println(nom bre + " " + nota):
I
// V i s i t a r l o s n o d o s d e l á r b o l ,
pu b lic void v i s i t a r l n o r d e n í )
I
// S i e l s e g u n d o a r g u m e n t o e s t r u e . l a v i s i t a c o m i e n z a
// en l a r a i z i n d e p e n d i e n t e m e n t e d e l p r i m e r a r g u m e n t o .
inorden(nul1 , t r u e ) :
/////////////////////////////////////////////////////////////////
Finalm ente, escribirem os una aplicación Test que. utilizando la clase CAr-
bolB inarioE quilibrado, cree un objeto arbolbe correspondiente a un árbol binario
de búsqueda en el que cada nodo haga referencia a un objeto CDatos. De forma
resum ida la aplicación T est:
//////////////////////////////////////////////////////////////////
// C r e a r un á r b o l binario perfectamente e q u i l i b r a d o de n nodos
//
pub'l i c c l a s s Test
I
public s t a t ic void m a i n t S t r i n g n args)
I
C A r b o l B i n a r i o E q u i l i b r a d o a r b o l b e = new C A r b o l B i n a r i o E q u i 1i b r a d o ( ):
566 JA V A : C U R S O D E P R O G R A M A C IÓ N
i n t númeroDeNodos;
S y s t e m . o u t . p r i n t ( " N ú m e r o de n o d o s : ” ):
númeroDeNodos = L e e r . d a t o l n t í ) :
árbol b e .c o n s t r u í r A r b o l E q u i 1i bradoí númeroDeNodos):
S y s t e m . o u t . p r i n t l n í );
// B u s c a r d a t o s
S t r i n g nombre:
S y s t e m . o u t . p r i n t í " n o m b r e a b u s c a r : " ) : nombre = L e e r . d a t o O :
CDatos obj = ( C D a t o s ) a r b o l b e . b u s c a r ( n e w CDato síno m bre, 0 ) ) :
i f ( obj != n u i l )
S y s t e m . o u t . p r in t ln ío b j .obtenerNombreí) + " “ +
o bj.obten erN otaí));
el se
S y s t e m . o u t . p r i n t í n i " La b ú s q u e d a f a l l ó " ) ;
// M o s t r a r l o s n o d o s d e l á r b o l
System .out.printlni"\n A rb o l:"):
a r b o l b e . v i s i t a r l n o r d e n í );
Las clases H a sh ... proporcionan conjuntos de datos que no perm iten duplica
dos y que utilizan algoritm os hash para el alm acenam iento de los datos y para su
posterior acceso.
EJERCICIOS RESUELTOS
1. R ealizar una aplicación que perm ita crear una lista lineal de elem entos de cual
quier tipo clasificados ascendentem ente. La lista vendrá definida por un objeto de
una clase abstracta que denom inarem os C L istaU nealSE O (G a s e L ista Lineal
Sim plem ente E nlazada O rdenada) y cada elem ento de la lista será un objeto de la
clase siguiente:
private c la ss CElemento
I
// A t r i b u t o s
p r iv a t e Object datos;
p r i v a t e CElemento s i g u i e n t e : // s i g u i e n t e elemento
// M é t o d o s
// . . .
1
El atributo elem A ctual hará referencia al elem ento accedido y elem A nterior al
anterior al actual, excepto cuando el elem ento actual sea el prim ero, en cuyo caso
am bas referencias señalarán a ese elem ento. T am bién incluirá los m étodos:
T odos los m étodos expuestos, excepto listaV acfa, deben actualizar las refe
rencias elem A ctual y elem Anterior.
568 J A V A : C U R S O D E P R O G R A M A C IÓ N
El m étodo com parar debe ser redefinido p o r el usuario en una subclase para
especificar el tipo de com paración que se desea realizar con dos elem entos de la
lista. Según esto, debe devolver un entero indicando el resultado de la com para
ción ( - 1 , 0 0 1 si o b jI< o b j2 , o b jl ==obj2, o o b jl> o b j2 , respectivam ente). Este
m étodo es invocado directam ente por el m étodo bu sca r e indirectam ente por los
m étodos a ñ adir y borrar, que invocan a buscar.
El m étodo listaV acía devuelve true si la lista está vacía y false en caso con
trario.
El m étodo buscar localiza un elem ento determ inado en la lista. T iene un pa
rám etro para alm acenar una referencia de tipo O b je c t a los datos que permitirán
localizar el elem ento en la lista, y devuelve una referencia al área de datos del
elem ento, o bien nu il si la lista está vacía o no existe un elem ento con esos datos.
El m étodo borrar borra un elem ento de la lista. T iene un parám etro para al
m acenar una referencia de tipo O b je c t a los datos que perm itirán localizar en la
lista el elem ento que se desea borrar. D evuelve una referencia al área de datos del
elem ento borrado, o bien nu il si la lista está vacía.
El m étodo obtenerP rim ero devuelve una referencia al área de datos del ele
m ento prim ero, o bien n u il si la lista está vacía.
//////////////////////////////////////////////////////////////////
// C l a s e a b s t r a c t a C L i s t a L i n e a l S E O :
// L i s t a l i n e a l simplemente enlazada ordenada ascendentemente.
//
public abstract class CListaLinealSEO
I
// p: r e f e r e n c i a a l p r i m e r e l e m e n t o de la lista,
p r i v a t e CElemento p = n u i l : // e l e m e n t o de c a b e c e r a
p r i v a t e CElemento e l e m A n t e r i o r = n u i l : // elemento a n t e r i o r
p r i v a t e CElemento ele mActual = nuil: // elemento actual
p u b l i c C L i s t a L i n e a l S E O Í ) {) // c o n s t r u c t o r
// El m é t od o s i g u i e n t e d e b e s e r r e d e f i n i d o en una s u b c l a s e p a r a
// q u e p e r m i t a c o m p a r a r d o s e l e m e n t o s de l a l i s t a p o r el a t r i b u t o
// q u e n e c e s i t e m o s en c a d a momento.
pu b lic a b s t r a c t in t comparar(Object o b j l. Object obj2):
// S i l a l i s t a r e f e r e n c i a d a p o r p e s t á v a c í a , r e t o r n a r ,
i f ( 1i s t a V a c í a () ) r e t u r n n u i l :
// S i l a l i s t a no e s t á v a c í a , e n c o n t r a r e l e l e m e n t o .
e l e m A n t e r i o r = p;
e l e m A c t u a l = p;
// P o s i c i o n a r s e en e l e l e m e n t o b u s c a d o .
w h i l e ( e l e m A c t u a l ! = n u i l && c o m p a r a r ( o b j . e l e m A c t u a l . d a t o s ) > 0)
I
elem Anterior = elemActual:
elemActual = e l e m A c t u a l . s i g u i e n t e :
I
if ( ele m Actual != n u i l )
return elem Actual.datos;
el se
return n u l1 :
1
570 JA V A : C U R S O D E P R O G R A M A C IÓ N
// S i l a l i s t a no e s t á v a c i a , e n c o n t r a r e l p u n t o d e i n s e r c i ó n
b u s c a r ( o b j ) ; // e s t a b l e c e l o s v a l o r e s d e e l e m A n t e r i o r y elemActual
// Do s c a s o s :
// 1) I n s e r t a r al p r i n c i p i o d e l a l i s t a
// 2 ) I n s e r t a r d e s p u é s d e l a n t e r i o r ( i n c l u y e i n s e r t a r a l f i n a l )
i f ( e l e m A n t e r i o r = = e l e m A c t u a l ) // i n s e r t a r a l p r i n c i p i o
[
q . s i g u i e n t e = p;
p = q ; // c a b e c e r a
e le m A n t e r i o r = ele mActual = p; // a c t u a l i z a r referencias
I
else // i n s e r t a r d e s p u é s d e l anterior
I
q . s ig u i e n t e = elemActual;
e l e m A n t e r i o r . s i g u i e n t e = q:
e l e m A c t u a l = q ; // a c t u a l i z a r referencia
// S i l a l i s t a no e s t á v a c i a , b u s c a r e l e l e m e n t o .
b u s c a r ( o b j ) ; // e s t a b l e c e l o s v a l o r e s de e l e m A n t e r i o r y elemActual
// D o s c a s o s :
// 1) B o r r a r e l p r i m e r e l e m e n t o de l a l i s t a
// 2 ) B o r r a r e l s i g u i e n t e a e l e m A n t e r i o r ( e l e m A c t u a l )
i f ( e l e m A c t u a l = = p ) // 1)
p = p . s i g u i e n t e : // c a b e c e r a
e l s e // 2)
ele m A nte rior.sig u ie n te = elem Actual.s ig u ie n t e ;
CA PÍTU LO 13: ESTRU C TU R A S DINÁM ICA S 5 7 1
p u b lic Object o b t e n e r S ig u i e n t e í )
I
// D e v o l v e r una r e f e r e n c i a a l o s d a t o s d e l e l e m e n t o s i g u i e n t e
II al a c t u a l y h a c e r que é s t e s e a e l a c t u a l .
// S i l a l i s t a e s t á v a c i a , d e v o l v e r n u i l ,
i f ( l is t a V a c ia í) ) return n u il;
// A v a n z a r un e l e m e n t o
e lem Anterior = elemActual;
ele m Actual = e l e m A c t u a l . s i g u i e n t e ;
i f ( elemActual != n u l 1 )
return elem Actual.datos:
el se
r e t u r n n u l 1:
//////////////////////////////////////////////////////////////////
En la lista que crearem os a partir de la clase anterior vam os a alm acenar ob
jeto s de la clase:
nombre = nom;
return nombre:
public void a s i g n a r N o t a ( d o u b l e n)
I
n o t a = n;
p u b li c double o b te n e rN o ta ()
I
return nota;
Finalm ente, realizam os una aplicación que utilizando la clase anterior cree
una lista lineal sim plem ente enlazada y ordenada, de objetos CDatos:
I II I //// // // //////////////////////////////////////////////////////
// C r e a r una l i s t a l i n e a l s i m p l e m e n t e e n l a z a d a
II
public class Test
I
public static void m o strarLista(C ListaLin ealSEO rden ada Ise)
I
II M o s t r a r ^ t o d o s l o s e l e m e n t o s d e l a l i s t a
C D a t o s o b j = ( C D a t o s ) l s e . o b t e n e r P r i m e r o ( ):
C A P ÍT U L O 13: ESTRUCTURAS DINÁM ICA S 5 7 3
i n t i - 1:
whi 1 e ( o b j ! - n u l 1)
I
System .out.p rintln(i++ + ” + obj.obtenerNom bre() + " " +
o b j.o b t e n e rN o ta ()):
obj - ( C D a t o s ) l s e . o b t e n e r S i g u i e n t e O ;
// L e e r d a t o s y a ñ a d i r l o s a l a l i s t a
CDatos obj;
S t r i n g n o mbr e :
double nota:
i n t i - 0:
S y s t e m . o u t .p rin t ln ( ” In tr o d u c ir datos. F i n a li z a r con C t r l + Z . ” ):
S y s t e m . o u t . p r i n t í " n o m b r e : ” ):
w h i l e ((nombre - L e e r . d a t o O ) ! - n u i l )
I
System .out.printí"nota: "):
n o t a - L e e r , d a t o D o u b l e ( ):
lse.aña dir(n ew CDatos(nombre. n o ta )):
S y s t e m . o u t . p r i n t í " n o m b r e del alumno a b o r r a r : "):
1
S y s t e m . o u t . p r i n t l n( ’ \ n " ):
// B o r r a r un e l e m e n t o d e t e r m i n a d o
S y s t e m . o u t . p r i n t ( " n o m b r e de l alumno a b o r r a r : " ) :
n o mb r e - L e e r . d a t o O :
obj - ( C D a t o s ) l s e . b o r r a r ( n e w CDatos(nom bre. 0 ) ) ;
i f (obj == n u i l )
S y s t e m . o u t . p r i n t l n ( " E r r o r : e l e m e n t o no b o r r a d o ” ):
// M o d i f i c a r un e l e m e n t o
S y s t e m . o u t . p r i n t ( " n o m b r e de l alumno a m o d i f i c a r : " ) :
n o mb r e - L e e r . d a t o O :
obj - (C D ato s)1se .b u scar(n e w CDatos(nombre. 0 ) ) :
S y s t e m . o u t . p r i n t l n ( " N o m b r e : " + obj .o b te n e rN o m b re O +
n o t a : " + o b j . o b t e n e r N o t a O ):
S y s t e m . o u t . p r i n t ( " n o t a nueva: “ ):
n o t a - L e e r , d a t o D o u b l e ( ):
obj.asignarNota(nota):
// M o s t r a r t o d o s
S y ste m .o u t.p rin tln ("L ista :"):
m o s t r a r L i s t a ( 1s e ) :
574 J A V A : C U R S O D E P R O G R A M A C IÓ N
2. E scribir una aplicación para que utilizando una pila, sim ule una calculadora capaz
de realizar las operaciones de * y /. L a m ayoría de las calculadoras aceptan la
notación infija y unas pocas la notación p ostfija. E n estas últim as, para sum ar 10
y 20 introduciríam os prim ero 10, después 20 y p o r últim o el +. C uando se intro
ducen los operandos, se colocan en una pila y cuando se introduce el operador, se
sacan dos operandos de la pila, se calcula el resultado y se introduce en la pila. La
ventaja de la notación postfija es que expresiones com plejas pueden evaluarse fá
cilm ente sin m ucho código. La calculadora del ejem plo propuesto utilizará la no
tación postfija.
Para realizar esta aplicación utilizarem os las clases C Pila derivada de CLis
taC ircularSE, C D atos y C Leer. C om o estas clases ya han sido im plem entadas, en
este ejercicio nos lim itarem os a u tilizar los recursos que proporcionan.
//////////////////////////////////////////////////////////////////
// C a l c u l a d o r a u t i l i z a n d o una p i l a . E s t a a p l i c a c i ó n , además de l a s
// c l a s e s n e c e s a r i a s de l a b i b l i o t e c a de J a v a , u t i l i z a l a s c l a s e s :
// C P i l a d e r i v a d a de C L i s t a C i r c u l a r S E . C D a t o s y C L e e r .
//
public class Test
I
private static C P i l a p i l a = new C P i l a O : // p i l a de o p e r a n d o s
private static d o u b l e f ] operando - (0. 0 ) : // o p e r a n d o 0 y 1
S y s t e m . o u t . p r i n t l n ( " F o r m a de i n t r o d u c i r l o s d a t o s : " ) ;
S y s te m .o u t.p r i n t l n ( " > p r i m e r operando 1 [ E n t r a r ] " ) ;
S y s t e m . o u t . p r i n t l n ( " > s e g u n d o operando 2 [ E n t r a r ] " ) ;
S y s t e m . o u t . p r i n t l n ( ”> o p e r a d o r [ E n t r a r ] \ n " ) ¡
Sy ste m .o u t.p rin tln í"P a ra s a l i r pulse q \n ");
do
I
try
(
S y s t e m . o u t . p r i n t ( " > “ );
oper * L e e r .d a t o O ; // l e e r un o p e r a n d o o un o p e r a d o r
switch ( o p e r.c h a r A t (O )) // v e r i f i c a r e l p r i m e r c a r á c t e r
1
case
o b t e n e r O p e r a n d o s ( );
S y s t e m . o u t . p r i n t l n ( o p e r a n d o [ 0 ] + o p e r a n d o f l ] );
p i 1 a . m e t e r E n P i 1 a ( new D o u b l e ( o p e r a n d o [ 0 ] + o p e r a n d o [ l ] ) ) ;
break;
case ’ - ' :
o b t e n e r O p e r a n d o s ( ):
System .out.println(operando[0] - o p e ra n d o [l]);
p i 1a .m e t e r E n P i 1a(new D o u b l e ( o p e r a n d o [ 0 ] - o p e r a n d o [ l ] ) ) :
break;
case
o b t e n e r O p e r a n d o s í );
System .out.p rintln(op erand o[0] * operando[1]):
p ila .m e te rE n P ila ín e w D o u b le (o p e ra n d o [0 ]*o p e ra n d o [l]));
break;
case
o b t e n e r O p e r a n d o s í ):
i f ( o p e r a n d o f l ] = = 0)
I
System .out.printlní"\nError: d ivisión por c e r o ") ;
break;
I
System .out.println(operando[0] / operandofl]);
p i 1 a . m e t e r E n P i 1 a (new Do ub l e ( o p e r a n d o [ 0 ] / o p e r a n d o [ l ] ) ) ;
break;
case ’q ’ :
// s a l i r
break;
default : // e s un o p e r a n d o
p i l a . m e t e r E n P i l a í n e w D o u b l e ( o p e r ) );
c a t c h ( N u m b e r F o r m a t E x c e p t i o n e)
1
S y s t e m . o u t . p r i n t ( " E r r o r : d a t o no e s válido. Teclee o tr o : ");
I
576 JA VA : C U R SO D E PROGRAM A CIÓN
c a t c h l N u l 1P o in t e rE x c e p tio n e)
I
S y s t e m . o u t . p r i n t t " E r r o r : t e c l e e " + ( 2 - p i 1a . t a m a ñ o t ) ) +
" operando(s) más");
1
I
w hile (oper.charAt(O) != ’q ’ );
3. Escribir una aplicación que perm ita calcular la frecuencia con la que aparecen las
palabras en un fichero de texto. L a form a de invocar al program a será:
donde fich ero _ d e_ texto es el nom bre del fichero de texto del cual deseam os obte
ner la estadística.
nombre = 1
obtener = 1
palabras = 1
p er mi t a = 1
programa = 1
que = 2
queremos = 1
será = 1
e stadística = 1
texto = 2
un = 1
una = 1
Total palabras: 44
C A PÍTU LO 13: ESTRU C TU R A S DINÁM ICA S 5 7 7
Según lo expuesto, cada nodo del árbol tendrá que hacer referencia a un área
de datos que incluya tanto la palabra com o el núm ero de veces que apareció en el
texto. Estos datos serán los atributos de una clase C D atos definida así:
// M é t o d o s
public CDatosO I) // c o n s t r u c t o r s i n parámetros
public in t obtenerContador()
(
return contador:
578 JA V A : C U R S O D E P R O G R A M A C IÓ N
/////////////////////////////////////////////////////////////////
// C l a s e d e r i v a d a d e l a c l a s e a b s t r a c t a C A r b o l B i n B . Redefine los
// m é t o d o s : c o m p a r a r , p r o c e s a r y v i s i t a r l n o r d e n .
//
public c la ss CArbolBinarioDeBusqueda extends CArbolBinB
I
public int t o t a l P a l a b r a s = 0:
public int t o t a l P a l a b r a s D i f e r e n t e s = 0:
// P e r m i t e c o m p a r a r d o s n o d o s d e l á r b o l p o r e l atributo
// nombre.
p u b l i c i n t co m p ara rtO b je ct o b j l . O b je ct o b j 2)
I
String s t r l = new S t r i n g t ( ( C D a t o s ) o b j 1 ) . o b t e n e r P a l a b r a < ) ) ;
String s t r 2 = new S t r i n g í ( ( C D a t o s ) o b j 2 ) . o b t e n e r P a l a b r a ( ) ) :
return s t r l . c o m p a r e T o ( s t r 2 );
// P e r m i t e m o s t r a r l o s d a t o s d e l nodo v i s i t a d o ,
p u b lic void p ro ce sa r!O b je c t obj)
I
S t r i n g p a l a b r a = new S t r i n g ( ( ( C D a t o s ) o b j ) . o b t e n e r P a l a b r a ( ) ) ;
i n t c o n t a d o r = ( ( C D a t o s ) o b j ) . o b t e n e r C o n t a d o r í );
S y s t e m . o u t . p r in t ín ( p a l abra + " = " + co n ta d o r):
t o t a l Pal a b r a s + = c o n t a d o r ;
total PalabrasDi ferentes++;
I I V i s i t a r l o s nodos del á r b o l,
p u b l i c v o i d vi s i t a r I n o r d e n ( )
I
// S i e l s e g u n d o a r g u m e n t o e s t r u e . l a v i s i t a c o m i e n z a
// en l a r a í z i n d e p e n d i e n t e m e n t e d e l p r i m e r a r g u m e n t o ,
inordentnul1 . t r u e ) :
/////////////////////////////////////////////////////////////////
renciado por el nodo visitado, contabiliza el núm ero total de palabras del texto
procesado y el núm ero total de palabras diferentes (esto es, com o si todas hubie
ran aparecido sólo una vez en el texto). El resto de los m étodos ya fueron explica
dos al h ablar de árboles binarios de búsqueda.
S ólo queda construir una aplicación que cree un objeto de la clase C ArbolBi-
narioD eB usqueda a partir de las palabras alm acenadas en un fichero y presente
los resultados pedidos. El código de esta aplicación se va a apoyar en tres m éto
dos: m a in , leerFichero y palabras.
El m étodo leerFichero abre el fichero y lo lee línea a línea. C ada línea leída
será pasada com o argum ento al m étodo palabras para su descom posición en pala
bras con el fin de añadirlas al árbol binario de búsqueda que ha sido declarado
com o un atributo de la clase aplicación. Para descom poner una línea en palabras
utilizarem os los recursos proporcionados por la clase S trin g T o k e n iz e r del pa
quete ú til de Java (esta clase fue explicada en el capítulo 7).
import j a v a . i o . * :
import j a v a . u t i 1 . * ;
//////////////////////////////////////////////////////////////////
// U t i l i z a r un á r b o l de b ú s q u e d a p a r a o b t e n e r l a f r e c u e n c i a c o n l a
// q u e a p a r e c e n l a s p a l a b r a s en un f i c h e r o d e t e x t o .
// E s t a a p l i c a c i ó n , a d emás de l a s c l a s e s n e c e s a r i a s de l a
// b i b l i o t e c a de J a v a , u t i l i z a l a s c l a s e s : C A r b o l B i n a r i o D e B u s q u e d a
// d e r i v a d a de C A r b o l B i n B y C D a t o s .
//
public class Palabras
I
p riv a te s t a t ic CArbolBinarioDeBusqueda arbolbb -
new C A r b o l B i n a r i o D e B u s q u e d a ( ):
S t r in g palabra:
CDatos o b j :
580 JA V A : C U R S O D E P R O G R A M A C IÓ N
try
I
// A s e g u r a r s e de q u e e l f i c h e r o , e x i s t e y s e p u e d e l e e r
i f ( ¡ f i c h F u e n t e . e x i s t s t ) || ! f i c h F u e n t e . i s F i 1e ( ) )
1
System .err.printlnt"N o existe el fichero " + nombrefich)
return;
I
if ( ¡ f i c h F u e n t e . c a n R e a d t ))
I
S y s t e m . e r r . p r i n t l n t " E l f i c h e r o " + nombrefich +
" no s e p u e d e l e e r " ) ;
return;
// A b r i r un f l u j o de e n t r a d a d e s d e e l f i c h e r o f u e n t e
F i l e l n p u t S t r e a m f i s = new F i 1e l n p u t S t r e a m t f i c h F u e n t e )
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r t f i s );
f l u j o E = new B u f f e r e d R e a d e r ( i s r );
// B u s c a r c a d e n a en e l fichero fuente
S trin g linea;
catch(IOException e)
I
S ystem .out. p rin t l n ( "E rro r: “ + e .getM essage());
CA PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 8 1
finally
I
// C e r r a r el flujo
try
I
if (flujoE != n u i l ) f l u j o E . c l o s e t ):
)
c a t c h ( I O E x c e p t i o n e)
I
System .out.p rintlní"Error: ” + e . t o S t r i n g ( ));
I
I
I
if ( a r g s . l e n g t h < 1)
System .err.println("Sintaxis: j a v a P a l a b r a s < f i c h e r o _ d e _ t e x t o > " ):
el se
1e e r F i c h e r o ( a r g s [ 0 ] ):
EJERCICIOS PROPUESTOS
1. Se quiere escribir un program a para m anipular ecuaciones algebraicas o polinó-
m icas dependientes de las variables x e y . P or ejem plo:
C ada térm ino del polinom io será representado por un objeto de una clase
CTerm ino y cada polinom io por un objeto que sea una lista lineal sim plem ente
enlazada ordenada, de elem entos CTermino.
2. En un fichero tenem os alm acenados los nom bres y las notas m edias de los alum
nos de un determ inado curso. L a estructura de cada uno de los registros del fiche
ro se corresponde con los atributos de una clase com o la siguiente:
public c la ss CRegistro
i
// A t r i b u t o s
p r i v a t e S t r i n g nombre:
p r i v a t e f l o a t nota:
// M é t o d o s
// . ..
1
Q uerem os leer los datos de este fichero para construir una estructura de datos
en m em oria que se ajuste a un árbol binario d e búsqueda perfectam ente equili
brado. Para ello, es aconsejable ordenar el fichero antes de crear el árbol. Esto fa
cilita la creación del árbol binario con los dos requisitos im puestos: que sea de
búsqueda y perfectam ente equilibrado. En este ejercicio supondrem os que parti
m os de un fichero ordenado. M ás adelante, en el capítulo de “A lgoritm os" vere
mos cóm o se ordena un fichero.
c r e a r el su b á r b o l i z q u i e r d o
l e e r un r e g i s t r o d e l f i c h e r o y a s i g n á r s e l o al nodo a c t u a l
c r e a r el sub á rb o l derecho
V em os que prim ero hay que construir el subárbol izquierdo, después la raíz y
p o r últim o el subárbol derecho. C om o las claves contenidas en el fichero están o r
denadas, la clave m enor se alm acenará en el nodo m ás a la izquierda y la m ayor
en el nodo m ás a la derecha, dando así lugar a un árbol de búsqueda, adem ás de
perfectam ente equilibrado.
C A PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 8 3
3. El filtro so rt lee líneas de texto del fichero estándar de entrada y las presenta en
orden alfabético en el fichero estándar de salida. El ejem plo siguiente m uestra la
form a de u tilizar sort:
sort
l o que puede h a c e r s e
en c u a l q u i e r momento
no s e h a r á
en n i n g ú n momento.
(eof)
en c u a l q u i e r momento
en n i n g ú n momento,
l o q u e puede h a c e r s e
no s e h a r á
Se desea escribir un program a de nom bre O rdenar, que actúe com o el filtro
sort. Para o rdenar las distintas líneas vam os a ir insertándolas en un árbol binario
de búsqueda, de tal form a que al recorrerlo podam os presentar las líneas en orden
alfabético. El program a se ejecutará utilizando la siguiente sintaxis:
Si se especifica el atributo opcional -r, las líneas del fichero serán presentadas
en orden alfabético descendente; si no se especifica, entonces se presentarán en
orden alfabético ascendente.
CA PÍTU LO 14
© F.J.Cebalos/RA-MA
ALGORITMOS
En este capítulo vam os a exponer cóm o resolver problem as m uy com unes en pro
gram ación. El prim ero que nos vam os a plantear es la recursión; se trata de un
problem a cuyo planteam iento form a parte de su solución. El segundo problem a
que vam os a ab ordar es la ordenación de objetos en general; la ordenación es tan
com ún que no necesita explicación; algo tan cotidiano com o una guía telefónica,
es un ejem plo de una lista clasificada, El localizar un determ inado teléfono exige
una búsqueda p o r algún m étodo; el problem a de búsqueda será el últim o que re
solverem os.
RECURSIVIDAD
Se dice que un proceso es recursivo si form a parte de sí m ism o, o sea que se defi
ne en función de sí m ism o. E jem plos típicos de recursión los podem os encontrar
frecuentem ente en problem as m atem áticos, en estructuras de datos y en m uchos
otros problem as.
que alm acenarlo para recuperarlo cuando se acabe la nueva ejecución del proceso
y haya que reanudar la antigua.
U n ejem plo es el m étodo de A ckerm an, <4, el cual está definido para todos los
valores enteros no negativos m y n de la form a siguiente:
A ( O . n ) - n+1
A(m.O) = A ( m - 1 . 1 ) ( m > 0)
A(m.n) = A (m -1 , A ( m , n - 1 )) ( m . n > 0)
<método A ( m . n ) >
I F (m e s i g u a l a 0 ) THEN
d e v o l v e r como r e s u l t a d o n+1
E LSE I F (n es ig u a l a 0) THEN
d e v o l v e r como r e s u l t a d o A(m -l.l)
ELSE
d e v o l v e r como r e s u l t a d o A(m -1.A(m .n-l))
ENDIF
END K m é t o d o A ( m , n ) >
public c la ss CRecursion
I
// M é t o d o r e c u r s i v o d e A c k e r m a n :
// A ( O . n ) = n+1
// A(m.O) = A ( m - l . l ) (m > 0)
// A(m.n) = A ( m - l , A ( m , n - l )) (m.n > 0)
p u b l i c s t a t i c i n t A c k e r m a n ( i n t m. i n t n)
I
if (m = 0)
return n+1;
else i f ( n = = 0)
return A c k e r i n a n t m - 1. 1):
else
return Ackerman(m-1. Ackerman(m,n-1));
Para probar cóm o funciona este algoritm o podem os escribir la aplicación si
guiente:
C A PÍTU LO 14: A LG O RITM O S 5 8 7
La m ejor form a de hacer esto es utilizar una pila, con el fin de alm acenar los
valores m y n cada vez que se invoque el m étodo para una nueva ejecución y to
m ar estos valores de la cim a de la pila, cuando esta nueva ejecución finalice, con
el fin de reanudar la antigua.
< mé t o d o A ( m , n ) >
U t i l i z a r una p i l a p a r a a l m a c e n a r l o s v a l o r e s d e m y n
I n i c i a r la p i l a c o n l o s v a l o r e s m.n
DO
Tomar l o s d a t o s d e l a p a r t e s u p e r i o r d e la p i l a
¡ F (m e s i g u a l a 0 ) THEN
Amn = n+1
I F ( p i l a no v a c i a )
s a c a r d e l a p i l a l o s v a l o r e s : m, n
m e t e r en l a p i l a l o s v a l o r e s : m. Amn
ELSE
d e v o l v e r como r e s u l t a d o Amn
ENDIF
E LSE I F (n e s i g u a l a 0 ) THEN
meter en la p i l a l o s v a l o r e s : m-1,1
ELSE
m e t e r en l a p i l a l o s v a l o r e s : m - 1, Amn
m e t e r en la p i l a l o s v a l o r e s : m.n-1
ENDIF
WHILE ( t r u e )
END S m é t o d o A ( m . n ) >
588 JA V A : C U R S O D E P R O G R A M A C IÓ N
// M é t o d o s
public COatostint im, int in) // c o n s t r u c t o r con p a r á m e t r o s
I
m = im:
n - in;
I
A B C
l 1
I ]
< m é t o d o m o v e r ( n _ d i s c o s . A. B. C)>
IF (r¡_discos es m a y o r q u e 0) THEN
m o v e r ( n _ d i s c o s - I . A. C. B)
m o v e r ( d i s c o _ n . A. B, C)
m o v e r ( n _ d i s e o s - 1 . B. A. C)
ENDIF
E N D < m é t o d o mo ve r>
public c la ss C Ha n o i
I
private static i n t m o v i m i e n t o s = 0:
Número de d i s c o s : 3
mover d i s c o de A a C
mover d i s c o de A a B
mover d i s c o de C a B
mover d i s c o de A a C
mover d i s c o de B a A
mover d i s c o de B a C
mover d i s e o de A a C
ORDENACIÓN DE DATOS
Uno de los procedim ientos m ás com unes y útiles en el procesam iento de datos, es
la ordenación de los m ism os. Se considera ordenar al proceso de reorganizar un
conjunto dado de objetos en una secuencia determ inada. El objetivo de este pro
ceso generalm ente es facilitar la búsqueda de uno o m ás elem entos pertenecientes
a un conjunto. Son ejem plos de datos ordenados las listas de los alum nos m atri
culados en una cierta asignatura, las listas del censo, los índices alfabéticos de los
libros, las guías telefónicas, etc. Esto quiere decir que m uchos problem as están
relacionados de alguna form a con el proceso de ordenación. Es p o r lo que la or
denación es un problem a im portante a considerar.
La ordenación, tanto num érica com o alfanum érica, sigue las m ism as reglas
que em pleam os nosotros en la vida norm al. Esto es, un dato num érico es m ayor
que otro cuando su valor es m ás grande, y una cadena de caracteres es m ayor que
otra cuando está después p o r orden alfabético.
592 J A V A : C U R S O D E P R O G R A M A C IÓ N
Método de la burbuja
Hay m uchas form as de ordenar datos, pero una de las m ás conocidas es la ordena
ción por el m étodo de la burbuja.
2. R epetim os el punto 1, ahora para los n-1 prim eros elem entos de la lista. Con
esto conseguim os llevar el valor m ayor de éstos a la posición n-1.
3. R epetim os el punto 1, ahora para los n-2 prim eros elem entos de la lista y así
sucesivam ente.
EN D I F
i - i+1
E N DDO
n - n-1
ENDDO
END <clasificar>
La clase siguiente incluye el m étodo o rdenar que utiliza este algoritm o para
ordenar una m atriz de tipo d o u b le o de tipo S trin g .
/////////////////////////////////////////////////////////////////
// O r d e n a c i ó n p o r e l m ét o d o de l a b u r b u j a . E l m é t od o " o r d e n a r " se
// s o b r e c a r g a d o s v e c e s : una p a r a o r d e n a r una m a t r i z de t i p o
// d o u b l e y o t r a p a r a o r d e n a r un a m a t r i z de t i p o S t r i n g .
//
public c la ss CMatriz
(
public static void ordenar(double[] m)
I
d o u b l e a ux ;
i n t i , número_de_elementos = m .le n g th :
boolean s = true:
public s t a t ic void o r d e n a r ( S t r i n g [ ] m)
1
S t r i n g a ux :
i n t i . número_.de_el e m e n t o s • m. l e n g t h ;
boolean s = true:
if ( m [ i - 1 ] .compareTo(m[i]) > 0)
I
// p e r m u t a r l o s e l e m e n t o s ( i -1) e (i)
aux = m [ i - 1 ] ;
mC i -1 ] = m [ i ] :
m [i] = aux;
s = true: I I permutación
/////////////////////////////////////////////////////////////////
O bserve que í inicialm ente vale fa lse para cada iteración y tom a el valor tru e
cuando al m enos se efectúa un cam bio entre dos elem entos. Si en una exploración
a lo largo de la lista no se efectúa cam bio alguno, s perm anecerá valiendo false, lo
que indica que la lista está ordenada, term inando así el proceso.
// M a t r i z de c a d e n a s de c a r a c t e r e s
S t r i n g f ] s = I " c c c " . " b b b ” . “a a a " . " e e e " . " d d d " 1;
CA PÍTU LO 14: A LG O RITM O S 5 9 5
CM atriz.ordenar(s):
f o r ( i n t i = 0: i < s . l e n g t h ; i + + )
S y ste m .o u t.p rin t(s[i] + " ");
S y s t e m . o u t . p r i n t l n í );
Método de inserción
El algoritm o para este m étodo de ordenación es el siguiente: inicialm ente, se o r
denan los dos prim eros elem entos de la m atriz, luego se inserta el tercer elem ento
en la posición correcta con respecto a los dos prim eros, a continuación se inserta
el cuarto elem ento en la posición correcta con respecto a los tres prim eros ele
m entos ya ordenados y así sucesivam ente hasta llegar al últim o elem ento de la
m atriz. Por ejem plo:
Valores iniciales:
Valores ordenados:
La program ación de este algoritm o, para el caso concreto de ordenar num éri
cam ente una lista de valores, es la siguiente:
II D e s d e e l segundo elemento
f o r ( i = 1; i < n_elementos: i++)
[
x = m[ i ] ;
k = i - 1;
// P a r a k— 1. s e ha a l c a n z a d o e l extremo i z q u i e r d o ,
w h i 1e ( k >=0 && x < m [ k ] )
I
m [ k + l ] = m [ k ]; // h a c e r h u e c o p a r a insertar
k --:
1
m[k+l] = x; // i n s e r t a r x en s u l u g a r
Método quicksort
El m étodo de ordenación quicksort, está generalm ente considerado com o el m ejor
algoritm o de ordenación disponible actualm ente. El proceso seguido por este al
goritm o es el siguiente:
2. Se divide la m atriz en dos partes: una con todos los elem entos m enores que el
valor seleccionado y otra con todos los elem entos m ayores o iguales.
3. Se repiten los puntos 1 y 2 para cada una de las partes en la que se ha dividido
la m atriz, hasta que esté ordenada.
CA PÍTU LO 14: A LG O RITM O S 5 9 7
< m é t o d o q s í m a t r i z "a")>
Se e l i g e un val or x d e la m a t r i z
D O M H I L F ( "a" no e s t é d i v i d i d o en dos p a r t e s )
[ d i v i d i r "a" en dos p a r t e s : a _ i n f y a _ s u p ]
a _ i n f c o n los e l e m e n t o s a, < x
a _ s u p c o n los e l e m e n t o s a¡ > - x
F.NDDO
IF ( e x i s t e a _ i n f ) THFN
qs( a _ i n f )
FND1F
IF ( e x i s t e a _ s u p ) THFN
qs( a_sup)
FNDIF
F N D <qs>
// M é t o d o r e c u r s i v o qs
p riv a te s t a t ic void q s ( S t r i n g [ ] m, int inf, int sup)
I
i n t i z q . der;
S t r i n g mi t a d . x;
i z q - i n f ; d er = sup;
mitad = m [ ( i z q + d e r ) / 2];
do
I
w hile (m [izq].compareTo(mitad) < 0 88 i z q < su p ) izq++;
w hile (mitad.compareTo(m[der]) < 0 88 d e r > i n f ) der--;
i f ( i z q <= der)
í
x - m[ i z q ] ; m [ i z q ] - m[der]; m [ d e r ] = x;
izq++; d e r -- ;
w h i l e ( i z q <= d e r ) ;
i f ( i n f < d e r) qs(m. inf. der);
i f ( i z q < sup) qs(m. izq. sup);
598 J A V A : C U R S O D E P R O G R A M A C IÓ N
O bservam os que cuando el v alo r m itad se corresponde con uno de los valores
de la lista, las condiciones izq < sup y d er > i n f de las sentencias
Para experim entarlo, pruebe com o ejem plo la lista de valores I 1 3 1 1 y elija
m ita d = 2 fijo. En este caso las sentencias anteriores serían así:
// M é t o d o no r e c u r s i v o q s
p r i v a t e s t a t i c v o i d q s N R ( S t r i n g [ ] m, int inf, int sup)
I
C P i l a p i l a = new C P i l a O : II p i l a de e l e m e n t o s ( i n f . s u p )
CDatos dato: II e n c a p s u l a l o s a t r i b u t o s i n f y s u p
i n t i z q , d e r . p¡
S t r i n g m i t a d , x;
// I n i c i a r l a p i l a c o n l o s v a l o r e s : i n f . s up
p i 1 a . m e t e r E n P i 1a ( n e w C D a t o s ( i n f , s u p ) ) ;
do
CA PÍTU LO 14: A LG O RITM O S 5 9 9
// Tomar l o s d a t o s i n f . s u p de l a p a r t e s u p e r i o r de l a pila
d a t o = ( C D a t o s ) p i l a . s a c a r D e P i l a ( );
i n f - d a t o . o b t e n e r l n f ( ) ; s u p = d a t o . o b t e n e r S u p ( );
do
[
// D i v i s i ó n de l a m a t r i z en d o s p a r t e s
i z q - i n f ; d e r = sup:
mitad - m [ ( i z q + d e r ) / 2 ];
do
I
w h i l e ( m[ i z q ] . c o m p a r e T o ( m i t a d ) < 0 && i z q < s u p ) izq++;
w h i l e ( m i t a d . c o m p a r e T o ( m [ d e r ] ) < 0 && d e r > i n f ) der--;
i f ( iz q <= der)
I
x = m [izq]; m [izq] - m[der]; m [ d e r ] = x;
izq++; d e r --;
w h i 1e ( i z q < = d e r ) :
i f ( i z q < sup)
1
II M e t e r en l a p i l a l o s v a l o r e s : i z q . sup
pila.m eterEnPila(new CDatostizq. sup)):
I
/* inf = inf; */ sup - der:
)
w hile (in f < der);
I
w hile (pila.tam a ño() != 0 ) :
1
En esta solución observam os que después de cada paso se generan dos nuevas
sublistas. U na de ellas la tratam os en la siguiente iteración y la otra la pospone
mos, guardando sus lím ites i n f y sup en una pila. El m étodo qsN R utiliza la clase
CPila, im plem entada en el capítulo de “E structuras dinám icas”, para crear una
pila que alm acene los valores i n f y sup a los que nos hem os referido anteriorm en
te. Estos lím ites son los atributos de objetos de una clase C D atos definida así:
BÚSQUEDA DE DATOS
Búsqueda secuencial
Este m étodo d e búsqueda, aunque válido, es el m enos eficiente. Se basa en com
p arar el valor que se desea buscar con cada uno de los valores de la m atriz. La
m atriz no tiene p o r qué estar ordenada.
Búsqueda binaria
Un m étodo eficiente de búsqueda, que puede aplicarse a las m atrices clasificadas,
es la búsqueda binaria. Si partim os de que los elem entos de la m atriz están alm a
cenados en orden ascendente, el proceso de búsqueda binaria puede describirse
así: se selecciona el elem ento del centro o aproxim adam ente del centro de la m a
triz. Si el valor a bu scar no coincide con el elem ento seleccionado y es m ayor que
él, se continúa la búsqueda en la segunda m itad de la m atriz. Si, por el contrario,
el valor a buscar es m enor que el valor del elem ento seleccionado, la búsqueda
continúa en la prim era m itad de la m atriz. En am bos casos, se halla de nuevo el
elem ento central, correspondiente al nuevo intervalo de búsqueda, repitiéndose el
ciclo. El proceso se repite hasta que se encuentra el valor a buscar, o bien hasta
que el intervalo de búsqueda sea nulo, lo que querrá decir que el elem ento busca
do no figura en la matriz.
i f ( m .le n g th = = 0) r e t u r n -1:
i n t m i t a d , i n f = 0, s u p = m . l e n g t h - 1;
602 JA V A : C U R S O D E PROGRAM A CIÓN
do
mitad - ( i n f + sup) / 2;
i f (v > m[mi t a d ] )
i nf = m i t a d + 1;
el se
sup = mi t a d - 1;
1
while ( m[mitad] ! = v && i n f < = s u p ) ;
if ( m [ m i t a d ] = = v)
r e t u r n mitad;
el se
re tu rn -1;
Búsqueda de cadenas
Uno de los m étodos m ás eficientes en la búsqueda de cadenas dentro de un texto
es el algoritm o B oyer y M oore. La im plem entación básica de este m étodo cons
truye una tabla delta que se utilizará en la tom a de decisiones durante la búsqueda
de una subcadena. D icha tabla contiene un núm ero de entradas igual al núm ero de
caracteres del código que se esté utilizando. P or ejem plo, si se está utilizando el
código de caracteres ASCII la tabla será de 256 entradas. C ada entrada contiene el
v alo r delta asociado con el carácter que representa. P or ejem plo, el valor delta
asociado con A estará en la entrada 6 5 y el valor delta asociado con el espacio en
blanco, en la entrada 32. El valor delta para un carácter, es la posición de la ocu
rrencia m ás a la derecha de ese carácter respecto a la posición final en la cadena
buscada. L as entradas correspondientes a los caracteres que no pertenecen a la ca
dena a buscar, tienen un valor igual a la longitud de esta cadena.
P or lo tanto, para definir la tabla delta para una determ inada subcadena a bus
car, construim os una m atriz con todos sus elem entos iniciados a la longitud de di
cha cadena, y luego, asignam os el v alo r delta para cada carácter de la subcadena,
así:
f o r ( i = 0; i < l o n g it u d _ c a d e n a _ p a t r ó n ; i + + )
d e l t a [ c a d e n a _ p a t r ó n [ i ] ] = 1o n g i t u d _ c a d e n a _ p a t r ó n - i - 1;
V eam os un ejem plo. S uponga que se desea buscar la cadena “cien” en el texto
“ M ás vale un ya que cien después se hará". La búsqueda com ienza así:
Texto: Más v a l e un y a q u e c i e n d e s p u é s s e h a r á
C aden a a b u s c a r : cien
El funcionam iento del algoritm o puede com prenderse m ejor situando la cade
na a buscar paralela al texto. La com paración es de derecha a izquierda: por lo
tanto, se com para el últim o carácter en la cadena a buscar (n) con el carácter que
está justam ente encim a en el texto {espacio). C om o n es distinto de espacio, la
cadena que se busca debe desplazarse a la derecha un núm ero de caracteres igual
al valor indicado p o r la entrada en la tabla delta que corresponde al carácter del
texto que no coincide. Para la cadena "cien ” ,
Texto: Más v a l e un y a q u e c i e n d e s p u é s s e h a r á
C a de n a a b u s c a r : cien
// C o n s t r u i r l a t a b l a " d e l t a "
i n t [ ] d e l t a = new i n t [ 2 5 6 ] :
i n t i , longCad = c a d e n a .1e n g t h :
CA PÍTU LO 14: ALGORITM OS 6 0 5
// i n i c i a r l a t a b l a " d e l t a "
f o r ( i = 0: i < 2 5 6 : i + + )
d e l t a [ i ] = 1o n g C a d :
// A s i g n a r v a l o r e s a l a t a b l a
f o r ( i ” 0 : i < 1o n g C a d : + + i )
d e l t a [ c a d e n a [ i ] ] = longCad - i - 1;
// A l g o r i t m o B o y e r - M o o r e
i n t j . l o n g T e x = t e x t o . 1e n g t h :
i - l o n g C a d - 1; // i e s e l Í n d i c e d e n t r o d e l texto
w h ile ( i < longTex)
I
j - l o n g C a d - 1: / / í n d i c e d e n t r o de l a c a d e n a a buscar
// M i e n t r a s h a y a c o i n c i d e n c i a de c a r a c t e r e s
w hile (cadena[j] =- t e x t o f i] )
I
if ( j > 0 )
I
// S i g u i e n t e posición a la izquierda
j--; i--:
I
else
1
// S e l l e g ó al principio de l a cadena, luego se encontró,
return i ;
// L o s c a r a c t e r e s n o c o i n c i d e n . M o v e r i l o que i n d i q u e el
// v a l o r " d e l t a " d e l c a r á c t e r d e l t e x t o q u e no c o i n c i d e
i + - d e l t a [ t e x t o [ i]]:
I
return -1;
f ic h e r o c: 18 3 2 - 10 6 0 - 14 42 44 6 8 - 12 24 3 0 48
Fase de distribución:
f i c h e r o a: 18 3 2 - 14 4 2 44 68
f i c h e r o b: 10 6 0 - 12 2 4 30 48
Fase de m ezcla:
fichero c: 10 18 32 60 - 12 14 24 30 4 2 44 4 8 68
Fase de distribución:
fichero a: 10 18 3 2 60
fichero b: 12 14 24 30 4 2 44 4 8 68
Fase de m ezcla:
fichero c: 10 12 14 18 24 3 0 32 4 2 4 4 4 8 6 0 6 8
CA PÍTU LO 14: A LG O RITM O S 6 0 7
P ara dejar ordenado el fichero del ejem plo hem os necesitado realizar dos pa
sadas. El proceso finaliza, tan pronto com o el núm ero de tram os ordenados del fi
chero c, sea 1. U na form a de reducir el núm ero de pasadas es distribuir los tram os
ordenados sobre m ás de dos ficheros.
< m é t o d o m e z c l a _ n a t u r a l ()>
n u t r a m o s = 0;
DO
[ C r e a r y a b r i r los f i c h er os te mp o r a l e s a y b]
n _ t r a m o s = d i s t r i b u c i ó n ( );
n_tramos = mezcla():
WH I L E (n _t r a m o s .'= 1):
E N D < m e z c l a _ n a t u r a l ()>
public c la ss CMezclaNatural
I
p u b l i c s t a t i c v o i d m e z c l a N a t u r a l ( F i 1e f i c h F u e n t e )
throws IOException
I
i n t n r o _ t r a m o s = 0;
// a y b s o n d o s f i c h e r o s temporales
do
I
d i s t r i b u i r ( f i c h F u e n t e . a, b ) :
n r o _ t r a m o s = m e z c l a r í a , b. f i c h F u e n t e ) :
)
while (nro_tramos != 1):
import ja v a .i o . * :
//////////////////////////////////////////////////////////////////
// O r d e n a r un f i c h e r o u t i l i z a n d o e l m é t od o de m e z c l a n a t u r a l .
// S e t r a t a de un f i c h e r o de t e x t o q u e a l m a c e n a una l i s t a de
// nombres.
II El no mb r e d e l f i c h e r o s e r e c i b e a t r a v é s de l a l i n e a de ó r d e n e s .
// La o r d e n a c i ó n s e r e a l i z a en o r d e n a l f a b é t i c o a s c e n d e n t e .
// La a p l i c a c i ó n e s t á s o p o r t a d a p o r l a c l a s e C M e z c l a N a t u r a l .
// Métodos:
// mezclaNatural
// d istribuir
// mezclar
II mai n
//
p u b lic c l a s s CMezclaNatural
I
// M e z c l a n a t u r a l / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
p u b lic s t a t i c vo id m e z c la N a tu ra l( F i 1e fic h F u e n te )
th rows IO E xce p tio n
I
// D e f i n i c i ó n de v a r i a b l e s
F i l e a = new F i 1e ( " f t e m p a . t m p " ) ; // f i c h e r o temporal
F i l e b = new F i 1 e ( " f t e m p b . t m p " ) ; // f i c h e r o temporal
in t nro_tramos;
do
I
n r o _ t r a m o s = d i s t r i b u i r ( f i c h F u e n t e , a. b);
if ( n r o _ t r a m o s < = 1)
I
// P r o c e s o f i n a l i z a d o . B o r r a r los ficheros temporales.
a.deleteO : b.deleteO :
return;
)
nro_tramos = m e z cla rla , b. fichFuente);
I
while (nro_tramos != 1);
I II m e z c l a N a t u r a l
C A P ÍT U L O 14: ALGORITM OS 6 0 9
// F a s e de d i s t r i b u c i ó n / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
p u b lic s t a t i c in t d i s t r i b u i r ( F i l e fuente. F i l e destinoA.
F i l e d e s t i n o B ) throws IO E xce p tio n
I
// A b r i r un f l u j o d e e n t r a d a d e s d e f u e n t e q u e p e r m i t a
// l e e r l a i n f o r m a c i ó n l i n e a a l i n e a .
F i l e l n p u t S t r e a m f i s = new F i 1e ! n p u t S t r e a m ( f u e n t e );
I n p u t S t r e a m R e a d e r i s r = new I n p u t S t r e a m R e a d e r t f i s ):
B u f f e r e d R e a d e r f e = new B u f f e r e d R e a d e r ( i s r ) ;
// A b r i r un f l u j o de s a l i d a h a c i a d e s t i n o A
F i l e O u t p u t S t r e a m f o s A = new F i 1 e O u t p u t S t r e a m í d e s t i n o A ) ;
O u t p u t S t r e a m W r i t e r o s r A = new O u t p u t S t r e a m W r i t e r ( f o s A ) ;
B u f f e r e d W r i t e r f a = new B u f f e r e d W r i t e r ( o s r A ) ;
// A b r i r un f l u j o de s a l i d a h a c i a d e s t i n o B
F i l e O u t p u t S t r e a m f o s B = new F i 1e O u t p u t S t r e a m t d e s t i n o B ):
O u t p u t S t r e a m W r i t e r o s r B = new O u t p u t S t r e a m W r i t e r ( f o s B ):
B u f f e r e d W r i t e r f b = new B u f f e r e d W r i t e r ( o s r B ):
B u ffe re d W r it e r faux = fa ; // f a u x s e r á f a o fb
Strin g linea: II última line a leída
S t r i n g I1nea_ant; // lin e a a n t e rio r a la última leída
i n t n r o _ t r a m o s = 1; // nú me r o t o t a l de t r a m o s o r d e n a d o s
II L e e r l a p r i m e r a l i n e a ( l i n e a anterior)
i f ( ( I 1 n e a _ a n t = f e . r e a d L i n e ( )) != n u i l )
I
// E s c r i b e en f a l a l i n e a l e í d a más el separador de l i n e a
f a . w r i t e ( 1 i n e a _ a n t ) ; f a . n e w L i n e ( );
1
el se
I
faux = n u i l ; fc.closeO : fa .closeO ; fb.closeO :
r e t u r n 0;
// L e e r l a s i g u i e n t e l i n e a ( l i n e a a c t u a l )
w h i le ( ( l i n e a = f c . r e a d L i n e ( )) != n u i l )
I
if ( 1 1 n e a . c o m p a r e T o t 1 i n e a _ a n t ) < 0)
I
// C a m b i a r a l o t r o f i c h e r o
faux = (faux = f a ) ? fb : fa;
++nro_tramos:
I
11nea_ant = 11n e a ;
// E s c r i b e en f a u x l a l i n e a l e í d a más el separador de l i n e a
f a u x . w r i t e ( 1 1nea); f a u x . n e w L i n e ():
I
610 JA V A: C U R SO DE PRO G R A M A CIÓ N
// F a s e d e m e z c l a / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
p u b l i c s t a t i c i n t m e z c l a r t F i l e fuenteA. F i l e fu enteB,
F i l e d e s t in o ) throws IOException
I
// A b r i r un f l u j o d e e n t r a d a d e s d e f u e n t e A que p e r m i t a
// l e e r l a i n f o r m a c i ó n l i n e a a linea.
F i l e l n p u t S t r e a m f i s A = new F i 1e l n p u t S t r e a m t f u e n t e A ) :
I n p u t S t r e a m R e a d e r i s r A = new I n p u t S t r e a m R e a d e r ! f i s A ) ;
B u f f e r e d R e a d e r f a = new B u f f e r e d R e a d e r ! i s r A ) ;
// A b r i r un f l u j o de e n t r a d a d e s d e f u e n t e B que p e r m i t a
// l e e r l a i n f o r m a c i ó n l i n e a a linea.
F i l e l n p u t S t r e a m f i s B = new F i l e l n p u t S t r e a m ! f u e n t e B ) :
I n p u t S t r e a m R e a d e r i s r B = new I n p u t S t r e a m R e a d e r t f i s B ) ;
B u f f e r e d R e a d e r f b = new B u f f e r e d R e a d e r ! i s r B ) ;
// A b r i r un f l u j o de s a l i d a h a c i a d e s t i n o
F i l e O u t p u t S t r e a m f o s = new F i 1e O u t p u t S t r e a m ! d e s t i n o );
C u t p u t S t r e a m W r i t e r o s r = new O u t p u t S t r e a m W r i t e r ( f o s );
B u f f e r e d W r i t e r f e = new B u f f e r e d W r i t e r ( o s r );
// Leemos l a s d o s p r i m e r a s lineas, un a de f a y o t r a de f b
U n e a D e F a = f a . r e a d L i n e ! );
1i neaDeFa_ant = U n e a D e F a ;
U n e a D e F b = f b . r e a d L i n e ( );
1i neaDeFb_ant = U n e a D e F b ;
// Vamos l e y e n d o y c o m p a r a n d o h a s t a q u e s e a c a b e a l g u n o de l o s
// f i c h e r o s . La f u s i ó n s e r e a l i z a e n t r e p a r e s d e t r a m o s
// o r d e n a d o s . Un t r a m o de f a y o t r o de f b d a r á n l u g a r a un
// t r a m o o r d e n a d o s o b r e f e .
w h i l e ( U n e a D e F a ! = n u i l && U n e a D e F b ! = n u i l )
I
if ( U n e a D e F a . c o m p a r e T o d IneaDeFb) < 0) // i f 1
I
i f ( 1 1 n e a D e F a .c o m p a r e T o ( 11n e a D e F a _ a n t ) < 0) // i f 2
// E n c o n t r a d o e l f i n a l d e l t r a m o de f a
I
11 n e a D e F a _ a n t = U n e a D e F a ;
// C o p i a m o s e l t r a m o o r d e n a d o de f b
do
I
f c . w r i t e ( 1 I n e a D e F b ) : f e . n e w L i n e ( );
CA PÍTU LO 14: ALGORITM OS 6 1 1
11neaDeFb_ant - líneaDeFb;
I
while ( ( U n e a D e F b - f b . r e a d L i n e t ) ) ! = n u i l &&
1 IneaDeFb.compareTod1neaDeFb_ant) > 0):
++nro_tramos;
l
else // de i f 2
[
// C o p i a m o s l a c a d e n a l e í d a de f a
11neaDeFa_ant = U n e a D e F a :
f e . w r i t e t 1 i n e a D e F a ) : f e . n e w L i n e t ):
U n e a D e F a = f a . r e a d L i n e ( ):
1
1
else // de i f 1
I
i f (1 I n e a D e F b . c o m p a r e T o d 1 n e a D e F b _ a n t ) < 0 ) // i f 3
// E n c o n t r a d o el f i n a l d e l t r a m o de fb
I
11neaDeFb_ant - U n e a D e F b :
// C o p i a m o s e l t r a m o o r d e n a d o d e fa
do
I
f c . w r i t e t l i n e a D e F a ) : f e . n e w L i n e ( );
1 1neaDeFa_ant - U n e a D e F a :
1
while ( ( U n e a D e F a - f a . r e a d L i n e t ) ) ! = n u i l &&
UneaDeFa.com pareTo(HneaDeFa_ant) > 0):
++nro_tramos;
1
else // d e i f 3
I
II C o p i a m o s l a c a d e n a l e í d a d e f b
11n e a D eF b_ a n t = l í n e a D e F b :
f c . w r i t e d I n e a D e F b ) ; f e . n e w L i n e t ):
U n e a D e F b = f b . r e a d L i n e t ):
1 // de w h i l e
// En e l c a s o d e a c a b a r s e primero los d a t o s de fb
i f ( 1 1 n e a D e F b = = n u l 1)
I
f e . w r i t e t l 1n e a D e F a ): f e . n e w L i n e t ):
w h i l e ( d i n e a D e F a = fa . re a d L i n e t )) != n u i l )
I
f e . w r i t e ( 1 1 n e a D e F a ); f e . n e w L i n e t );
// En e l c a s o de a c a b a r s e primero lo s d a t o s d e fa
612 JA V A : C U R SO DE PROGRAM A CIÓN
// L e e r el f i c h e r o y m o s t r a r l o
S t r in g línea;
w h ile ( ( l í n e a = f e . r e a d L i n e ( )) != n u i l )
S y s t e m . o u t . p r i n t l n ( 1 í n e a ):
f e . e l o s e ( ):
C A PÍTU LO 14: ALGORITM OS 6 1 3
catchílOException e)
I
System .out.p rintlní"Error: " + e .g e tM e s sa g e í)):
I
1
I
i
//////////////////////////////////////////////////////////////////
De la clase B u ffe re d W rite r. cabe destacar los m étodos: w rite que perm ite
escribir una línea de texto pero sin escribir el carácter delim itador de la misma
(norm alm ente \n, \r, o \r\n) y n e w L in e que perm ite escribir el carácter delim itador
de línea: este carácter es definido por el sistem a y no tiene que ser necesariam ente
el carácter \n.
614 JA V A : C U R SO D E PROGRAM A CIÓN
/////////////////////////////////////////////////////////////////
// D e f i n i c i ó n de l a clase CListaTfnos.
//
import j a v a . i o . * ;
public c la ss CListaTfnos
I
private R a n d o m A c c e s s F i 1e f e s : // f l u j o
private int nregs: // nú me r o de r e g i s t r o s
private i n t tamañoReg = 140: // t a ma ñ o d e l r e g i s t r o en b y t e s
// B u s c a r un d e t e r m i n a d o registro a p artir de po s
// M é t o d o q u i c k s o r t p a r a o r d e n a r e l f i c h e r o ////////////////////
pu blic void q u i c k s o r t O throws IOException
I
qs(0, nregs - 1):
// O b t e n e r d e l r e g i s t r o m i t a d , el campo p o r e l que
// s e va a o r d e n a r e l f i c h e r o .
mitad - c a m p o ((iz q + d e r ) / 2 ) :
do
I
while (cam poíizq).com pareíoím itad) < 0 && i z q < s u p ) izq++:
w h ile (m it a d .compareTo(campo(der)) < 0 && d e r > i n f ) der--;
i f ( iz q < - der)
I
p e r m u ta rR e g istro s(iz q . der):
izq++: d e r --;
w h i l e ( i z q <= d e r ) :
if (in f < der) q s ( i n f , d e r );
i f (iz q < sup) q s t iz q , sup):
// P e r m u t a r l o s r e g i s t r o s de l a s p o s i c i o n e s i y j
p r i v a t e v o i d p e r m u t a r R e g i s t r o s ( i n t i . i n t j ) th rows IOException
I
C P e r s o n a x. y;
// L e e r l o s r e g i s t r o s de l a s posiciones i y j
x =* v a l o r E n ( i );
616 JA V A: C U R S O D E PROGRAM A CIÓN
y = v a l o r E n í j ) ;
// E s c r i b i r l o s en l a s posiciones j e i
ponerValorEn(j. x):
p o n e r V a l o r E n ( i , y );
I
// O b t e n e r e l campo u t i l i z a d o p a r a o r d e n a r , d e l r e g i s t r o nreg
p r i v a t e S t r i n g campotint n reg) throws IOException
I
f e s . s e e k ( n r e g * tamañoReg); // s i t u a r e l p u n t e r o de L/E
r e t u r n f e s . re a d U T F t ): // d e v u e l v e e l nombre
El m étodo perm utarR egistros es llam ado p o r qs (q uicksort) cuando hay que
perm utar d o s registros del fichero para que queden correctam ente ordenados.
F i l e f i c h e r o - new F i l e C l i s t a t f n o s . d a t " ) :
l i s t a t f n o s - new C L i s t a T f n o s ( f i c h e r o ) :
// ...
c a s e 6: // o r d e n a r
l i s t a t f n o s . q u i c k s o r t f ):
break:
ALGORITMOS HASH
Los algoritm os hash son m étodos de búsqueda, que proporcionan una longitud de
búsqueda pequeña y una flexibilidad superior a la de otros m étodos, com o puede
ser, el m étodo de búsqueda binaria que requiere que los elem entos de la matriz
estén ordenados.
Matrices hash
Clave Contenido
50 4 0
3721
6375
La m atriz se organiza con elem entos form ados p o r dos m iem bros: clave y
contenido. La clave constituye el m edio de acceso a la m atriz. A plicando a la cla
ve una función de acceso f a , previam ente definida, obtenem os un núm ero entero
positivo i correspondiente a la posición del elem ento en la m atriz.
i = fa (c la v e)
Com o ejem plo, suponer que la clave de acceso se corresponde con el núm ero
del docum ento nacional de identidad (dni) y que el contenido son los datos co
rrespondientes a la persona que tiene ese dni. U na función de acceso, i= fa(dni),
que haga corresponder la posición del elem ento en la m atriz con el d n i, es inm e
diata: / = dni. E sta función así definida presenta un inconveniente y es que el nú
m ero de valores posibles de i es dem asiado grande para utilizar una m atriz. Para
solucionar este problem a, siem pre es posible, dada una m atriz de longitud L, crear
una función de acceso, fa , que genere un valor com prendido entre 0 y L, m ás co
618 JA VA: C U R SO D E PROGRAM A CIÓN
m únm ente entre 1 y L. E n este caso puede suceder que dos o m ás claves den lugar
a un m ism o valor de i:
Cláve Contenido
5040
3721
688 : 4007
3900
c
6375
i = fa (6 3 8 3 ) = 3
accesos = (2-k)/(2-2k)
siendo k igual al núm ero de elem entos existentes en la m atriz dividido p o r L. Por
ejem plo, si existen 60 elem entos en una m atriz de longitud L = 100 , el núm ero m e
dio de accesos para localizar un elem ento será:
Para red u cir al m áxim o el núm ero de colisiones y, com o consecuencia, obte
ner una longitud m edia de búsqueda baja, es im portante eleg ir bien la función de
acceso. U na fu n ció n de acceso o fu n ció n hash bastante utilizada y q u e proporcio
na una distribución de las claves uniform e y aleatoria es la fu n ció n m ita d d el c u a
drado q ue dice: “dada una clave C, se eleva al cuadrado (C 2) y se cogen n bits del
m edio, siendo 2" <= L '\ Por ejem plo, supongam os:
L = 2 5 6 lo q u e implica n = 8
C = 6 25
C2 = 3 9 0 6 2 5 ( 0 < = C2 < = 2 32- l )
3 9 0 6 2 5 10 = 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 ;
n b i t s del m e d i o : 0 1 0 1 1 1 1 1 ; = 9 5 10
i = m ódulo(clave/L)
C uando se utilice esta función es im portante elegir un núm ero prim o p ara L,
con la finalidad de que el núm ero de colisiones sea pequeño. Esta función es lle
vada a cabo en Jav a p o r m edio del operador %.
5040 JT 2039 0
C ada elem ento de esta estructura incorpora un nuevo m iem bro R, el cual es
una referencia a la lista encadenada de desbordam iento.
Eliminación de elementos
En el m étodo hash la elim inación de un elem ento no es tan sim ple com o dejar va
cío dicho elem ento, ya que esto daría lugar a que los elem entos insertados por c o
lisión no puedan ser accedidos. Por ello, se suele utilizar un m iem bro
com plem entario que sirva para poner una m arca de que dicho elem ento está eli
m inado. E sto perm ite acceder a otros elem entos que dependen de él p o r colisio
nes, ya que la clave se conserva y tam bién perm ite insertar un nuevo elem ento en
esa posición cuando se dé una nueva colisión.
Clase CHashAbierto
C om o ejercicio escribim os a continuación una clase denom inada C H ashAbierto
que proporciona los m étodos necesarios para trabajar con m atrices hash utilizando
el m étodo hash abierto, cuyo seudocódigo se expone a continuación:
C A PÍTU LO 14: ALGORITM OS 6 2 1
< m é t o d o h a s h ( m a t r i z . e l e m e n t o x)>
[La m a t r i z está i niciada a cero ]
i - matricula módulo número_elementos
DO U H I L E (no i ns er t a d o y h aya e l e m e n t o s libres)
IF ( e l e m e n t o "i" e s t á libre) THEN
c o p i a r e l e m e n t o x e n la p o s i c i ó n i
ELSE
I F ( cl av e d u p l i c a d a ) THEN
er ro r: c l a v e d u p l i c a d a
ELSE
[se ha p r o d u c i d o una c o l i s i ó n ]
[ a v a n z a r al s i g u i e n t e e l e m e n t o ]
i - i+1
IF (i - n ú m e r o _ e l e m e n t o s ) THEN
i - 0
EN DI F
ENDIF
ENDIF
ENDDO
E N D <h as h>
L a clase C H ashA bierto que vam os a im plem entar incluirá un atributo privado
m atrizhash para referenciar la m atriz hash. Un objeto C H ashAbierto encapsula
una m atriz hash de 101 elem entos por om isión, que son referencias a objetos de
tipo O b je c t. lo que perm itirá alm acenar elem entos de cualquier clase. A sim ism o,
incluye los m étodos indicados en la tabla siguiente:
//////////////////////////////////////////////////////////////////
// C l a s e a b s t r a c t a C H a s h A b i e r t o : m é t od o h a s h a b i e r t o .
// Para u t i l i z a r l o s métodos p r o p o r c io n a d o s por e s t a c l a s e .
// t e n d r e m o s q u e c r e a r una s u b c l a s e de e l l a y r e d e f i n i r l o s
// m é t o d o s : f a ( f u n c i ó n de a c c e s o ) y c o m p a r a r .
//
public abstract class CHashAbierto
I
il A t r i b u t o s
p r iv a t e ObjectC] m atrizhash;
// M é t o d o s
public CHashAbierto()
I
m a t r i z h a s h = new O b j e c t C 1 0 1 3 :
// B u s c a r un número p r i m o a p a r t i r d e un nú me r o d a d o / / / / / / / / / / /
p u b l i c i n t n ú m e r o P r i m o ( i n t n)
I
boolean primo = f a l s e ;
int i, r = (in t)M ath.sqrt((dou ble)n):
i f (n % 2 — 0) n++;
w hile (¡prim o)
I
primo = tru e :
f o r ( i = 3: i < = r ; i + = 2 )
i f (n % i = = 0) primo = f a l s e :
C A P ÍT U L O 14: A LG O RITM O S 6 2 3
if (¡prim o) n + = 2: // s i g u i e n t e i mp a r
I
return n;
// F u n c i ó n d e a c c e s o / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
II E s t e m ét od o d e b e s e r r e d e f i n i d o en una s u b c l a s e p a r a p o d e r
II d e f i n i r l a f u n c i ó n de a c c e s o q u e e l u s u a r i o d e s e e a p l i c a r ,
public abstract in t fa(0bject obj):
// M é t o d o c o m p a r a r / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
II E s t e m ét od o d e b e s e r r e d e f i n i d o en una s u b c l a s e p a r a que
// p e r m i t a c o m p a r a r d o s e l e m e n t o s de l a m a t r i z h a s h p o r el
// a t r i b u t o que n e c e s i t e m o s en ca d a momento,
p u b l i c a b s t r a c t i n t c o m p a r a r ( O b j e c t o b j l . O b j e c t o b j 2>:
// M é t o d o h a s h a b i e r t o / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
p u b l i c v o i d h a s h l n ( 0 b j e c t x)
l
int i: // I n d i c e p a r a a c c e d e r a un e l e m e n t o
i n t c o n t a = 0: // c o n t a d o r
boolean in s e r t a d o = f a l s e :
i = f a ( x ); // f u n c i ó n d e a c c e s o
w h i l e ( ¡ i n s e r t a d o && c o n t a < m a t r i z h a s h . l e n g t h )
I
if (m a triz h a s h [ i] == n u il) // e l e m e n t o l i b r e
I
m a t r i z h a s h [ i ] = x;
insertado = true;
I
e l s e // el a v e d u p l i ca d a
i f (comparar(x. m a t r iz h a s h [ i]) = = 0)
I
S y s t e m . o u t .p r in t ln ( "e r r o r : clave du p lica d a "):
insertado = true:
1
else // c o l i s i ó n
I
// S i g u i e n t e e l e m e n t o l i b r e
i++; conta++:
i f (i == m a trizha sh .length ) i = 0:
if (co n ta == m a t r i z h a s h . l e n g t h )
System .out.p r i n t l n ( " e r r o r : m atriz llena\n"):
// x p r o p o r c i o n a r á e l a t r i b u t o u t i l i z a d o p a r a b u s c a r . El r e s t o
// de l o s a t r i b u t o s no i n t e r e s a n ( s o n l o s que s e d e s e a c o n o c e r )
in t i; // i n d i c e p a r a acceder a un e l e m e n t o
i n t c o n t a = 0 ; // c o n t a d o r
boolean encontrado = f a l s e :
i * f a ( x ): II f u n c i ó n de a c c e s o
i f ( m a t r i z h a s h t i ] == n u i l ) r e t u r n n u i l :
w h i l e ( ¡ e n c o n t r a d o && c o n t a < m a t r i z h a s h . l e n g t h )
I
if (comparar(x, m a t r i z h a s h [ i ] ) = = 0)
I
x = m atrizha sh [i];
encontrado = true:
1
else // c o l i s i ó n
I
// S i g u i e n t e e l e m e n t o l i b r e
i++; conta++;
i f (i == m atrizh a sh .le n g th ) i = 0;
if (conta m a t r i z h a s h . 1 e n g t h ) // no e x i s t e
r e t u r n nul'l :
el se
r e t u r n x;
/////////////////////////////////////////////////////////////////
// D e f i n i c i ó n de l a c l a s e CAl u mno
II
public class CAl u mno
I
// A t r i b u t o s
p riv a te int m atrícula:
p r i v a t e S t r i n g nombre:
// Métodos
CA PÍTU LO 14: ALGORITM OS 6 2 5
public C A I u m n o ( ) {1
no mb r e = nom;
m a t r i c u l a = mat;
no mb r e - nom;
r e t u r n n o mbr e ;
public void a s i g n a r M a t r i c u l a ( i n t ma t )
matri cu la = m at;
public long o b t e n e r M a t r ic u la ()
I
return m atrícula;
Los objetos C Alum no serán alm acenados en la m atriz utilizando com o clave
el núm ero de m atrícula. Según esto, derivam os la clase H ashA bierto de la clase
abstracta C H ashAbierto y redefinim os los m étodos f a y com parar así:
//////////////////////////////////////////////////////////////////
// C l a s e d e r i v a d a de l a c l a s e a b s t r a c t a C H a s h A b i e r t o . Redefine
// l o s m é t o d o s : f a ( f u n c i ó n d e a c c e s o ) y c o m p a r a r .
//
public class HashAbierto extends CHashAbierto
I
public HashAbierto(int nElementos)
I
s u p e r ( n F l e m e n t o s );
//////////////////////////////////////////////////////////////////
Finalm ente, escribirem os una aplicación Test que perm ita crear un objeto
H ashA bierto, envoltorio de la m atriz hash. Para probar su correcto funciona
m iento escribirem os código que perm ita tanto añadir com o buscar objetos en di
cha matriz.
import j a v a . i o . * :
//////////////////////////////////////////////////////////////////
// A p l i c a c i ó n para t r a b a j a r c o n una m a t r i z hash
//
public e la ss Test
I
public static void m a in (S trin g [] args)
I
// D e f i n i c i ó n de v a r i a b l e s
PrintStream f lu jo S = System.out;
i n t n _ e l e m e n t o s : // nú m e r o de e l e m e n t o s de l a m a t r i z hash
i nt i :
S t r i n g nombre:
i nt m a t r ic u l a :
C AI um n o x ;
// C r e a r un o b j e t o H a s h A b i e r t o ( e n c a p s u l a l a m a t r i z h a s h )
S y s t e m . o u t . p r i n t l n ( " n ú m e r o de e l e m e n t o s : ");
n _ e l e m e n t o s = L e e r . d a t o l n t t );
H a s h A b i e r t o m = new H a s h A b i e r t o ( n _ e l e m e n t o s ) :
f l u j o S . p r i n t l n ( " S e c o n s t r u y e una m a t r i z de ” +
m .númeroDeElementos( ) + " e l e m e n t o s " ) :
// In t r o d u c ir datos
CA PÍTU LO 14: A LG O RITM O S 6 2 7
f l u j o S . p r i n t í n ( " In t r o d u c ir datos. ” +
"Para f i n a l i z a r , m atrícula = 0 \ n ") ;
flujoS. p rin tC m a tricu la : “ ) : m a t r í c u l a = L e e r , d a t o l n t ( ):
w h i l e ( m a t r i c u l a ! - 0)
1
f l ujoS. p r in t ( "nombre: " ) : no mb r e = L e e r . d a t o O :
m .hashln(new CAIumno(nombre. m a t r íc u l a ) ) :
flujoS.printC m atricula: ” ) : m a t r í c u l a - L e e r . d a t o l n t í ):
// B u s c a r d a t o s
f lu j o S . p r in t ln t " B u s c a r datos. ” +
"Para f i n a l i z a r , m atricu la = 0 \ n ") ¡
flujoS.printC m atricula: " ) : m a t r i c u l a = L e e r . d a t o I n t ( ):
w h i l e ( m a t r i c u l a ! - 0)
1
x - (CAlumno)m.hashOut(new C A lu m n o ( "" . m a t r i c u l a ) ) :
i f ( x ! = n u l 1)
flu jo S . p r in t ín ( "n o m b r e : " + x .obtenerNombret));
el se
f l u j o S . p r i n t l n ( " N o e x i s t e ” ):
flujoS.printC m atricula: ” ) ; m a t r í c u l a - L e e r . d a t o I n t ( ):
I
1
I
EJERCICIOS RESUELTOS
1. C om parar las dos siguientes versiones del m étodo búsqueda binaria e indicar cuál
de ellas es m ás eficaz.
i f (m.length = 0 ) r e t u r n - 1;
i n t m i t a d , i n f = 0. s u p - m . l e n g t h - 1;
do
I
mitad = ( i n f + sup) / 2:
628 JA V A : C U R S O D E P R O G R A M A C IÓ N
if (v > m [m itad])
i n f = mi t a d + 1;
el se
s u p = mi t a d - 1 :
I
while( m[mitad] ! = v && i n f <= s u p ) ;
if (m[mi t a d ] = v)
r e t u r n mitad;
else
r e t u r n -1;
i f (m.length = 0 ) r e t u r n - 1;
i n t m i t a d , i n f = 0. s u p = m . l e n g t h - 1;
do
I
mi t a d = ( i n f + sup) / 2.:
i f (v > m[mitad])
i nf = mi t a d + 1;
el se
sup = m itad;
if ( m [ i n f ] = = v)
return in f;
else
r e t u r n -1;
Ii
L a aplicación siguiente perm ite ver de una form a práctica que la versión se
gunda em plea m enos tiem po de ejecución que la prim era. E sta aplicación crea una
m atriz y, utilizando prim ero una versión y después la otra, realiza una búsqueda
por cada uno de sus elem entos y dos búsquedas m ás para dos valores no pertene
cientes a la m atriz, uno m enor que el m enor y otro m ayor que el m ayor. El tiem po
de ejecución m edido en m ilisegundos se obtiene por diferencia de los tiem pos de
vueltos por el m étodo c u rre n íT im e M illis de la clase S ystem al iniciar cada pro
ceso de búsqueda y al finalizarlo.
class prueba
1
public static int búsquedaBinl(double[] m. d o u b l e v)
I
// V e r s i ó n 1
// V e r s i ó n 1
t i - S y s t e m . c u r r e n t U m e M i 11 i s ( ) ;
i = búsquedaBinl(a.O ):
f o r ( i - 0: i < n; i + + )
i « b ú s q u e d a B i n l ( a . i + 1 ):
i = b ú s q u e d a B i n l ( a . n + 1 ):
S y s te m . o u t .p r in t ln í( S y s t e m . c u r r e n t T im e M illis () - t i) + " m ilisegundos"):
// V e r s i ó n 2
t i - S y s t e m . c u r r e n t T i m e M i 11 i s ( ) :
i - búsquedaBin2(a,0):
f o r ( i = 0 : i < n; i + + )
i - b ú s q u e d a B i n 2 ( a . i + L ):
i - b ú s q u e d a B i n 2 ( a . n + 1 ):
S y s t e m . o u t . p r i n t í n ( ( S y s t e m . c u r r e n t T i m e M i 11 i s ( ) - t i ) + ” m i l i s e g u n d o s " ) :
630 JA V A : C U R SO D E PROGRAM A CIÓN
2. Un centro num érico es un núm ero que separa una lista de núm eros enteros
(com enzando en 1) en dos grupos de núm eros cuyas sum as son iguales. El prim er
centro num érico es el 6, el cual separa la lista (1 a 8) en los grupos: (1, 2, 3, 4, 5)
y (7, 8) cuyas sum as son am bas iguales a 15. El segundo centro num érico es el 35,
el cual separa la lista ( l a 49) en los grupos: (1 a 34) y (36 a 49) cuyas sum as son
am bas iguales a 595. E scribir un program a que calcule los centros num éricos en
tre 1 y n.
El ejem plo (1 a 5 ) 6 (7 a 8), donde se observa que 6 es un centro num érico, sugie
re ir probando si los valores 3, 4, 5, 6, .... en, n-1 son centros num éricos. En
general en es un centro num érico si la sum a de todos los valores enteros desde 1 a
cn-1 coincide con la sum a desde cn+ 1 a lim _sup_grupo2 (lím ite superior del gru
po segundo de núm eros). Para que el program a sea eficiente, buscarem os el valor
lim _sup_grupo2 entre los valores cn+ 1 y n-1 utilizando el m étodo de búsqueda
binaria. R ecuerde que la sum a de los valores enteros entre 1 y x viene dada p o r la
expresión ( x * (x + l))/2 .
II B ú s q u e d a binaria
C A PÍTU LO 14: ALGORITM OS 6 3 1
do
I
m i t a d = ( i n f + s u p ) / 2;
suma_grupo2 = (m itad * (m itad + 1 ) ) / 2 • suma_grupol - en;
i f (sum a_gru po l > suma_grupo2)
inf = m i t a d + 1:
el se
s u p = m i t a d - 1;
I
while ( suma_grupol ! - s u m a _ g r u p o 2 && i n f < = s u p ) ;
EJERCICIOS PROPUESTOS
1. Escribir un program a que calcule los centros num éricos entre i y n utilizando el
algoritm o de búsqueda secuencial en lugar del de búsqueda binaria. U tilice el
m étodo c u rre n tT im e M illis de la clase S y ste m para com parar cuánto m ás lento es
el m étodo de búsqueda secuencial que el de búsqueda binaria.
4. E scribir una clase que incluya un m étodo orilenarM atriz2D que perm ita ordenar
ascendentem ente una m atriz de dos dim ensiones en la que cada elem ento sea un
o bjeto de la clase C Alum no expuesta anteriorm ente en este m ism o capítulo.
Para realizar el proceso de ordenación em plee el m étodo que quiera, pero hágalo
directam ente sobre la m atriz por el cam po nom bre.
Finalm ente, escriba una aplicación que pueda recibir el nom bre de un fichero
com o argum ento en la línea de órdenes, cuyos registros sean de la clase C A lum no,
alm acene los registros en una m atriz de dos dim ensiones y utilizando el m étodo
o rdenar que acaba de escribir, ordene la m atriz y la visualice una vez ordenada.
5. R ealizar un program a que cree una lista dinám ica a partir d e una serie de núm eros
cualesquiera introducidos por el teclado. A continuación, ordenar la lista ascen
dentem ente utilizando el m étodo quicksort.
// M é t o d o s
// . . .
I
HILOS
U no de los pasos im portantes que la inform ática dio en favor de los desarrollado-
res de softw are fue colocar un nivel de softw are por encim a del hardw are de un
ordenador. Este nivel de softw are, conocido com o sistem a operativo, es en esen
cia una interfaz fácil de u tilizar que nos perm ite controlar todas las partes del
hardw are, en la m ayoría de los casos, sin un profundo conocim iento del m ism o.
A su vez, los sistem as operativos tam bién han experim entado un gran avance,
pasando de los sistem as de un único procesador a los actuales sistem as operativos
distribuidos o de red, o a los sistem as operativos con m ultiprocesadores. Esta
evolución ha desem bocado en un m ejor aprovecham iento de todos los recursos
disponibles, perm itiéndonos ejecutar cada vez m ás tareas en m enos tiem po.
CONCEPTO DE PROCESO
Un proceso es un ejem plar en ejecución de un program a. C ada proceso consta de
bloques de código y de datos cargados desde un fichero ejecutable o desde una
biblioteca dinám ica. Tam bién es propietario de otros recursos que se crean du
rante la vida de dicho proceso y se destruyen cuando finaliza. Por ejem plo, un
proceso posee:
634 JA V A : C U R S O D E P R O G R A M A C IÓ N
P re p a ra d o
^ lo q u e a d ^ J
^ E n eje cu c ió n
HILOS
U n hilo (thread - llam ado tam bién proceso ligero o subproceso) es la unidad de
ejecución de un proceso y está asociado con una secuencia de instrucciones, un
conjunto de registros y una pila. C uando se crea un proceso, el sistem a operativo
crea su prim er hilo (hilo prim ario) el cual puede a su vez, crear hilos adicionales.
E sto pone de m anifiesto que un proceso no se ejecuta, sino que es sólo el espacio
de direcciones donde reside el código que es ejecutado m ediante uno o m ás hilos.
s a l d o - C u e n t a . O b t n e r S a l d o ( );
saldo +- ingreso:
Cuenta.EstablecerSald o( saldo ):
Los hilos com parten la U C P de la m ism a form a que lo hacen los procesos,
pueden crear hilos hijo y se pueden bloquear. N o obstante, m ientras un hilo esté
bloqueado se puede ejecutar otro hilo del m ism o proceso, en el caso de hilos so
portados por el kernel (núcleo del sistem a operativo: program as en ejecución que
hacen que el sistem a funcione), no sucediendo lo m ism o con los hilos soportados
p o r una aplicación (por ejem plo, en W indow s N T todos los hilos son soportados
por el kernel). Un ejem plo, im aginem os que alguien llega a un cajero para depo
sitar dinero en una cuenta y casi al m ism o tiem po, un segundo cliente inicia la
m ism a o peración sobre la m ism a cuenta en otro cajero. P ara que los resultados
sean correctos, el segundo cajero quedará bloqueado hasta que el registro que está
siendo actualizado por el prim er cajero, quede liberado.
R esum iendo, sabem os que en la U C P puede haber varios program as con va
rios procesos ejecutándose concurrentem ente, habilidad que se denom ina m ultita-
rea, y a su vez, un proceso puede crear varios hilos y ejecutarlos de form a
concurrente, lo que se traduce básicam ente en una m ultitarea dentro de m ultitarea:
el usuario sabe que puede ejecutar varias aplicaciones sim ultáneam ente, y el pro
gram ador sabe que cada aplicación puede ejecutar varios hilos a la vez.
Estados de un hilo
Igual que los procesos con un solo hilo de control, los hilos pueden encontrarse en
uno de los siguientes estados:
P re p a ra d o
J
B lo q u ea d o
J
E n eje cu c ió n
)
• Nuevo. El hilo ha sido creado pero aún no ha sido activado. C uando se active
pasará al estado preparado.
• Preparado. El hilo está activo y está a la espera de que le sea asignada la
UCP.
CA PÍTU LO 15: HILOS 6 3 7
E s evidente que los hilos son extraordinariam ente útiles, pero tam bién es evi
dente que si no se utilizan adecuadam ente pueden introducir nuevos problem as
m ientras tratam os de resolver otros m ás antiguos. P or lo tanto, es un error pensar
638 JA V A: C U R SO DE PROGRAM A CIÓN
que la m ejor form a de desarrollar una aplicación es dividirla en partes que se eje
cuten cada u na com o un hilo.
La m ayoría del soporte que Java proporciona para trabajar con hilos reside en la
clase T h re a d del paquete ja v a .la n g, aunque tam bién la clase O b ject, la interfaz
R u n n a b le y la clases T h r e a d G r o u p y T h re a d D e a th del m ism o paquete, así c o
m o la m áquina virtual, proporcionan algún tipo de soporte.
Los hilos en Java se pueden crear de dos form as: escribiendo una nueva clase
derivada de T h re a d , o bien haciendo que una clase existente im plem ente la in
terfaz R u n n a b le .
// C o n s t r u c t o r e s
T h r e a d ( [ argumentos])
// M é t o d o s
s t a t i c Thread c u r r e n t T h r e a d ( )
II D e v u e l v e una r e f e r e n c i a a l h i l o que a c t u a l m e n t e e s t á
// en e j e c u c i ó n ,
void d e s t r o y ( )
// D e s t r u y e e s t e h i l o , s i n r e a l i z a r n i n g u n a o p e r a c i ó n de
// l i m p i e z a .
S t r i n g getNameO
// D e v u e l v e e l no mb r e d e l h i l o ,
int g e t P r io r it y ( )
II D e v u e l v e l a p r i o r i d a d d e l h i l o ,
void i n t e r r u p t f )
II E n v í a e s t e h i l o al e s t a d o d e p r e p a r a d o ,
boolean i s A l i v e O
II V e r i f i c a s i e s t e h i l o e s t á v i v o ( n o ha t e r m i n a d o ) .
C A P ÍT U L O 15: H IL O S 639
// I n i c i a l a e j e c u c i ó n de e s t e h i l o : l a m á q u i n a v i r t u a l de J a v a
// i n v o c a al m é t od o ru n de e s t e h i l o ,
s t a t ic void y i e l d í )
// D e t i e n e t e m p o r a l m e n t e l a e j e c u c i ó n de e s t e h i l o p a r a
II p e r m i t i r l a e j e c u c i ó n de o t r o s .
// M é t o d o s h e r e d a d o s d e l a c l a s e O b j e c t : n o t i f y . n o t i f y A l l y w a i t
void n o t i f y ( )
// D e s p i e r t a un h i l o de l o s que e s t á n e s p e r a n d o p o r el
// m o n i t o r d e e s t e o b j e t o ,
v o i d n o t i f y A l 1( )
II D e s p i e r t a t o d o s l o s h i l o s q u e e s t á n e s p e r a n d o por el
// m o n i t o r de este objeto,
vo id w a i t (tmilisegundosl. nanosegundos]])
II E n v í a e s t e h i l o a l e s t a d o d e e s p e r a h a s t a que o t r o h i l o
// i n v o q u e a l m é t od o n o t i f y o n o t i f y A l l . o h a s t a q u e t r a n s c u r r a
// e l t i e m p o e s p e c i f i c a d o .
I
Según hem os dicho anteriorm ente, un hilo puede ser un objeto d e una subclase de
la clase T h re a d . Entonces, para que una aplicación pueda lanzar un determ inado
hilo de ejecución, el prim er paso es escribir la clase del hilo derivada de T h re a d y
sobreescribir el m étodo r u n heredado por ésta, con el fin de especificar la tarea
que tiene que realizar dicho hilo.
Por ejem plo, supongam os que querem os escribir una aplicación elem ental que
en un instante determ inado de su ejecución lance un hilo que realice un sim ple
conteo. La clase del hilo puede ser la siguiente:
Para p o d er lanzar un hilo de la clase C ontador A delante, prim ero tenem os que
construir un objeto de esa clase y después enviar a dicho objeto el m ensaje s ta rt;
De esto últim o se encarga el constructor de la clase. La siguiente aplicación
m uestra un ejem plo:
El operador new crea un hilo cuentaAdelante (el hilo está en el estado nuevo).
El m étodo start cam bia el estado del hilo a preparado. De ahora en adelante y
hasta que finalice la ejecución del hilo cuentaAdelante, será el planificador de
hilos el que determ ine cuándo éste pasa al estado de ejecución y cuándo lo aban
dona para p erm itir que se ejecuten sim ultáneam ente otros hilos.
Según lo expuesto, el m étodo start no hace que se ejecute inm ediatam ente el
m étodo ru n del hilo, sino que lo sitúa en el estado preparado para que com pita
por la U C P ju n to con el resto de los hilos que haya en este estado. Sólo el planifi
cador puede asignar tiem po de U C P a un hilo y lo hará con cuentaAdelante en
cualquier instante después de que haya recibido el m ensaje start. Por lo tanto, un
hilo durante su tiem po de vida, gasta parte de él en ejecutarse y el resto en perm a
necer en alguno de los estados distintos al de ejecución. M ás adelante aprenderá
cóm o un hilo transita entre los diferentes estados.
1. El objeto debe ser de una clase que im plem ente la interfaz R u n n a b le , ya que
es esta la que aporta el m étodo run.
2. Sobreescribir el m étodo r u n con las sentencias que tiene que ejecutar el hilo.
3. C rear un objeto de esa clase.
4. C rear un objeto de la clase T h re a d pasando com o argum ento al constructor,
el objeto cuya clase incluye el m étodo run.
5. Invocar al m étodo start del objeto T h re ad .
ThreadtRunnable objeto)
Para poder lanzar un hilo asociado con la clase ContadorAtras, prim ero tene
m os que co n struir un objeto de la m ism a, después un objeto de la clase T h re a d
pasando com o argum ento el objeto de la clase ContadorAtras y finalm ente, enviar
al objeto T h re a d el m ensaje start; de estas dos últim as operaciones se encarga el
co nstructor ContadorAtras. La siguiente aplicación m uestra un ejem plo:
E sta form a de lanzar un hilo quizás sea un poco m ás com plicada. Sin em bar
go, hay razones suficientes para hacer este pequeño esfuerzo. Si el m étodo r u n es
parte de la interfaz de una clase cualquiera, tiene acceso a todos los m iem bros de
esa clase, cosa que no ocurre si pertenece a una subclase de T h re a d . O tra razón
es que Java no perm ite la herencia m últiple; entonces, si escribim os una clase de
rivada de T h re a d , esa clase no puede ser a la vez una subclase de cualquier otra.
P o r ejem plo, para poder asociar un hilo con la clase CCuentaAhorro derivada de
CCuenta (capítulo 10), la única form a de hacerlo es que CCuentaAhorro imple-
m ente la interfaz R u n n a b le .
Finalm ente, a pesar d e que en ocasiones hablem os en térm inos sim ilares a: “ la
clase ContadorAtras es un hilo” , desde el punto de vista de la program ación
orientada a objetos no es correcto expresarse así, a pesar de entendem os. Lo único
CA PÍTU LO 15: HILOS 6 4 3
que es correcto es: “ la clase ContadorAtras está asociada con un hilo” . Observe
en el ejem plo anterior que el hilo es el objeto de la clase T h re a d , no el objeto de
la clase ContadorAtras. Entonces, siem pre que necesitem os que una clase tenga el
com portam iento de un hilo, deberem os im plem entar en la m ism a la interfaz R u n -
nable y sobreescribir el m étodo run.
Demonios
Un demonio, a diferencia de los hilos tradicionales, no form a parte de la esencia
del program a, sino de la m áquina Java. Los demonios son usados generalm ente
para prestar servicios en segundo plano a todos los program as que puedan nece
sitar el tipo de servicio proporcionado. Por ejem plo, el recolector de basura de Ja
va es un ejem plo de este tipo de hilos.
Para crear un hilo demonio sim plem ente hay que crear un hilo norm al y en
viarle el m ensaje setD aenion:
hilo.setDaem on(true):
Si un hilo es un demonio, entonces cualquier hilo que el cree será autom áti
cam ente un demonio.
Para sab er si un hilo es un demonio sim plem ente hay que enviarle el m ensaje
isD a e m o n . El m étodo que se ejecuta devolverá tru e si el hilo es un demonio y
false en caso contrario;
boolean b - h i 1 o . i s D a e m o n ( );
644 JA V A : C U R S O D E P R O G R A M A C IÓ N
El intérprete Java norm alm ente perm anece en ejecución hasta que todos los
hilos en el sistem a finalizan su ejecución. Sin em bargo, los dem onios son una ex
cepción, ya que su labor es proporcionar servicios a otros program as. Por lo tanto,
no tiene sentido continuar ejecutándolos cuando ya no haya program as en ejecu
ción. Por esta razón, el intérprete Java finalizará cuando todos los hilos que que
den en ejecución sean dem onios. El siguiente ejem plo m uestra cóm o im plem entar
un hilo dem onio:
//////////////////////////////////////////////////////////////////
// H i l o demonio. Suena "bip" a p r o x i m a d a m e n t e ca d a segundo
//
public class C D e m o ni o e x t e n d s T h r e a d
I
p u b l i c CDemoni o ( )
I
setDaemon(true);
s t a r t t ):
1
//////////////////////////////////////////////////////////////////
Para iniciar un dem onio, dbip, de la clase C D em onio basta con escribir una
sentencia com o la siguiente:
C De m o n i o d b i p = new C D e m o n i o O ;
Finalizar un hilo
Un hilo term ina de form a natural cuando su m étodo r u n devuelve el control.
C uando esto sucede el hilo pasa al estado m uerto (ha term inado) y no hay forma
de salir de este estado. Esto es, una vez que el hilo está m uerto, no puede ser
arrancado otra vez; si deseam os ejecutar otra vez la tarea desem peñada p o r el hilo
C A PÍTU LO 15: H ILOS 6 4 5
hay que construir un nuevo objeto hilo y enviarle el m ensaje start, pero sí se pue
de invocar a sus m étodos.
Por ejem plo, supongam os una clase ContadorAdelante que m uestra un conta
d o r ascendente que será detenido cuando el atributo continuar sea false. La clase,
adem ás de este atributo y del m étodo r u n que m uestra la cuenta, tiene un m étodo
term inar que pone el atributo continuar a false. y dos constructores: el prim ero
inicia el hilo con el nom bre asignado por om isión y el segundo, tam bién lo inicia
pero con el nom bre pasado com o argum ento.
//////////////////////////////////////////////////////////////////
// C l a s e que d e f i n e un h i l o q u e c u e n t a a s c e n d e n t e m e n t e m i e n t r a s
// q u e e l a t r i b u t o c o n t i n u a r s ea t r u e .
//
public class C o n ta d o r A d e la n t e e x t e n d s Thread
I
private boolean c o n t in u a r - true:
public ContadorAdelantet>
I
s t a r t ():
1
public void r u n ( )
I
i n t i - 1:
while (continuar)
(
System .out.print(getNam e() + " " + i++ + " \ r " ) ¡
I
S y s t e m . o u t . p r i n t l n ( ):
1
public void t e r m i n a r O
I
continuar = fa lse;
I
I
//////////////////////////////////////////////////////////////////
un contador ascendente en la pantalla, el dem onio hace sonar un bip cada segun
do. El contador se detendrá cuando el usuario pulse la tecla Entrar.
import j a v a . i o . * :
//////////////////////////////////////////////////////////////////
// T e r m i n a r un h i l o .
II
p u b lic c l a s s Test
I
public s t a t ic void m a in (S trin g [] args)
I
// L a n z a r e l d e m o n i o d b i p
C D e m o n i o d b i p = new C D e m o n i o O ;
// L a n z a r el h i l o c u e n t a A d e l a n t e
C o n t a d o r A d e l a n t e c u e n t a A d e l a n t e = new C o n t a d o r A d e l a n t e ( " C o n t a d o r + " );
S y s t e m . o u t . p r i n t l n ( " P u l s e [ E n t r a r ] p a r a f i n a l i z a r ” );
I n p u t S t r e a m R e a d e r i s = new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) ;
B u f f e r e d R e a d e r b r = new B u f f e r e d R e a d e r ! i s ) :
try
I
b r .r e a d L i n e ( ); II e j e c u c i ó n d e t e n i d a hasta pulsar [Entrar]
I
c a t c h ( l O E x c e p t i o n e) I I
// P e r m i t i r a l h i l o c u e n t a A d e l a n t e finalizar
c u e n t a A d e l a n t e . t e r m i n a r ( );
//////////////////////////////////////////////////////////////////
Controlar un hilo
A hora que ya hem os visto cóm o realizar una determ inada tarea utilizando un hilo,
podem os deducir fácilm ente que su ciclo de vida evoluciona según m uestra la fi
gura siguiente:
Finalizó
N uevo j i start C H ilo 1 .1 H ilo e n 1 “run” H ilo |
■»—------ 1
, hi'° J a e te m a o e je c u c ió n s m u e rto 1
U n hilo, durante su ciclo de vida está transitando por los estados: nu evo, p r e
parado, en ejecución, bloqueado y m uerto, estudiados anteriorm ente. El estado en
ejecución se corresponde en la figura con el bloque “hilo en ejecución” , el cual se
alcanza desde el estado preparado al que pasa el hilo después de que haya sido
C A P ÍT U L O 15: HILOS 647
creado y haber recibido el m ensaje start, y cuando el hilo está vivo y no está en
ejecución es que está detenido, bloque “hilo detenido” .
Precisam ente, el m étodo isA liv e de T h re a d devuelve true si el hilo que reci
be este m ensaje ha sido arrancado (sta rt) y todavía no ha m uerto.
N orm alm ente es el planificador el que controla cuándo un hilo debe estar en
ejecución y cuándo pasa a estar detenido, pero en ocasiones tendrem os que ser
nosotros los q ue program em os las circunstancias bajo las cuales un hilo pueda pa
sar a ejecución, o bien deba pasar de ejecución a algunos de los estados p rep a ra
d o o bloqueado (bloqueado porque esté dorm ido, esté esperando a que otro hilo lo
desbloquee, o esperando a que term ine una operación de E/S, o bien esperando a
apropiarse de un m étodo sincronizado).
Preparado
P re p a ra d o
J
"T i yield
E n eje cu c ió n
J
El m étodo y ie ld es static, por lo tanto, opera sobre el hilo que actualm ente se
esté ejecutando. C uando necesite invocarlo basta con que escriba: Thread.yield().
Bloqueado
M uchos m étodos que ejecutan operaciones de entrada tienen que esperar por al
guna circunstancia en el m undo exterior antes de que ellos puedan proseguir; este
com portam iento se conoce com o bloqueo. P or ejem plo, la sentencia siguiente lee
un byte de la entrada estándar lanzando un hilo que ejecuta read:
n = S y s t e m . i n . r e a d ( );
Java im plem enta m uchos de los bloqueos que ocurren durante una operación
de E/S llam ando a los m étodos sleep y w ait que vem os a continuación.
D orm ido
U n hilo dorm ido pasa tiem po sin hacer nada, p o r lo tanto, no utiliza la UCP.
el tiempo acabó
o interrupt
U na llam ada al m étodo sleep solicita que el hilo actualm ente en ejecución ce
se durante un tiem po especificado. H ay dos form as de llam ar a este método:
T h r e a d . s i e e p ( mi 1 is e g u n d o s ) ;
T h r e a d . s l e e p (mi 1 isegund os , n a n o s e g u n d o s ) :
Se puede observar que el m étodo sleep, igual que yield, es static. Ambos
m étodos operan sobre el hilo que actualm ente se esté ejecutando.
L a figura anterior indica que cuando un hilo despierta (el tiem po que tenía
que dorm ir ha transcurrido) no continúa la ejecución, sino que se m ueve al estado
C A PÍTU LO 15: H ILOS 6 4 9
Esperando
n o tify /n o tify A II
Planificación de hilos
M uchos ordenadores tienen sólo una UCP, así que los hilos que requieran ejecu
tarse deben com partirla. La ejecución de m últiples hilos sobre una única UCP, en
cierto orden, es llam ada planificación. La m áquina Java (Java R untim e Environ-
m ent - JRE: m áquina virtual de Java, incluido el planificador, clases del núcleo
central de Jav a y los ficheros de soporte) soporta un algoritm o de planificación
determ inista (en cualquier m om ento se puede saber qué hilo se está ejecutando o
cuánto tiem po continuará ejecutándose) m uy sim ple, conocido com o fix e d p r io r ity
scheduling (planificación por prioridad: el hilo que se elige para su ejecución es el
de prioridad m ás alta). Esto es, la planificación de la U C P es totalm ente por d ere
cho de prioridad (preem ptive).
Lo anteriorm ente expuesto significa que cada hilo Java tiene asignado una
prioridad definida por un valor num érico entre M /N _ P R IO R IT Y y M AX_PR IO -
R IT Y (constantes definidas en la clase T h re a d ), de form a que cuando varios hilos
estén preparados, será elegido para su ejecución el de m ayor prioridad. Sola
m ente cuando la ejecución de ese hilo se detenga por cualquier causa, podrá eje
cutarse un hilo de m enor prioridad; y cuando un hilo con prioridad m ás alta que el
650 J A V A : C U R S O D E P R O G R A M A C IÓ N
que actualm ente se está ejecutando se m ueva al estado preparado, pasará auto
m áticam ente a ejecutarse.
C uando todos los hilos que com piten p o r la U CP tienen la m ism a prioridad, el
planificador elige para su ejecución al siguiente según el orden resultante de apli
car el algoritm o round-robin (no preem ptive). En este caso, la cola de hilos listos
para ejecutarse se trata com o una cola circular FIFO . La U C P será cedida a otro
hilo bien porque:
R esum iendo, cuando se ejecuta un proceso que tiene varios hilos preparados,
la m áquina Java asigna la U C P en función de la prioridad que tenga asignada el
hilo activo: de m ayor prioridad a m enor prioridad. En Java, los hilos tienen asig
nadas prioridades de 1 a 10 (10 es la prioridad m ás alta: M A X _P R IO R IT Y ). Por
otra parte, cuando el sistem a asigna la U C P a un hilo, trata de igual form a a todos
los hilos de la m ism a prioridad. Esto es, asigna un cuanto al prim er hilo prep a ra
d o de prioridad 10, cuando éste finaliza su intervalo de tiem po, asigna otro cuanto
al siguiente hilo preparado de prioridad 10 y así sucesivam ente. C uando todos los
hilos de prioridad 10 han tenido su intervalo de tiem po, se em pieza otra vez por el
prim ero.
Según lo expuesto ¿cóm o perm itir la ejecución de hilos con prioridad infe
rior? La respuesta está en saber que m uchos hilos del sistem a son detenidos de
vez en cuando, por m otivos diferentes. Así, cuando todos los hilos de prioridad 10
estén detenidos, el sistem a asigna cuantos a los hilos preparados de prioridad 9.
U n razonam iento análogo nos conduce a pensar que los hilos de prioridad 8 sólo
pueden ejecutarse cuando los hilos d e prioridades 10 y 9 estén detenidos. Parece
entonces, que los procesos de prioridad 1 nunca se ejecutarán, o que se ejecutarán
de tarde en tarde. Pero, la verdad es que no es así. La m ayoría de los hilos consu
men su tiem po durm iendo, lo que perm ite la ejecución de los hilos de prioridades
bajas con una frecuencia, probablem ente, un poco inferior.
h i l o . s e t P r i o r i t y (nuevaPrioridad) ;
C onstante Valor
M IN _PR10R1TY
N O R M _PR IO R ITY 5 (valor por om isión)
M A X _PR IO R ITY 10
i nt p = g e t P r i o r i t y ( ) :
652 JA V A: C U R SO D E PROGRAM A CIÓN
El valor que devuelve este m étodo está com prendido entre M ¡N _P R I()R IT Y y
M AX _PR IO R ITY.
El siguiente ejem plo im plem enta una aplicación que visualiza n contadores.
Por ejem plo, para 2 contadores la pantalla m ostraría una línea con el siguiente
formato: nom bre del hilo, prioridad y cuenta.
C ada contador es un hilo. Las prioridades asignadas a cada hilo son diferentes
(2, 3, etc.). La clase que da lugar a cada objeto hilo contador es la siguiente:
// O t r o s m é t od o s
I
El m étodo r u n de la clase sim plem ente genera, cuenta y acum ula 500000
núm eros aleatorios. El m iem bro cuenta es público porque otro hilo C uentas utili
zará ese valor para m ostrar el progreso de cada uno de los contadores.
La clase C uentas se im plem enta tam bién com o un hilo encargado de lanzar
las cuentas. Su m étodo ru n contiene un bucle que se ejecutará m ientras los hilos
contadores estén vivos; en cada iteración m ostrará p o r cada hilo contador su nom
bre, prioridad y estado de la cuenta, y esperará durante nM ilisegundos. La priori
dad del hilo C uentas será (nC uentas+ 2)% Thread.M A X _P R IO R ITY , donde /¡Cuen
tas es el núm ero de hilos contador. Por ejem plo, para 2 hilos contador la prioridad
del prim er hilo, ContadorfO], será 2, la del segundo. C o n ta d o r!I], será 3 y la del
hilo de la clase Cuentas, 4. De esta form a, m ientras el hilo C uentas duerm e los
hilos contador com piten por la U C P (lógicam ente finalizará antes la cuenta el de
m ayor prioridad) y cuando despierte, por ser el hilo de m ayor prioridad obtendrá
inm ediatam ente la U CP y m ostrará los resultados actuales de las cuentas. A con
tinuación se m uestra el código correspondiente a esta clase:
CA PÍTU LO 15: HILOS 6 5 3
public C uentasíint n)
I
n C u e n t a s = n: // nú me r o de h i l o s c o n t a d o r e s
// E s t a b l e c e r l a p r i o r i d a d de e s t e hilo
s e t P r i o r i t y í í n C u e n t a s + 2 I X T h r e a d . H A X _ P R 1 0 R I T Y );
// C r e a r y e s t a b l e c e r l a s p r i o r i d a d e s de l o s hilos contador
c u e n t a = new C o n t a d o r [ n C u e n t a s ] :
f o r ( i n t i = 0; i < n C u e n t a s : i + + )
I
c u e n t a [ i ] = new C o n t a d o r O ;
cuenta [ i ] . s e t P r i o r i t y ( ( i + 3 U T h r e a d .MAX_PRI0RITY -1 );
// M o s t r a r e l nombre y l a p r i o r i d a d d e e s t e h i l o
S y s t e m . o u t . p r in t ln ( t h i s .getNameí) + P-" +
t h i s . g e t P r i o r i t y ( ));
// L a n z a r l o s h i l o s c o n t a d o r e s p a r a s u e j e c u c i ó n
f o r ( i = 0; i < n C u enta s; i+ + )
c u e n t a [ i ] . s t a r t ( );
do
[
// M o s t r a r nombre d e l h i l o , p r i o r i d a d y e s t a d o de l a c u e n t a
f o r ( i = 0: i < n C u e n t a s : i + + )
S y s t e m . o u t . p r in t íc u e n t a [ i] .getNameí) +
P ■" + c u e n t a [ i ] . g e t P r i o r i t y ( ) + " " +
c u e n t a [ i] .cuenta + ” " ) :
S y s t e m . o u t . p r i n t í ”\ r " ) :
// ¿ H a y h i l o s v i v o s ?
h a y a H i l o s V i v o s = c u e n t a [ 0 ] . i s A l i v e í );
f o r ( i = 1; i < n C u e n t a s : i + + )
h a y a H i l o s V i v o s = h a y a H i 1o s V i v o s || c u e n t a [ i ] . i s A l i v e í );
// A h o r a e l h i l o d o r m i r á n M i 1 i s e g u n d o s . m i e n t r a s l o s h i l o s
// c o n t a d o r e s s i g u e n s u c u r s o ,
try
í
int nMi 1 i s e g u n d o s = ( i n t ) í l í ) * M a t h . powí 2 . n C u e n t a s ) ) :
654 JA V A : C U R S O D E P R O G R A M A C IÓ N
s i e e p ( n M i 1i s e g u n d o s ) ;
1
catch ( I n t e r r u p t e d E x c e p t i o n e) 1 I
I
while ( h a y a H i 1o s V i v o s ):
La aplicación Test que m uestre los resultados perseguidos puede ser la si
guiente:
SINCRONIZACIÓN DE HILOS
En los ejem plos que hem os visto hasta ahora cada hilo contenía todo lo que nece
sitaba para su ejecución: datos y m étodos. A dem ás, cada uno de ellos se podía
ejecutar sin que interfiriera en la ejecución de cualquier otro hilo que se ejecutara
concurrentem ente con él. E stam os en el caso de h ilos independientes.
Sin em bargo, hay m uchas situaciones en las que dos o m ás hilos ejecutándose
concurrentem ente deben acceder a los m ism os recursos y/o datos. C om o ejem plo,
im agine la situación donde dos hilos acceden al m ism o fichero de datos; un hilo
CA PÍTU LO 15: HILOS 6 5 5
puede escribir en el fichero m ientras el otro sim ultáneam ente lee del m ism o. Es
tam os en el caso de h ilos cooperantes. Este tipo de situación puede crear resulta
dos im predecibles, adem ás de indeseables. En estos casos, sim plem ente se debe
tom ar el control de la situación y asegurar que cada hilo acceda a los recursos de
una m anera previsible, sincronizando las actividades que desarrollan cada uno de
ellos. P ara realizar operaciones de sincronización Java proporciona los siguientes
elem entos de sincronización: secciones críticas, w a it y notify.
En general un hilo se sincroniza con otro hilo poniéndose él m ism o a dorm ir.
N o obstante, antes de ponerse a dorm ir, debe poner en conocim iento del sistem a
qué evento debe ocurrir para reanudar su ejecución. D e esta form a, cuando se
produzca ese evento, el sistem a despertará al hilo perm itiéndole continuar la eje
cución. Por ejem plo, si un hilo padre necesita esp erar hasta que uno o m ás hilos
hijo finalicen, se pone él m ism o a dorm ir hasta que el hilo o hilos hijo pasen al
estado m uerto.
Secciones críticas
S upongam os una aplicación en la que dos hilos de un proceso acceden a una única
m atriz de datos con la intención de registrar los resultados obtenidos durante un
experim ento. El program a podría sim ularse así:
• C ream os un objeto datos que envuelva una m atriz unidim ensional con el pro
pósito de alm acenar los datos adquiridos a través de una tarjeta que actúa co
m o interfaz entre nuestra aplicación y el m edio utilizado para realizar el
experim ento. En nuestro ejem plo, sim ularem os cada uno de los datos adquiri
dos con un valor obtenido a partir de unos sencillos cálculos.
• C ream os uno o m ás hilos para que tom en los datos y los vayan alm acenando
en la m atriz hasta llenarla, instante en el que su ejecución finalizará.
Im plem entem os una clase C D atos para m anipular una m atriz unidim ensional
de tipo d o u b le con n elem entos. D icha clase incluirá los atributos:
y los m étodos:
656 J A V A : C U R S O D E P R O G R A M A C IÓ N
public c la ss COatos
I
// A t r i b u t o s
p r iv a t e d o u b le [] dato:
p r i v a t e i n t i n d = 0:
p u b l i c i n t ta maño:
// M é t o d o s
p u b l i c C D a t o s d n t n)
I
i f ( n < 1 ) n - 10:
t a mañ o = n;
d a t o - new d o u b l e [ n ] ;
Uno o m ás hilos serán los encargados d e adquirir los datos. Q uiere esto decir
que cuando se lancen estos hilos, el constructor de cada uno de ellos debe de reci
b ir com o argum ento el objeto C D atos donde serán alm acenados los datos que se
CA PÍTU LO 15: HILOS 6 5 7
adquirirán, ejecutando el m étodo cálculos del objeto C D atos. La clase de los hilos
aludidos puede ser así:
do
I
i - m .cálculos(getNam e()): // a d q u i r i r d a t o s
I
w h i 1e ( i < m.tamaño);
Para lanzar los hilos que adquirirán los datos, im plem entarem os una aplica
ción com o la siguiente:
public c la ss Test
1
public static void m a in (S trin g [] args)
I
C D a t o s d a t o s = new C D a t o s ( l O ) ;
C A d q u i r i r D a t o s a d q u i r i r D a t o s _ 0 = new C A d q u i r i r D a t o s ( d a t o s ) ;
a d q u i r i r D a t o s _ 0 . s t a r t ( ):
I
1
Thread-0 tomó l a m u e s t r a 8
Thread-0 tomó l a m u e s t r a 9
O bservando los resultados vem os que todo se ha desarrollado norm alm ente.
M odifiquem os la aplicación Test para que ahora utilice dos hilos en lugar de uno,
para adquirir los datos:
C AdquirirDatos a d q u i r i r D a t o s _ 0 = new C A d q u i r i r D a t o s ( d a t o s );
C AdquirirDatos a d q u i r i r D a t o s _ l = new C A d q u i r i r D a t o s ( d a t o s );
a d q u i r i r D a t o s _ 0 . s t a r t ( ):
adqui r i r D a t o s _ l . s t a r t ( ) :
T h r e a d - 0 tomó l a m u e s t r a 0
T h r e a d - 0 tomó l a m u e s t r a 1
T h r e a d - 0 tomó l a m u e s t r a 2
T h r e a d - 1 tomó l a m u e s t r a 0
T h r e a d - 1 tomó l a m u e s t r a 4
T h r e a d - 0 tomó l a m u e s t r a 4
T h r e a d - 1 tomó l a m u e s t r a 5
T h r e a d - 0 tomó l a m u e s t r a 6
T h r e a d - 1 tomó l a m u e s t r a 7
T h r e a d - 0 tomó l a m u e s t r a 9
T h r e a d - 1 tomó l a m u e s t r a 9
j a v a . l a n g . A r r a y l n d e x O u t O f B o u n d s E x c e p t i o n : 10
at. C D a t o s . a s i g n a r ( C D a t o s . j a v a : 2 1 )
at C D a t o s . c é l c u l o s ( C D a t o s . j a v a :29)
a t C A d q u i r i r D a t o s . r u n t C A d q u i r i r D a t o s . j a v a , Com p il e d Code)
índice del elem ento de la m atriz donde está alm acenada). En cam bio, al ejecutarse
los dos hilos concurrentem ente, sí se han dado esos problem as.
1. p u b l i c i n t c á l c u l o s í S t r i n g h i l o )
2. 1
3. i f ( i nd > - t a ma ñ o ) r e t u r n ta maño;
4. d o u b l e x = M a t h . r a n d o m ( );
5. S y s t e m . o u t . .p r i n t l n ( h i 1 o + " m u e s t r a
6. asignartx. i n d ) ;
7. i nd++:
8. r e t u r n ind;
9. 1
L ógicam ente los problem as expuestos aparecen porque dos hilos están acce
diendo a un m ism o objeto de datos sin ningún sincronism o. Por lo tanto, la forma
de ev itar los problem as planteados es que cuando un hilo esté accediendo a ese
objeto de datos, no pueda hacerlo el otro y viceversa. E sta sección de código que
en un instante determ inado tiene que acceder exclusivam ente a un objeto de datos
com partido, recibe el nom bre de sección crítica.
En Java, cada “objeto” tiene un m onitor (tam bién llam ado cerrojo — lock). En un
instante determ inado, ese m onitor es controlado, com o m ucho, por un solo hilo.
El m onitor controla el acceso al código sincronizado del objeto; en o tras palabras,
a la sección crítica.
Y ¿cóm o se crea una sección crítica? La form a m ás sencilla de crear una sec
ción crítica es agrupando el código definido com o crítico en un m étodo declarado
s y n c h ro n iz e d (sincronizado).
Si ahora ejecuta de nuevo la aplicación Test com probará que todo funciona
com o esperábam os.
Un hilo que quiera ejecutar el código sincronizado de un objeto debe prim ero
intentar adquirir el control del m onitor d e ese objeto. Si el m onitor está disponi
ble, esto es, si no está controlado p o r o tro hilo, entonces lo adquirirá y ejecutará el
código sincronizado y cuando finalice liberará el m onitor. E n cam bio, si el m oni
tor está controlado p o r otro hilo, entonces el hilo que lo intentó se bloqueará y
sólo reto m ará al estado preparado cuando el m onitor esté disponible.
e n tra r e n el código
s incron izad o
E n eje cu c ió n
m o n ito r n o o b te n id o
U na sección crítica puede ser tam bién un bloque de código que se ejecuta so
bre un determ inado objeto. En este caso, la sección crítica se delim ita así:
synchronized ( objeto)
I
II C ó d i g o que s e e j e c u t a sobre objeto
I
CA PÍTU LO 15: HILOS 6 6 1
Si aplicam os esta segunda técnica sobre el ejem plo anterior, podem os elim i
nar el m étodo cálculos de la clase C D atos y reescribir el m étodo r u n de la clase
C AdquirirD atos así:
p u b l i c C A d q u i r i r D a t o s ( CDatos mdatos) // c o n s t r u c t o r
I
m = mdatos:
synchronized (m)
1
i f ( m . i n d > - m.tamaño) r e t u r n :
x - M a t h . r a n d o m t );
S y s t e m . o u t . p r i n t l n ( g e t N a m e ( ) + " tomó l a m u e s t r a " + m.ind):
m .a sig n a ríx , m.ind):
m.ind++:
O bserve que el código anterior exige que el atributo in d de C D atos sea públi
co. En la versión anterior era privado.
synchronized (m)
1
do
(
i f ( m .i n d >= m.tamaño) r e t u r n :
x - M a t h . r a n d o m t );
S y s t e m . o u t . p r i n t l n ( g e t N a m e í ) + " tomó l a m u e s t r a " + m.ind):
m .a sign a r(x , m.ind):
662 JA V A : C U R S O D E P R O G R A M A C IÓ N
m. i n d + + ;
I
while (m .ind < m.tamaño):
I
M onitor reentrante
public c la ss CDatos
I
II ...
La m áquina Java perm ite a un hilo volver a tom ar el control de un m onitor del
que ya lo tiene, porque los m onitores Java son reentrantes. Esto sólo funcionará
en sistem as que soporten m onitores reentrantes.
H em os visto que las secciones críticas son m uy fáciles de utilizar, pero sólo se
pueden em plear para sincronizar hilos involucrados en una única tarea; en el
ejem plo anterior la tarea era única: alm acenar datos en una m atriz. L os m étodos
w a it y n o tify proporcionan una alternativa m ás para com partir un objeto, pero
con la diferencia de que perm iten sincronizar hilos involucrados en tareas distin
tas, una dependiente de la otra. Piense, por ejem plo, en un sistem a que cada vez
que genera un m ensaje lo encapsula en un objeto C M ensaje con el fin de m ani
pularlo. En este caso, las tareas involucradas sobre el objeto C M ensaje son: una,
alm acenar el m ensaje generado y otra, obtener el m ensaje alm acenado para m os
trarlo. C laram ente se ve que una tarea depende de la otra; evidentem ente, un m en
saje no puede ser m ostrado si antes no se ha producido.
public ProductoríCMensaje c) // c o n s t r u c t o r
I
m e n s a j e = c:
númeroMsj = ( i n t ) ( M a t h . r a n d o m í) * 100);
m e n s a j e . a l m a c e n a r í n ú m e r o M s j ) : // a l m a c e n a e l m e n s a j e
System .out.printlní"Productor " + getNameí) +
" almacena el mensaje # “ + númeroMsj);
try
I
i n t msegs = ( i n t ) ( M a t h . r a n d o m í ) * 100):
// P o n e r a d o r m i r e l h i l o h a s t a que s e p r o d u z c a el
// s i g u i e n t e m e n s a j e ,
sleep ím segs);
I
catch ( In t e rru p t e d E x c e p t io n e) I )
p u b lic ConsumidoríCMensaje c) // c o n s t r u c t o r
I
m en sa j e = c:
w hile (true)
I
msj = m e n s a j e . o b t e n e r í ) : // o b t i e n e e l ú l t i m o m e n s a j e
S y s t e m . o u t .p r in t ln ( "C o n s u m id o r ” + getNameí) +
“ obtuvo: " + m s j ):
U na aplicación que lance los hilos productor y consum idor y m uestre los re
sultados que producen puede ser la siguiente:
public c la ss Test
666 J A V A : C U R S O D E P R O G R A M A C IÓ N
public static v o i d ma i n ( S t r i n g [ D a r g s )
I
C M e n s a j e m e n s a j e = new C M e n s a j e O :
P r o d u c t o r p r o d u c t o r l = new P r o d u c t o r ( m e n s a j e ) :
C o n s u m i d o r c o n s u m i d o r l = new C o n s u m i d o r ( m e n s a j e ) ;
p r o d u c t o r l . s t a r t ( );
c o n s u m i d o r l . s t a r t ( ):
C uando ejecute la aplicación anterior, tenga presente que los hilos productor y
consum idor trabajarán indefinidam ente. P or lo tanto, para detener la ejecución
tendrá que pu lsar las teclas C trl+C . E n lugar de esto, podríam os haber utilizado la
técnica m ostrada en el apartado “ F inalizar un hilo” . N o lo hem os hecho para no
com plicar el código y centrarnos en el tem a de sincronización. U na v ez que haya
ejecutado la aplicación, observará resultados análogos a los siguientes:
entrar en el código - í E s p e ra n d o 1. J R ln n u o a d n 1
sincronizado 1
E n e je c u c ió n j
| móñitoTr^^ notify/notifyAII
tiempo excedido
interrupt
CA PITU LO 15: HILOS 6 6 7
v o i d w a i t ( O ? / isegu nd os í . n a n o s e g u n d o s ] ] )
Precisam ente, una de las diferencias entre sleep y w ait es que el prim ero,
cuando es llam ado, no cede el control del m onitor, m ientras que el segundo sí.
El m étodo n o tify A Il, a diferencia de notify, despierta todos los hilos que es
tán esperando por el m onitor que controla el acceso al código sincronizado de un
objeto. Igualm ente, los hilos despertados com petirán de la m anera habitual por
adquirir la UCP con el resto de los hilos que estén en el estado preparado.
Evidentem ente, el m étodo notify es m ás rápido que n o tify A Il, pero su form a
de proceder nos puede conducir a situaciones no deseadas cuando hay varios hilos
esperando en el m ism o objeto. En este caso, rara vez se utiliza, y p o r seguridad se
sugiere utilizar n o tify A Il.
II ...
I
A hora, cuando el hilo productor adquiera el control del m onitor del objeto
C M ensaje y ejecute el m étodo sincronizado a lm acenar, lo prim ero que hará será
interrogar el atributo disponible. Si su valor es tru e , el hilo se pondrá a dorm ir
hasta que se m uestre el últim o m ensaje producido y cede el control del m onitor, y
si vale false, alm acena el nuevo m ensaje, cam bia el atributo disponible a tru e , e
invoca a notifyA U para despertar a todos los hilos que estén esperando p o r este
monitor.
A sim ism o, cuando el hilo consum idor adquiera el control del m onitor del ob
je to C M ensaje y ejecute el m étodo sincronizado obtener, lo prim ero que hará será
interrogar el atributo disponible. Si su valor es false, el hilo esperará hasta que
haya un m ensaje cediendo el control del m onitor, y si su valor es tru e , cam bia el
atributo disponible a false, invoca a notifyA U para despertar a todos los hilos que
estén esperando por este m onitor y retom a el m ensaje.
CA PÍTU LO 15: H ILOS 6 6 9
public c la ss Test
I
public static void m a in (S trin g [] args)
(
C M e n s a j e m e n s a j e = new C M e n s a j e O :
P r o d u c t o r p r o d u c t o r l = new P r o d u c t o r ( m e n s a j e );
C o n s u m i d o r c o n s u m i d o r ] = new C o n s u m i d o r ( m e n s a j e ) :
C o n s u m i d o r c o n s u m i d o r 2 = new C o n s u m i d o r ( m e n s a j e ) :
670 JA V A : C U R S O D E P R O G R A M A C IÓ N
p r o d u c t o r l , s t a r t ( );
c o n s u m i d o r l . s t a r t ( ):
c o n s u m i d o r 2 . s t a r t ( );
A continuación edite los m étodos alm acenar y obtener, y cam bie las senten
cias w hile p o r if:
Productor T h r e a d - 0 a l m a c e n a : m e n s a j e #14
Consumidor Thread-1 obtuvo: m e n s a j e #14
Consumidor Thread-2 obtuvo: m e n s a j e #14
Interbloqueo
A nteriorm ente dijim os que la inanición (starvation) ocurre cuando un hilo se que
da com plem ente bloqueado y no puede progresar porque no puede acceder a los
CA PÍTU LO 15: H ILOS 6 7 1
recursos q ue necesita; si esto ocurre entre dos o m ás hilos porque esperan por una
condición recíproca que nunca puede ser satisfecha, estam os en un caso de inter-
bloqueo (deadlock: algunos autores prefieren denom inarlo abrazo m ortal). Por
ejem plo dos hilos necesitan im prim ir un docum ento alm acenado en el disco, para
lo que necesitan los recursos disco e im presora. Puesto que los hilos se están eje
cutando paralelam ente, suponga que uno ya ha adquirido el disco y el otro la im
presora. E sto significa que am bos hilos quedarán bloqueados, cada uno de ellos
esperando por el recurso que tiene el otro.
GRUPO DE HILOS
C ada hilo Jav a es un m iem bro de un grupo de hilos. E ste grupo puede ser el pre
definido por Java o uno especificado explícitam ente. Los grupos de hilos propor
cionan un m ecanism o p ara agrupar varios hilos en un único objeto con el fin de
poder m anipularlos todos de una vez; p o r ejem plo, poder interrum pir un grupo de
hilos invocando una sola vez al m étodo in te r r u p t. A su vez, un grupo de hilos
tam bién puede pertenecer a otro grupo, form ando una estructura en árbol. Desde
el punto de vista de esta estructura, un hilo sólo tiene acceso a la inform ación
acerca de su grupo, no a la de su grupo padre o de cualquier otro grupo.
Java proporciona soporte para trabajar con grupos de hilos a través de la clase
T h re a d G ro u p del paquete lang.
Grupo predefinido
C uando cream os un hilo sin especificar su grupo en el constructor. Java lo coloca
en el m ism o grupo (grupo actual) del hilo bajo el cual se crea (hilo actual).
Después, cada hilo consum idor que es creado es añadido al grupo actual que
hem os denom inado consum idores.
672 JA V A : C U R S O D E P R O G R A M A C IÓ N
Finalm ente, m ás adelante, se envía al grupo actual el m ensaje list con el obje
tivo de escrib ir inform ación acerca del grupo de hilos. O tros m étodos puede ver
los en la docum entación proporcionada con el JDK.
public c la ss Test
I
public static void m a in (S trin g [] args)
I
ThreadGroup c o n su m id o re s =
T h r e a d . c u r r e n t T h r e a d ( ) . g e t T h r e a d G r o u p ( );
C M e n s a j e m e n s a j e = new C M e n s a j e ! ):
P r o d u c t o r p r o d u c t o r l = new P r o d u c t o r ( m e n s a j e ) ;
C o n s u m i d o r c o n s u m i d o r l = new C o n s u m i d o r t m e n s a j e , c o n s u m i d o r e s ,
"consumi d o r l " ) ;
C o n s u m i d o r c o n s u m i d o r 2 = new C o n s u m i d o r ( m e n s a j e , c o n s u m i d o r e s ,
" c o n s u m i d o r 2 " );
consumidores . l i s t O ;
II...
1
S iguiendo con el ejem plo anterior, vem os que cuando se invocó al constructor
C onsum idor se pasaron tres argum entos: un objeto C M ensaje, el grupo de hilos y
el nom bre del hilo que se desea añadir al grupo. S egún esto, el constructor de esta
clase será com o se indica a continuación:
Grupo explícito
Para añadir un hilo a un determ inado grupo prim ero crearem os el grupo y después
procederem os de la m ism a form a explicada en el apartado anterior. Por ejem plo,
si en el ejem plo anterior en lugar de utilizar el grupo predefinido p o r Java quisié
ram os definir explícitam ente un grupo referenciado por la variable consum idores
y denom inado tam bién consum idores, la prim era línea del m étodo m a in la susti
tuiríam os por la som breada en el código m ostrado a continuación:
public c la ss Test
I
public static void m a in (S trin g [] args)
(
ThreadGroup c o n su m id o re s - new T h r e a d G r o u p ( " c o n s u m i d o r e s " ):
II...
TUBERIAS
B ásicam ente una tubería es utilizada para canalizar la salida de un hilo (puede ser
el hilo principal de un program a en ejecución) hacia la entrada de otro. De esta
form a los hilos pueden com partir datos sin tener que recurrir a otros elem entos
com o, por ejem plo, ficheros tem porales o m atrices.
1
lp¡i
P ip e d R e a d e r P ip e d W rite r
si se cerrara un extrem o se interrum piría el flujo. Esto m ism o ocurre con los flujos
que denom inam os tuberías.
P i p e d W r i t e r e m i s o r - new P i p e d W r i t e r ( ) ;
P i p e d R e a d e r r e c e p t o r = new P i p e d R e a d e r ( e m i s o r ) ;
o bien:
P i p e d R e a d e r r e c e p t o r = new P i p e d R e a d e r t ):
P i p e d W r i t e r e m i s o r = new P i p e d W r i t e r ( r e c e p t o r ):
Por ejem plo, pensem os en una lista de objetos, relacionados con alum nos que
cursan una determ inada asignatura, que deseam os ordenar para después obtener
una lista de los aprobados. Sin tuberías, el program a tendría que alm acenar los re
sultados entre cada paso en algún lugar, p o r ejem plo, en m atrices:
D ejam os este problem a para que lo resuelva el lector. N osotros vam os a re
solver uno m ás breve que m uestre sim plem ente cóm o se utilizan las tuberías. Un
hilo productor produce m ensajes que pasa a través de una tubería a otro hilo con
sum idor para que los m uestre en pantalla. La figura siguiente resum e lo expuesto:
Lista
de
m e n s a je s
C A P ÍT U L O 15: HILOS 6 7 5
En prim er lugar vam os a m ostrar la aplicación que lanzará los hilos productor
y consum idor:
import j a v a . i o . * ;
p u b lic c l a s s Test
I
public s t a t ic void m a in (S trin g [] args)
I
try
I
P i p e d W r i t e r e m i s o r = new P i p e d W r i t e r ( );
P i p e d R e a d e r r e c e p t o r = new P i p e d R e a d e r ( e m i s o r ) ;
P r o d u c t o r p r o d u c t o r l = new P r o d u c t o r ( e m i s o r ):
C o n s u m i d o r c o n s u m i d o r l = new C o n s u m i d o r ! r e c e p t o r ):
p r o d u c t o r l . s t a r t ( );
c o n s u m i d o r l . s t a r t í ):
I
catch (IOException ignorada) II
import j a v a . i o . * :
pu blic c la s s Productor extends Thread
I
private PipedW riter emisor = n u il ;
676 JA V A : C U R S O D E P R O G R A M A C IÓ N
p u b l i c P r o d u c t o r ( P i p e d W r i t e r em) // c o n s t r u c t o r
I
e m i s o r = em:
f l u j o S = new P r i n t W r i t e r ( e m i s o r );
I
publi c void r u n ( )
I
w hile (true)
(
a l m a c e n a r M e n s a j e t ):
try
I
i n t msegs = ( i n t ) ( M a t h . ra n d o m t ) * 1 0 0 ):
// P o n e r a d o r m i r el h i l o h a s t a q u e s e p r o d u z c a el
// s i g u i e n t e m e n s a j e ,
s l e e p í m s e g s );
I
catch ( InterruptedException e) I I
númeroMsj = ( i n t ) ( M a t h . r a n d o m í ) * 1 0 0 ) :
// S u p o n e r o p e r a c i o n e s p a r a b u s c a r e l m e n s a j e en una t a b l a
// d e m e n s a j e s : r e s u l t a d o :
t e x to M e n s a je = "mensaje # " + númeroMsj:
f l u j o S . p r i n t l n ( t e x t o M e n s a j e ) : // e n v i a r m e n s a j e p o r l a t u b e r í a
System .out.printlní"Productor " + getNamet) +
" almacena: " + t e x t o M e n s a j e ) :
p r o t e c t e d v o i d f i n a l i z e O th rows IO E x c e p t io n
i
i f ( f l u j o S != n u i l ) I f l u j o S . c l o s e ( ): f l u j o S = n u i l ; I
i f ( e m i s o r != n u i l ) I e m i s o r .e l o s e ( ): e m i s o r = n u i l : )
Finalm ente, m ostram os la clase correspondiente al hilo consum idor. E sta cla
se tiene un atributo receptor, que referenciará el extrem o de la tubería desde el
cual el hilo consum idor obtendrá los m ensajes. Este atributo será establecido por
el constructor de la clase.
C A PÍTU LO 15: HILOS 6 7 7
P ara obtener los m ensajes, el m étodo ru n del hilo consum idor crea un flujo
(flu jo E ) de la clase B u ffe re d R e a d e r desde el extrem o receptor. Este flujo perm i
tirá utilizar el m étodo re a d L in e , que P ip e d R e a d e r no tiene, para obtener los
m ensajes enviados por el productor. C uando no haya ningún m ensaje, sim ple
m ente el hilo que ejecuta el m étodo re a d L in e queda bloqueado.
import j a v a . i o . * ;
p u b li c c l a s s Consumidor extends Thread
receptor - nuil
p riv a t e BufferedReader flu jo E = n u il :
S t r i n g msj = n u l 1 :
try
e ( ) ; // o b t e n e r m e n s a j e d e l a tubería
S y s t e m .o u t .p r in t ln ("C o n s u m id o r " + getNameO +
" obtuvo: " + m s j ):
EJERCICIOS RESUELTOS
1. R ealizar una aplicación que utilice dos hilos, un productor y un consum idor, tra
bajando sobre una única m atriz de enteros positivos. E sto es, un hilo productor
generará enteros que alm acenará en una m atriz circular y un hilo consum idor ob
tendrá de esa m atriz los enteros generados por el productor. M uchas aplicaciones
de la vida ordinaria reproducen este problem a. U n ejem plo es el adm inistrador de
im presión en un servidor de red; los productores son los usuarios de la red y el
consum idor la im presora o im presoras.
quear la m atriz sólo m ientras se esté insertando un d ato y el consum idor lo hará
sólo m ientras se esté extrayendo. C uando la m atriz esté vacía, el hilo consum idor
se pondrá a esperar hasta que haya datos. A sim ism o, cuando la m atriz esté llena,
el hilo productor se pondrá a esperar hasta que haya elem entos libres.
La m atriz que alm acenará los datos será un objeto de la clase C M atriz. Esta
clase estará form ada p o r los atributos:
m M atriz de n enteros positivos.
indP rod índice del elem ento donde el productor debe insertar el si
guiente elem ento. Su valor será: 0, I, 2, ..., n-1, 0, 1, 2 ,...
indC ons índice del elem ento donde el consum idor debe obtener el si
guiente elem ento. Su valor será: 0, 1, 2, ..., n-1, 0, 1, 2 ,...
elem entosV acíos N úm ero de elem entos vacíos en un instante determ inado.
elem entosLlenos N úm ero de elem entos llenos en un instante determ inado.
y p o r los m étodos:
alm acenar A lm acena un dato en el siguiente elem ento vacío.
obtener O btiene el siguiente dato aún no extraído.
//////////////////////////////////////////////////////////////////
// S i n c r o n i z a c i ó n de h i l o s : wait y n o tify .
//
publi c c la s s CMatri z
I
private i n t [ ] m;
private i n t i n d P r o d = 0 ; // I n d i c e p r o d u c t o r
private i n t i n d C o n s = 0 : // I n d i c e c o n s u m i d o r
private in t elem entosVacios, elementosLlenos:
catch ( In te r r u p te d E x c e p tio n e) I I
I
e l e m e n t o s V a c í o s - •;
elementosLle nos-H-;
S y s t e m . o u t . p r i n t ( ” vac1 o s : " + e l e m e n t o s V a c í o s + " , llenos: " +
elementosLlenos + " \r"):
m [ i n d P r o d ] - num;
i n d P r o d - ( i n d P r o d + 1) % m . l e n g t h ;
II D e s p e r t a r h i l o s ;
noti f y A l 1();
//////////////////////////////////////////////////////////////////
En el problem a del productor y del consum idor los recursos que estos hilos
deben adquirir para poder ejecutarse son los elem entos vacíos y los elem entos lle
nos de la m atriz, respectivam ente. C ada uno de estos tipos de recursos los repre
sentarem os por sendas variables que actuarán com o sem áforos: elem entosLlenos y
e lem en tos Vacíos. Un valor cero equivale a sem áforo en rojo y un v alor distinto de
cero a sem áforo en verde.
elem entosLlenos es un sem áforo inicialm ente en rojo para el consum idor
(porque no hay ningún elem ento lleno, esto es, no se puede obtener) que repre
senta los elem entos actualm ente llenos de la m atriz y elem entosV acíos es un se
m áforo inicialm ente en verde para el productor (porque todos los elem entos están
vacíos, esto es, se puede alm acenar) que representa los elem entos actualm ente
CA PÍTU LO 15: H ILOS 6 8 1
II e l e m e n t o s V a c í o s e s e l s e m á f o r o p a r a e l productor
w h i l e '( e l e m e n t o s V a c í o s = = 0)
I
try
1
wa i t ( ) ; // e l hilo s e po ne a d o r m i r y c e d e e l monitor
I
catch ( InterruptedException e) I )
)
ele m ento sV ac í o s - -:
el ementosLlenos-H-;
m f i n d P r o d ] = num:
i n d P r o d - ( i n d P r o d + 1) % m.length;
noti f y A l1();
Según los expuesto, el hilo productor básicam ente se lim itará a llam ar al m é
todo a lm acenar de la clase C M atriz. Esto es:
//////////////////////////////////////////////////////////////////
II S i n c r o n i z a c i ó n de h i l o s . H ilo productor.
//
public class P ro d u c to r exten ds Thread
I
private CMatriz m atriz;
private boolean c o n t in u a r = tru e :
public P r o d u c t o r ( C M a t r i z m) // c o n s t r u c t o r
682 JA V A : C U R S O D E P R O G R A M A C IÓ N
m atriz - m:
publi c voi d r u n ( )
I
int nú me r o : // nú me r o p r o d u c i d o
while (continuar)
(
n ú me r o - ( i n t ) ( M a t h . r a n d o m í ) * 1 0 0 ) :
m a t r i z . a l m a c e n a r ( n ú m e r o ) : // a l m a c e n a e l nú mero
//System .out.printlní"Productor " + getNameO +
// " a l m a c e n a : nú mero " + n ú m e r o ) :
//////////////////////////////////////////////////////////////////
A nálogam ente, el hilo consum idor básicam ente se lim itará a llam ar al m étodo
o btener de la clase C M atriz. Esto es:
//////////////////////////////////////////////////////////////////
// S i n c r o n i z a c i ó n de h i l o s . H ilo consumidor.
//
public class Consumidor extends Thread
(
p riva te CMatriz m atriz:
p r iv a t e boolean c o n tin u a r = true:
public C o n s u m i d o r ( C M a t r i z m) // c o n s t r u c t o r
I
m a t r i z = m;
P ara probar el com portam iento de am bos hilos puede servir la aplicación si
guiente:
import j a v a . i o . * :
//////////////////////////////////////////////////////////////////
// S i n c r o n i z a c i ó n de h i l o s .
//
public class Test
I
public static void m a in ( S t r in g [ ] args)
(
C M a t r i z m a t r i z = new C M a t r i z ( l O ) :
P r o d u c t o r p r o d u c t o r l = new P r o d u c t o r t m a t r i z ) ;
C o n s u m i d o r c o n s u m i d o r l = new C o n s u m i d o r í m a t r i z ):
I n p u t S t r e a m R e a d e r i s - new I n p u t S t r e a m R e a d e r í S y s t e m . i n ) ;
B u f f e r e d R e a d e r b r = new B u f f e r e d R e a d e r í i s );
try
1
b r . r e a d L i n e í ) : // e j e c u c i ó n d e t e n i d a hasta pulsar [Entrar]
// I n i c i a r l a e j e c u c i ó n d e l o s h i l o s
p r o d u c t o r l . s t a r t t ):
c o n s u m i d o r l . s t a r t { );
b r . r e a d L i n e ( ) : // e j e c u c i ó n d e t e n i d a hasta pulsar [Entrar]
I
c a t c h ( l O E x c e p t i o n e) I)
// P e r m i t i r a l o s h i l o s f i n a l i z a r
p r o d u c t o r l . t e r m i n a r ( );
consumi d o r l . t e r m i n a r ! );
//////////////////////////////////////////////////////////////////
EJERCICIOS PROPUESTOS
1. E scribir una aplicación que lance tres hilos que ordenen otras tantas m atrices,
todas de la m ism a dim ensión, utilizando, el prim ero el m étodo de ordenación de
la burbuja, el segundo el de inserción y el tercero el m étodo quicksort. V isualizar
684 JA VA: C U R SO D E PRO G R A M A CIÓ N
2. S upongam os una lista de objetos, relacionados con alum nos que cursan una d e
term inada asignatura, que deseam os ordenar para después obtener una lista de los
aprobados. U tilizando tuberías, podem os plantear la solución del problem a según
m uestra la figura siguiente:
C ada o bjeto alum no alm acenará inform ación relativa al nom bre del alum no, al
nom bre de la asignatura y a la nota.
Para obtener el resultado solicitado, los pasos a seguir básicam ente pueden ser los
siguientes:
1. C rear el fichero con la inform ación de los alum nos ordenada por el nom bre
del alum no.
2. A brir un flujo desde el fichero que perm ita leer la inform ación del m ism o.
3. Invocar a un m étodo o rdenar que reciba com o parám etro el flujo abierto en el
punto 2 y devuelva una referencia a un objeto P ip e d ln p u tS tre a m (o a su su-
perclase), que se corresponda con el extrem o de una tubería en la que un hilo
lanzado por este m étodo coloque los alum nos clasificados por la nota. Para
realizar la ordenación, el hilo cargará la inform ación en una m atriz, la ordena
rá y después la volcará en la tubería.
4. Invocar a un m étodo aprobados que reciba com o argum ento el flujo de datos
resultante del punto 3 y devuelva una referencia a un objeto P ip e d ln p u tS
tre a m (o a su superclase), que se corresponda con el extrem o de una tubería
en la que o tro hilo lanzado p o r este m étodo coloque los alum nos aprobados.
ÍNDICE
añadir un com ponente a un panel, 730
A añadir un elem ento a una m atriz, 297
abstracción. 32 aplicación, 11; 63; 264
abstract. 330 append, 187
A ccesibilidad. 723 applet, 8; 11; 707, 705
acceso aleatorio, 457 crear, 705
acceso secuencial, 429; 479 parám etros, 711
accesos directos. 699 A PPLET. etiqueta. 705
A ckerman, 586 appletview er, 10
acos, 115 árbol. 542
A ctionEvent, 729 binario, 543
A ctionListener, 727 binario de búsqueda, 546
actionPerform ed, 728 binario perfectam ente equilibrado, 558
add, 730 recorrer, 544
addA ctionListener, 727 archivo, 420
addA djustm entListener, 727 argum entos, 73
addFocusListener, 728 pasar, 219
addltem Listener, 728 argum entos en la línea d e órdenes, 221
addK eyListener, 728 A rithm eticException, 148
addM ouseListener, 728 arrastrar y colocar, 723
addM ouseM otionListener, 728 arraycopy, 218
addW indow Listener, 728 A rraylndexO utO fB oundsException, 168
A djustm entListener, 727 A rrays, 233
adm inistrador de interfaz de usuario, 731 ASCII, 765
adm inistradores de diseño, 730 asignación d e objetos, 274
asignar, 731 asignar un adm inistrador de diseño, 731
algoritm o B oyer y M oore, 602 asin, 1 15
algoritm o d e planificación determ inista, 649 ASP. 704
algoritm os hash, 616 atan. 115
ám bito de una variable. 85 atan2. 115
anidar if, 124 atóm icas, operaciones, 660
anidar w hile. do, o for, 136 atrapar la excepción, 403
anim ación, 718 atributos, 26; 255
ANSI. 763 con el m ism o nom bre, 341
770 JA V A: C U R S O DE PROGRAM A CIÓN
M static, 291
métodos, 27; 256
m ail, 692 de una subclase, 340
m ain, 13; 29; 73; 79 en línea, 371
argum entos, 222 mezcla natural, 606
m anejadores de eventos. 727 m iembro de una clase, 30
M ap, 566 m iem bros del objeto, 77
m áquina Java. 649 miembros heredados, 336
máquina virtual. 6 m iem bros que son punteros, 279
m arcos en páginas HTM L, 702 m ilisegundos transcurridos desde el I de enero
mark, 613 de 1970.242
M ath, 114 m in. 115
matrices, 164 M IN JV A LU E. 104
de objetos, 294 m kdir. 436
métodos. 168 m ódem . 692
verificar si son iguales, 234 m odificador, 72
matriz m odificadores de acceso, 257
acceder a un elem ento, 167 monitor, 659; 667
asignar un valor a todos sus elem entos, 234 m onitores reentrantes, 663
asociativa, 172 M ouscListener, 728
buscar un valor. 233 M ouseM otionListener, 728
com o valor retom ado, 217
crear, 166
de cadenas de caracteres. 196
N
de longitud 0. 297 NaN. 104; 206
de objetos String. 203 N E G A T IV E JN E IN IT Y . 104
declarar. 165 new, 73; 74; 79
es un objeto. 166 new A udioClip, 717
multidim ensional. 191 ncw Linc, 613
num érica multidim ensional, 192 new s, 693
ordenar, 235 nivel de protección predeterm inado, 77
pasar com o argum ento. 215 nodo de un árbol, 544
sparse, 213 notify. 663; 667
max, 115 notifyA ll, 667
M AX_VALUE, 104 nuil, 43; 111; 203; 498
m em oria para objetos String. 238 NumberFormat, 226
memoria, asignar y liberar. 74 NumberForm atException, 106
m ensaje, 24 núm ero racional, 306
mensajes, 75 núm eros aleatorios, 240
m ensajes en la barra de estado, 718
M essageForm at. 233
Method. 97 O
método. 24; 28; 72 O bject. 92; 97; 168; 235; 287; 509; 758
abreviado, 263 O bjcctlnputStream , 451
abstracto. 330
O bjectO utputStream , 450
consulta dinám ica, 371 objeto, 24
de inserción. 595 aplicación. 63
de la burbuja. 592 String, crear, 238
d e la clase. 78 tem poral, 309
de quicksort. 596 objetos, guardar/leer en/de un fichero. 449
final, 268 ocultación de datos, 257
recursivo, 224
sobrecargado. 262
776 JA V A : C U R S O D E P R O G R A M A C IÓ N
operadores, 52 P O S IT IV E JN F IN IT Y , 104
a nivel de bits, 55 PO ST. 740
aritm éticos, 52 postordcn, 544
condicional, 57 pow. 115
de asignación, 56 predeterm inado, 258
de relación. 53 preem ptive, 649
instanceof, 530 preorden, 544
lógicos, 54 preparado. 636
new. 73; 74; 79 print, 101
tem ario, 57 println, 13; 26; 101
unitarios, 55 PrintStream , 26; 100
ordenación. 591 PrintW riter, 102
ordenar un fichero. 605 prioridad de un hilo, 651
utilizando acceso aleatorio, 614 private. 258
out, 13; 26; 96 proceso, 633
OutO ÍM em oryError. 74; 413 proceso ligero, 635
OutputStream , 94 productor-consum idor, 678
program a. 4; 634
program ación orientada a objetos. 23
P protección de una clase, 68
package, 305 protected, 258
página dinám ica. 703 protocolo, 689
páginas de transferencia de ficheros, 693
ASP, 704 proyecto, 18
JSP, 737 public, 68; 258
web, 695 public. clase, 32
paint, 708 pública. 67
palabras clave. 47 PushbackR eader. 425
panel de contenido, 730
panel raíz. 730
Q
paquete, 67; 303
awt, 710 quicksort, 596
crear, 304
java.io, 421
java.text, 226
R
java.útil, 228 racional, 306
protección de, 68 raíz de un árbol, 544
parám etros, 73 random , 115; 241; 242
de un applet, 711 R andom A ccessFile, 458
pasados por referencia, 83; 215 read. 93; 113; 197; 425
pasados por valor, 83; 215 Reader, 93
parselnt, 104 readLine. 99; 111; 203
pasar argum entos, 82 readUTF, 463
PI. 115 ready. 425; 458
pila, 527 recolector de basura, 75; 279
planificación, 649 recorrer un árbol, 544
planificador, 637 recursión. 585
planificador de hilos, 641 recursividad, 224
plantillas, 509 recursos, 715
play, 716 redefinir miem bros de la superclase. 343
polimorfismo, 34; 360 reentrantes, m onitores, 663
POO, 23 referencia. 79
POP 2 y 3, 691
A P É N D IC E F: ÍN D IC E 7 7 7
C om o veis faltan páginas, si, falta la p a ite de los applets y dem ás (paite 3 del libro).
E scan ear está m al, es ilegal, p ero tam b ién lo es tener W indows sin com prarlo
U tiliza linux, y verás lo que verdaderam ente se pu d e h a c er con u n sistem a operativo.