Está en la página 1de 704

RESUMEN DEL CONTENIDO

PA R TE 1. PRO G R A M A C IÓ N BÁ SIC A .................................... 1


C A P ÍT U L O 1. FA SES EN EL D E SA R R O L L O DE UN P R O G R A M A .................... 3

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 5. C L A SES DE USO C O M Ú N ...................................................................... 89

C A P ÍT U L O 6. SE N TEN C IA S DE 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

PARTE 2. PRO G R A M A C IÓ N A V AN ZA DA ............................251


C A P ÍT U L O 9. C LA SES Y P A Q U E T E S ............................................................................ 253

C A P ÍT U L O 10. S U B C LA SES E IN T E R F A C E S ............................................................ 329

C A P ÍT U L O 11. E X C E P C IO N E S ......................................................................................... 397

C A PÍTU L O 12. TR A B A JA R C O N F IC H E R O S ............................................................. 419

C A PÍTU L O 13. EST R U C T U R A S D IN Á M IC A S ............................................................ 495


V I I I JA V A: CU R SO DE PROGRAM A CIÓN

C A PÍTU L O 14. A L G O R IT M O S .......................................................................................... 585

C A P ÍT U L O 15. H IL O S ............................................................................................................ 633

PARTE 3. PRO G RA M A S PARA IN T E R N E T ......................... 685


C A PÍTU LO 16. A PA R TIR DE A Q U Í ............................................................................... 687

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

C. PLA TA FO 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 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

PA R TE I. PRO G RA M A CIÓ N B Á SIC A ................................... 1

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

M EC A N ISM O S B Á SIC O S DE LA P O O ..................................................................... 24


O b je to s............................................................................................................................... 24
M ensajes............................................................................................................................ 24
M éto d o s............................................................................................................................. 24
C la s e s ................................................................................................................................. 25
C Ó M O C REA R U N A C L A S E DE O B JE T O S ........................................................... 26
C A R A C T E R ÍST IC A S DE LA P O O ............................................................................... 32
A bstracción....................................................................................................................... 32
E n cap su lam ien to ............................................................................................................. 33
H e re n c ia ............................................................................................................................ 33
P o lim orfism o.................................................................................................................... 34
C O N ST R U C TO R ES Y D E S T R U C T O R E S................................................................. 34
E JE R C IC IO S R E S U E L T O S ............................................................................................. 34
E JE R C IC IO S P R O PU E ST O S.......................................................................................... 36

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

PRESEN TA CIÓ N DE LA SIN TA X IS DE JA V A ..................................................... 37


C A R A C TER E S D E JA V A ................................................................................................ 38
Letras, dígitos y o tro s .................................................................................................... 38
E spacios en b la n c o ......................................................................................................... 38
C aracteres especiales y signos de p u n tu a c ió n ........................................................ 39
S ecuencias de esc a p e ..................................................................................................... 39
T IPO S DE D A T O S .............................................................................................................. 40
Tipos p rim itiv o s.............................................................................................................. 40
b y te................................................................................................................................ 41
sh o rt.............................................................................................................................. 41
i n t .................................................................................................................................. 41
lo n g .............................................................................................................................. 41
c h a r................................................................................................................................ 42
flo a t.............................................................................................................................. 42
d o u b le........................................................................................................................... 43
b o o lean ........................................................................................................................ 43
T ip o s re fere n ciad o s........................................................................................................ 43
L IT E R A L E S ........................................................................................................................... 43
L iterales e n te ro s .............................................................................................................. 44
Literales reales................................................................................................................. 44
Literales de un solo ca rác te r........................................................................................ 45
L iterales de cadenas de c a rac te res............................................................................. 45
ID E N T IFIC A D O R E S.......................................................................................................... 46
C O N T E N ID O X I

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

SEN TEN C IA i f ...................................................................................................................... 121


ANUDAMIENTO D E SEN TEN C IA S if .......................................................................... 124
E STR U C T U R A else i f ......................................................................................................... 126
SEN TEN C IA s w itc h ............................................................................................................. 129
SEN TEN C IA w h ile ............................................................................................................... 133
Bucles an id ad o s................................................................................................................. 136
SEN TEN C IA d o ... w h ile ................................................................................................... 139
SEN TEN C IA f o r .................................................................................................................... 142
SEN TEN C IA b re a k ............................................................................................................... 146
SEN TEN C IA c o n tin u é ......................................................................................................... 146
E T IQ U E T A S ........................................................................................................................... 147
SEN TEN C IA S try ... c a tc h .................................................................................................. 148
EJER C IC IO S R E S U E L T O S ............................................................................................... 149
EJER C IC IO S P R O P U E S T O S ............................................................................................ 159
CONTENIDO X I I I

C A P ÍT U L O 7. M A T R IC E S .................................................................................................. 163

IN T R O D U C C IÓ N A LAS M A T R IC E S ....................................................................... 164


M A TR IC ES N U M ÉR IC A S U N ID IM E N S IO N A L E S .............................................. 165
D eclarar una m a triz ....................................................................................................... 165
C rear una m a triz ............................................................................................................. 166
Iniciar una m a triz ........................................................................................................... 167
A cceder a los elem entos de una m atriz..................................................................... 167
M étodos de una m a triz .................................................................................................. 168
T rabajar con m atrices u n id im e n sio n a les................................................................. 169
M atrices aso c ia tiv a s....................................................................................................... 172
C A D EN A S DE C A R A C T E R E S ...................................................................................... 175
L eer y escribir una cadena de c a ra c te re s................................................................. 176
T rabajar con cadenas de c a ra c te re s ........................................................................... 178
C lase S trin g ...................................................................................................................... 181
S tring(String v a lo r).................................................................................................. 181
String to S trin g O ........................................................................................................ 182
String concat(S tring s t r ) ......................................................................................... 182
int com pareT o(String o tro S trin g )........................................................................ 182
int lengthQ ................................................................................................................... 184
• String to L o w erC ase().............................................................................................. 184
String toU pperC aseO ............................................................................................... 184
String trim O ................................................................................................................ 184
boolean startsW ith(String p re fijo )....................................................................... 184
boolean endsW ith(String s u fijo ).......................................................................... 184
String substring(int Indicelnicial. int In d ice F in a l)......................................... 185
ch ar charA t(int ín d ic e )............................................................................................ 185
int indexO ftint c a r)................................................................................................... 185
int indexO fiS tring s tr ) ............................................................................................. 185
String replace(char car, char n u ev o C ar)............................................................ 185
static S tring valueO fitipo d a to )............................................................................ 186
char[] to C h arA rray O ............................................................................................... 186
byte(] getB ytes()........................................................................................................ 186
C lase S trin g B u ffcr.......................................................................................................... 186
S trin g B u ffer([arg ])................................................................................................... 186
int lengthO .................................................................................................................. 187
int capacityO ............................................................................................................... 187
StringB uffer append(tipo x ) .................................................................................. 187
StringB uffer insert(int índice, tipo x ) ................................................................. 187
StringB uffer delete(int p l , int p 2 ) ....................................................................... 188
StringB uffer replace(int p l, int p2, String s t r ) ................................................. 188
StringB uffer re v e rse Q ............................................................................................. 188
String substring(int Indicelnicial, int In d ice F in a l)......................................... 189
X IV JA V A: C U R SO DE PROGRAM A CIÓN

ch ar charA t(int ín d ic e )............................................................................................ 189


void setC harA t(int índice, char c a r ) ................................................................... 189
String to S trin g O ....................................................................................................... 189
Clase S trin g T o k en izer................................................................................................... 189
C onversión de cadenas de caracteres a datos n u m érico s.................................... 191
M A TR IC ES DE R EFE R EN C IA S A O B JE T O S ........................................................ 191
M atrices num éricas m ultidim ensionales................................................................... 192
M atrices de cadenas de caracteres............................................................................. 196
M atrices de objetos S trin g ........................................................................................... 203
E JE R C IC IO S R E S U E L T O S ............................................................................................. 205
E JE R C IC IO S P R O P U E S T O S .......................................................................................... 210

C A P ÍT U L O 8. M É T O D O S ................................................................................................... 215

PA SA R UNA M A TR IZ C O M O A R G U M E N T O A UN M É T O D O ..................... 215


M A T R IZ C O M O V A LO R R ET O R N A D O PO R U N M É T O D O ......................... 217
R EFE R E N C IA A UN TIPO P R IM IT IV O .................................................................... 219
A R G U M EN TO S EN LA LÍN EA DE Ó R D E N E S ..................................................... 221
M ÉTO D O S R E C U R S IV O S .............................................................................................. 224
V ISU A LIZA R DATO S C O N F O R M A T O .................................................................. 225
D ar form ato a n ú m eros.................................................................................................. 226
L o calid ad ..................................................................................................................... 227
A lin eac ió n .................................................................................................................. 228
C lase para form atos n u m é ric o s............................................................................ 229
D ar form ato a fechas/horas.......................................................................................... 231
D ar form ato a m en sajes................................................................................................ 233
LA C L A SE A rra y s............................................................................................................... 233
binaryS earch.................................................................. 233
equals.................................................................................. 234
fill........................................................................................................................................ 234
s o rt....................................................................................................................................... 235
LA C L A SE O b je c t............................................................................................................... 235
boolean equals(O bject o b j).......................................................................................... 236
String toStringO ............................................................................................................... 237
void fin alizeO .................................................................................................................. 237
M ÁS S O B R E R E FE R EN C IA S Y O B JE T O S S tr in g ................................................ 238
EJER C IC IO S R E S U E L T O S ............................................................................................. 240
E JER C IC IO S P R O P U E S T O S .......................................................................................... 247
C O N TEN ID O X V

PA R TE 2. PRO G RA M A CIÓ N A V AN ZA DA........................... 251


C A P ÍT U L O 9. C L A S E S Y P A Q U E T E S ......................................................................... 253

D EFIN IC IÓ N DE UN A C L A S E ....................................................................................... 253


A tributos............................................................................................................................ 255
M étodos de una c la s e ...................................................................................................... 256
C ontrol de acceso a los m iem bros de la clase.......................................................... 257
A cceso predeterm inado.......................................................................................... 257
A cceso p ú b lic o ........................................................................................................... 258
A cceso p riv ad o ........................................................................................................... 258
A cceso protegido........................................................................................................ 258
IM PLE M E N TA C IÓ N D E UNA C L A S E ........................................................................ 259
M ÉTO D O S S O B R E C A R G A D O S .................................................................................... 262
IM PL EM E N T A C IÓ N DE UN A A P L IC A C IÓ N ......................................................... 264
C O N T R O L DE A C C ESO A UNA C L A S E ................................................................... 265
R EFE R E N C IA th is................................................................................................................ 266
V A R IA B LE S. M ÉTO D O S Y C LA SES F IN A L E S .................................................... 267
IN IC IA C IÓ N DE UN O B JE T O ........................................................................................ 269
C o n stru cto r......................................................................................................................... 269
S obrecarga del c o n stru c to r............................................................................................ 272
L lam ar a un constructor.................................................................................................. 274
A signación de o b je to s ..................................................................................................... 274
C onstructor c o p ia ............................................................................................................. 275
D E ST R U C C IÓ N DE O B JE T O S ....................................................................................... 276
D estru cto r........................................................................................................................... 277
E jecutar el recolector de basura.................................................................................... 279
R EFE R EN C IA S C O M O M IEM B R O S DE U N A C L A S E ........................................ 279
C O M PA R A R O B JE T O S .................................................................................................... 287
M étodo e q u a ls ................................................................................................................. 287
M IEM B R O S ST A T IC DE UNA C L A S E ...................................................................... 288
A tributos s ta tic .................................................................................................................. 289
A cceder a los atributos s ta tic ........................................................................................ 290
M étodos s ta tic ................................................................................................................... 291
Iniciador e s tá tic o .............................................................................................................. 292
M A TR IC ES D E O B JE T O S................................................................................................. 294
P A Q U E T E S ........................................................................................................................... 303
C rear un paquete............................................................................................................... 304
UN EJEM PLO DE D ISEÑ O DE UN A C L A S E ...................................................... 306
EJER C IC IO S R E S U E L T O S .............................................................................................. 319
E JER C IC IO S P R O P U E S T O S ............................................................................................ 326
X V I JA V A : C U R SO DE PROGRAM A CIÓN

C A P ÍT U L O 10. S U B C L A S E S E IN T E R F A C E S ......................................................... 329

C LA SES Y M ÉTO D O S A B S T R A C T O S ..................................................................... 330


SU B C LA SES Y H E R E N C IA .......................................................................................... 331
DEFINIR UNA S U B C L A S E ............................................................................................ 334
C ontrol de acceso a los m iem bros de las clases..................................................... 336
Qué m iem bros hereda una subclase........................................................................... 336
A TR IB U TO S C O N EL M ISM O N O M B R E ................................................................ 341
R E D EFIN IR M É TO D O S DE LA S U P E R C L A S E .................................................... 343
C O N ST R U C T O R E S DE LAS S U B C L A S E S .............................................................. 345
D E STR U C TO R E S DE LAS S U B C L A S E S ................................................................. 347
JER A R Q U ÍA DE C L A S E S ............................................................................................... 349
R EFE R E N C IA S A O B JETO S DE UNA S U B C L A S E ............................................. 356
C onversiones im plícitas................................................................................................ 357
C onversiones ex p lícitas................................................................................................ 359
P O L IM O R F IS M O ................................................................................................................ 360
M ÉTO D O S EN L ÍN E A ..................................................................................................... 370
IN T E R F A C E S ....................................................................................................................... 371
D efinir una in te rfa z ....................................................................................................... 371
Un ejem plo: la interfaz IF e c h a ................................................................................... 372
U tilizar una in terfaz....................................................................................................... 374
Clase abstracta frente a in terfaz.................................................................................. 377
U tilizar una interfaz com o un tip o ............................................................................. 378
Interfaces frente a herencia m ú ltip le ........................................................................ 379
Para qué sirve una in te rfa z .......................................................................................... 380
Im plem entar m últiples in te rfa c e s.............................................................................. 380
C LA SES A N ID A D A S ....................................................................................................... 381
C lases in tern as................................................................................................................. 382
C lases definidas dentro de un m é to d o ...................................................................... 383
C lases an ó n im as.............................................................................................................. 384
EJER C IC IO S R E S U E L T O S ............................................................................................. 386
EJER C IC IO S P R O P U E S T O S ..................................... 396

C A P ÍT U L O 11. E X C E P C I O N E S ...................................................................................... 397

E X C EPC IO N ES DE JA V A ............................................................................................... 399


M A N EJA R E X C E P C IO N E S ........................................................................................... 401
Lanzar una ex c ep ció n .................................................................................................... 402
A trapar una ex cep ció n ................................................................................................... 402
B LO Q U E DE F IN A L IZ A C IÓ N ....................................................................... 405
D EC LA R A R E X C E P C IO N E S ......................................................................................... 406
C R E A R Y LA N Z A R E X C E P C IO N E S ......................................................................... 408
C O N T E N ID O X V I I

C U Á N D O U T IL IZA R E X C E PC IO N E S Y C U Á N D O N O .................................... 413


E JE R C IC IO S R E S U E L T O S ............................................................................................. 413
E JE R C IC IO S P R O P U E S T O S .......................................................................................... 418

C A P ÍT U L O 12. T R A B A JA R C O N F IC H E R O S ......................................................... 419

V ISIÓ N G E N E R A L D E LOS FLU JO S D E E /S ......................................................... 421


Flujos q ue no procesan los datos de E /S ................................................................... 422
Flujos q ue procesan los datos de E /S ........................................................................ 424
A B R IEN D O FIC H ER O S PARA A C C ESO S E C U E N C IA L .................................. 429
Flujos de b y tes................................................................................................................. 430
F ileO u tp u tS trean i..................................................................................................... 430
F ileln p u tS tream ......................................................................................................... 433
C lase F ile ........................................................................................................................... 434
Flujos de c a ra c te re s........................................................................................................ 437
F ile W riter.................................................................................................................... 438
F ile R e a d e r................................................................................................................... 439
Flujos de d ato s................................................................................................................. 440
D ataO utputS tream .................................................................................................... 441
D ata ln p u tS trea m ....................................................................................................... 442
U n ejem plo de acceso s e c u e n c ia l............................................................................... 443
SER IA C IÓ N DE O B JE T O S ............................................................................................. 448
E scribir objetos en un fich ero ......................... 450
L eer objetos desde un fich ero ...................................................................................... 451
Seriar objetos que referencian a o b je to s................................................................... 453
A B R IE N D O FIC H ER O S PA R A A C C E SO A L E A T O R IO .................................... 457
La clase R an d om A ccessF ile........................................................................................ 457
La clase C P e rs o n a .......................................................................................................... 460
La clase C L istaT fnos...................................... 461
C onstructor C L istaT fn o s........................................................................................ 461
E scribir un registro en el fic h e ro .......................................................................... 463
A ñadir un registro al final del fic h e ro ................................................................ 464
L eer un registro del fic h e ro ................................................................................... 464
E lim inar un registro del fichero............................................................................ 465
B uscar un registro en el fich ero ............................................................................ 465
U n ejem plo de acceso aleatorio a un fic h e ro .......................................................... 466
M odificar un reg istro ............................................................................................... 469
A ctualizar el fic h e ro ................................................................................................ 471
U TILIZA C IÓ N DE D ISPO SIT IV O S E S T Á N D A R .................................................. 472
E JER C IC IO S R E S U E L T O S ............................................................................................. 473
E JER C IC IO S P R O P U E S T O S .......................................................................................... 493
X V I I I JA V A : C U R SO D E PROGRAM A CIÓN

C A P ÍT U L O 13. E S T R U C T U R A S D IN Á M IC A S ........................................................ 495

LISTA S L IN E A L E S ........................................................................................................... 496


Listas lineales sim plem ente enlazadas...................................................................... 496
O peraciones b á s ic a s ...................................................................................................... 499
Inserción de un elem ento al com ienzo de la lis ta ........................................... 500
Inserción de un elem ento en g e n e ra l.................................................................. 502
B orrar un elem ento de la lista............................................................................... 503
R ecorrer una lis ta ..................................................................................................... 504
B orrar todos los elem entos de una lista .............................................................. 504
B uscar en una lista un elem ento con un valor x .............................................. 504
UNA C L A S E PARA LISTA S L IN E A L E S .................................................................. 505
C lase genérica para listas lin e a le s ............................................................................. 509
C lase L in k e d L ist............................................................................................................ 519
LISTA S C IR C U L A R E S ..................................................................................................... 522
C lase C L istaC ircularS E ................................................................................................ 523
P IL A S ....................................................................................................................................... 527
C O L A S .................................................................................................................................... 529
E JE M P L O .............................................................................................................................. 530
LISTA D O B L E M EN T E E N L A Z A D A .......................................................................... 533
Lista circular doblem ente e n la z a d a ........................................................................... 534
C lase C L istaC ircularD E ......................................................................................... 534
E je m p lo ....................................................................................................................... 540
Á R B O L E S .............................................................................................................................. 542
Á rboles b in a rio s ............................................................................................................. 543
Form as de recorrer un árbol b in a rio ......................................................................... 544
Á RBOLES B IN A R IO S DE B Ú S Q U E D A .................................................................... 546
C lase C A rbolB inB .......................................................................................................... 547
B uscar un nodo en el á rb o l.......................................................................................... 550
Insertar un nodo en el á rb o l......................................................................................... 551
B orrar un nodo del á r b o l .............................................................................................. 553
U tilización de la clase C A rb o lB in B ......................................................................... 555
Á RBOLES B IN A R IO S P E R FEC TA M EN TE E Q U IL IB R A D O S ........................ 558
C lase C A rb o lB inE .......................................................................................................... 559
U tilización de la clase C A rbolB inE ........................................................................... 564
C LA SES R EL A C IO N A D A S DE LA B IB LIO TEC A JA V A .................................. 566
EJERCICIO S R E S U E L T O S ............................................................................................. 567
EJER C IC IO S P R O P U E S T O S .......................................................................................... 581

C A P ÍT U L O 14. A L G O R IT M O S ....................................................................................... 585

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

C A P ÍT U L O 15. H I L O S ......................................................................................................... 633

C O N C E PTO D E P R O C E S O ............................................................................................. 633


H IL O S ...................................................................................................................................... 635
E stados de un h i l o .......................................................................................................... 636
C uándo se debe crear un hilo ....................................................................................... 637
C óm o se crea un h ilo ..................................................................................................... 638
H ilo derivado de T h read ......................................................................................... 640
H ilo asociado con una c la s e .................................................................................. 641
D e m o n io s.......................................................................................................................... 643
F inalizar un h i l o .................................................................. 644
C ontrolar un h i l o ............................................................................................................ 646
P re p a ra d o .................................................................................................................... 647
B lo q u e a d o .................................................................................................................. 647
D o rm id o ...................................................................................................................... 648
E sp eran d o .................................................................................................................... 649
Planificación de h ilo s .................................................................................................... 649
¿Q ué ocurre con los hilos que tengan igual prioridad?.................................. 650
A signar prioridades a los h ilo s ................................................................................... 651
SIN C R O N IZ A C IÓ N DE H IL O S .................................................................................... 654
XX JA V A : C U R SO DE PROGRAM A CIÓN

Secciones c rític a s ........................................................................................................... 655


C rear una sección c rític a ........................................................................................ 659
M onitor reentrante.................................................................................................... 662
U tilizar w ait y n o tify ..................................................................................................... 663
¿Por qué los m étodos alm acenar y obtener utilizan un b u c le ? ..................... 669
ln te rb lo q u e o ..................................................................................................................... 670
G R U PO DE H IL O S ............................................................................................................ 671
G rupo p re d efin id o .......................................................................................................... 671
G rupo e x p lícito ................................................................................................................ 673
T U B E R ÍA S ............................................................................................................................ 673
E SPER A A C TIV A Y P A S IV A ........................................................................................ 678
EJER C IC IO S R E S U E L T O S ............................................................................................. 678
EJER C IC IO S P R O P U E S T O S .......................................................................................... 683

PARTE 3. PRO G RA M A S PARA IN T E R N E T ......................... 685


C A P ÍT U L O 16. A P A R T IR D E A Q U Í ............................................................................. 687

¿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

C iclo de vida de un a p p le t............................................................................................ 711


Pasar parám etros a un a p p le t....................................................................................... 711
F u e n te s .............................................................................................................................. 713
C o lo r................................................................................................................................... 713
M ostrar una im agen........................................................................................................ 714
R eproducir un fichero de s o n id o ................................................................................ 7 16
M ostrar inform ación en la barra de e s ta d o ............................................................. 718
C rear una anim ación...................................................................................................... 718
R estricciones de seguridad con los ap p lets............................................................. 722
IN T ER FA Z G R Á F IC A ...................................................................................................... 723
Estructura de una aplicación........................................................................................ 724
C rear un com ponente S w in g ....................................................................................... 726
M anejo de ev en to s.......................................................................................................... 727
C o n ten ed o res.................................................................................................................... 729
O rganizar ios controles en un c o n te n e d o r........................................................ 730
E stablecer la apariencia de las v en tan a s.................................................................. 731
U n ejem plo de una interfaz gráfica............................................................................ 732
A pplets que utilizan com ponentes S w in g ................................................................ 734
S E R V L E T S ............................................................................................................................ 737
Estructura de un se rv le t................................................................................................ 738
Softw are necesario para ejecutar un servlet............................................................ 742
Ejecutar un serv le t.......................................................................................................... 743
Invocando al servlet desde una página H T M L ...................................................... 744
EJER C IC IO S R E S U E L T O S ............................................................................................. 746
EJER C IC IO S P R O P U E S T O S .......................................................................................... 749

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.

P oner un program a en m archa es sinónim o de ejecutarlo. C uando ejecutam os


un program a, nosotros sólo vem os los resultados que produce (el procesador de
textos m uestra sobre la pantalla el texto que escribim os; el ju eg o visualiza sobre
la pantalla las im ágenes que se van sucediendo) pero no vem os el guión seguido
por el ordenador para conseguir esos resultados. Ese guión es el program a.

A hora, si nosotros escribim os un program a, entonces sí que sabem os cóm o


trabaja y por qué trabaja de esa form a. Esto es una form a m uy diferente y curiosa
de ver un program a de ordenador, lo cual no tiene nada que ver con la experiencia
adquirida en la ejecución de distintos program as.
4 JA V A : C U R SO D E PRO G R A M A CtÓ N

A hora, piense en un ju eg o cualquiera. La pregunta es ¿qué hacem os si quere­


m os enseñar a otra persona a ju g a r? Lógicam ente le explicam os lo que debe ha­
cer; esto es, los pasos que tiene que seguir. D icho de otra form a, le dam os
instrucciones de cóm o debe actuar. Esto es lo que hace un program a de ordena­
dor. Un program a no es nada m ás que una serie de instrucciones dadas al ordena­
d o r en un lenguaje entendido por él, para decirle exactam ente lo que querem os
que haga. Si el ordenador no entiende alguna instrucción, lo com unicará general­
m ente m ediante m ensajes visualizados en la pantalla.

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.

Un lenguaje ensam blador utiliza códigos nem otécnicos para indicarle al


hardw are (com ponentes físicos del ordenador) las operaciones que tiene que reali-
C A PÍTU LO 1: FASES EN E L D ESA RRO LLO DE UN PROGRAM A 5

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:

MOV AH. 4CH

El código M O V expresa una operación consistente en m over alguna inform a­


ción desde una posición de m em oria a otra.

Para traducir un program a escrito en ensam blador a lenguaje m áquina


(código binario) se utiliza un program a llam ado ensam blador que ejecutam os m e­
diante el propio ordenador. Este program a tom ará com o datos nuestro program a
escrito en lenguaje ensam blador y dará com o resultado el m ism o program a pero
escrito en lenguaje m áquina, lenguaje que entiende el ordenador.

C ada m odelo de ordenador, dependiendo del procesador que utilice, tiene su


propio lenguaje ensam blador. D ebido a esto decim os que estos lenguajes están
orientados a la m áquina.

H oy en día son m ás utilizados ios lenguajes orientados al problem a o lengua­


jes de alto nivel. E stos lenguajes utilizan una term inología fácilm ente com prensi­
ble que se aproxim a m ás al lenguaje hum ano. En este caso la traducción es
llevada a cabo por o tro program a denom inado com pilador.

C ada sentencia de un program a escrita en un lenguaje de alto nivel se des­


com pone en general en varias instrucciones en ensam blador. Por ejem plo:

printft "hola" ):

L a función p r i n t f del lenguaje C le dice al ordenador que visualice en el m o­


nitor la cadena de caracteres especificada. E ste m ism o proceso escrito en lenguaje
ensam blador necesitará de varias instrucciones. Lo m ism o podríam os decir del
m étodo p rin tln de Java:

System .out.printlrit " h o l a ” );

A diferencia de los lenguajes ensam bladores, la utilización de lenguajes de


alto nivel no requiere en absoluto del conocim iento de la estructura del procesador
que utiliza el ordenador, lo que facilita la escritura de un program a.
6 JA VA : C U R SO DE PROGRAM A CIÓN

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.

¿Q ué es una m áquina virtual? U na m áquina que no existe físicam ente sino


que es sim ulada en un ordenador por un program a. ¿P or qué utilizar una m áquina
virtual? Porque, por tratarse de un program a, es m uy fácil instalarla en cualquier
ordenador, basta con copiar ese program a en su disco duro, por ejem plo. Y, ¿qué
ventajas reporta? Pues, en el caso de Java, que un program a escrito en este len­
guaje y com pilado, puede ser ejecutado en cualquier ordenador del m undo que
tenga instalada esa m áquina virtual. E sta solución hace posible que cualquier o r­
denador pueda ejecutar un program a escrito en Java independiente de la platafor­
ma que utilice, lo que se conoce com o transportabilidad de program as.

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.

A diferencia de un com pilador, un intérprete verifica cada línea del program a


cuando se escribe, lo que facilita la puesta a punto del program a. En cam bio la
ejecución resulta m ás lenta ya que acarrea una traducción sim ultánea.
C A PÍTU LO I : FASES EN E L D ESA RRO LLO DE UN PRO G R A M A 7

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

U na de las ventajas significativas de Java sobre otros lenguajes de program a­


ción es que es independiente de la plataform a, tanto en código fuente com o en bi­
nario. Esto quiere decir que el código producido por el com pilador Java puede
transportarse a cualquier plataform a (Intel, Sparc, M otorola, etc.) que tenga ins­
talada una m áquina virtual Java y ejecutarse. Pensando en Internet esta caracterís­
tica es crucial ya que esta red conecta ordenadores m uy distintos. En cam bio,
C++, por ejem plo, es independiente de la plataform a sólo en código fuente, lo c u ­
al significa que cada plataform a diferente debe proporcionar el com pilador ade­
cuado para obtener el código m áquina que tiene que ejecutarse.

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.

Los códigos de bytes de Java son un conjunto de instrucciones correspon­


dientes a un lenguaje m áquina que no es específico de ningún procesador, sino de
la m áquina virtual de Java. ¿D ónde se consigue esta m áquina virtual? H oy en día
casi todas las com pañías de sistem as operativos y de navegadores han im plem en-
tado m áquinas virtuales según las especificaciones publicadas p o r Sun M icrosys­
tems, propietario de Java, para que sean com patibles con el lenguaje Java. Para las
aplicaciones de Internet (denom inadas applets) la m áquina virtual está incluida en
el navegador y para las aplicaciones Java convencionales, puede venir con el sis­
tem a operativo, con el paquete Java, o bien puede obtenerla a través de Internet.

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

La explosión de Internet en 1994, gracias al navegador gráfico M osaic para la


W ord W ide W eb ( W W W ), no pasó desapercibida para el grupo investigador de
Sun. Se dieron cuenta de que los logros alcanzados en su proyecto de investiga­
ción eran perfectam ente aplicables a Internet. C om parativam ente, Internet era
com o un gran conjunto de aparatos electrónicos de consum o, cada uno con un
procesador diferente. Y es cierto; básicam ente. Internet es una gran red m undial
que conecta m últiples ordenadores con diferentes sistem as operativos y diferentes
arquitecturas de m icroprocesadores, pero todos tienen en com ún un navegador
que utilizan para com unicarse entre sí. E sta idea hizo que el grupo investigador
abandonara el proyecto de desarrollar un lenguaje que perm itiera la com unicación
entre aparatos electrónicos de consum o y dirigiera sus investigaciones hacia el d e ­
sarrollo de un lenguaje que perm itiera crear aplicaciones que se ejecutaran en
cualquier ordenador de Internet con el único soporte de un navegador.

A partir de aquí ya todo es conocido. Se em pezó a hablar de Java y- de sus


aplicaciones, conocidas com o applets. U n applet es un program a escrito en Java
que se ejecuta en el contexto de una página W eb en cualquier ordenador, indepen­
CA PÍTU LO 1: FASES EN E L D E SA R R O L LO DE UN PRO G R A M A 9

dientem ente de su sistem a operativo y de la arquitectura de su procesador. Para


ejecutar un applet sólo se necesita un navegador que soporte la m áquina virtual de
Java com o, por ejem plo, M icrosoft Internet E xplorer o Netscape. U tilizando un
navegador de éstos, se puede descargar la página W eb que contiene el applet y
ejecutarlo. Precisam ente en este cam po, es donde Java com o lenguaje de progra­
m ación no tiene com petidores. N o obstante, con Java se puede program ar cual­
q uier cosa, razón por la que tam bién puede ser considerado com o un lenguaje de
propósito general; pero, desde este punto de vista, hoy por hoy. Java tiene m uchos
com petidores que le sobrepasan con claridad; por ejem plo A da o C++.

¿POR QUÉ APRENDER JAVA?


Una de las ventajas m ás significativas de Java es su independencia de la platafor­
ma. En el caso de que tenga que desarrollar aplicaciones que tengan que ejecutar­
se en sistem as diferentes esta característica es fundam ental.

O tra característica im portante de Java es que es un lenguaje de program ación


orientado a objetos (POO). Los conceptos en los que se apoya esta técnica de pro­
gram ación y sus ventajas serán expuestos en el capítulo siguiente.

A dem ás de ser transportable y orientado a objetos. Java es un lenguaje fácil


de aprender. T iene un tam año pequeño que favorece el desarrollo y reduce las p o ­
sibilidades de com eter errores; a la vez es potente y flexible.

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.

REALIZACIÓN DE UN PROGRAMA EN JAVA


En este apartado se van a exponer los pasos a seguir en la realización de un pro­
gram a, por m edio de un ejem plo.

L a siguiente figura, m uestra de form a esquem ática lo que un usuario de Java


necesita y debe hacer para desarrollar un program a.
10 JA VA : C U R SO DF. PROGRAM ACIÓN

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

Evidentem ente, para poder escribir program as se necesita un entorno de d esa­


rrollo Java. Sun M icrosystem s, propietario de Java, proporciona uno de form a
gratuita, Java D evelopm ent K it (JD K ), que se puede obtener en la dirección de
Internet:
http://www.sun.com

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.

Para instalar la versión que incluye el C D m encionado en una plataform a


W indow s, hay que ejecutar el fichero j2 sd kl_ 3 _ 0 -w in .ex e. De m anera predeter­
m inada el paquete será instalado en jd k l.3 , pero se puede instalar en cualquier
otra carpeta. A continuación puede instalar en jd k l.3 \d o c s la docum entación que
se proporciona en el fichero j2 sd kl_ 3 _ 0 -d o c.zip . Puede ver m ás detalles sobre la
instalación al final del libro. U na vez finalizada la instalación, se puede observar
el siguiente contenido:

□ ¡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 jr e es el entorno de ejecución de Java utilizado por el SDK. Es si­


m ilar al intérprete de Java (java), pero destinado a usuarios finales que no re­
quieran todas las opciones de desarrollo proporcionadas con la utilidad java.
Incluye la m áquina virtual, la biblioteca de clases, y otros ficheros que so­
portan la ejecución de program as escritos en Java.

• La carpeta lib contiene bibliotecas de clases adicionales y ficheros de soporte


requeridos por las herram ientas de desarrollo.

• La carpeta dem o contiene ejem plos.

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

Cómo crear un programa


Un program a puede ser una aplicación o un applet. C on este libro aprenderá prin­
cipalm ente a escribir aplicaciones Java. D espués aplicará lo aprendido para escri­
bir algunos applets. Em pecem os con la creación de una aplicación sencilla: el
clásico ejem plo de m ostrar un m ensaje de saludo.
1 2 JA VA : C U R S O DE PROGRAM A CIÓN

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.

Interfaz de línea de órdenes


Em pecem os por editar el fichero fuente Java correspondiente a la aplicación. Pri­
m eram ente visualizarem os el editor de textos que vayam os a utilizar; por ejem plo,
el B lock de notas o el Edil. El nom bre del fichero elegido para guardar el progra­
m a en el disco, debe tener com o extensión ja v a ; por ejem plo H olaM undo.java.

U na vez visualizado el editor, escribirem os el texto correspondiente al pro­


gram a fuente. E scríbalo tal com o se m uestra a continuación. O bserve que cada
sentencia del lenguaje Java finaliza con un p u n to y com a y que cada línea del
program a se finaliza pulsando la tecla E ntrar (E n ter o J ).

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

¿Qué hace este programa?

C om entam os brevem ente cada línea de este program a. No apurarse si algunos de


los térm inos no quedan m uy claros ya que todos ellos se verán con detalle en ca­
pítulos posteriores.

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.

A continuación se escribe el m étodo principal m a in . O bserve que un m étodo


se distingue p o r el m odificador () que aparece después de su nom bre y que el blo­
que de código correspondiente al m ism o d efine las acciones que tiene que ejecutar
dicho m étodo. C uando se ejecuta una aplicación, Java espera que haya un m étodo
m ain . Este m étodo define el punto de entrada y de salida de la aplicación.

El m étodo p r in tln del objeto o u t m iem bro de la clase S y stem de la biblioteca


Java, escribe com o resultado la expresión que aparece especificada entre com illas.
Observe que la sentencia com pleta finaliza con punto y com a.

Guardar el programa escrito en el disco

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.

Compilar y ejecutar el programa

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

Previam ente, para que el sistem a operativo encuentre la utilidad ja v a c , utili­


zando la línea de órdenes hay que añadir a la variable de entorno path la ruta de la
carpeta donde se encuentra esta utilidad y otras que utilizarem os a continuación.
Por ejem plo:

pa t h = %p a t h % : c : \ j d k l . 3 \ b i n

La expresión % path% representa el valor actual de la variable de entorno


path. U na ruta va separada de la anterior por un punto y com a.

En la figura siguiente se puede observar el proceso seguido para com pilar


H olaM undo.java desde la línea de órdenes.
1 4 JA VA : CU R SO D E PROGRAM A CIÓ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 : \ K a p 0 1 \ H o laM undo > ia u a c lio Ia M undo . j a u a

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.

Al com pilar un program a, se pueden presentar errores d e com pilación, debi­


do s a que el program a escrito no se adapta a la sintaxis y reglas del com pilador.
Estos errores se irán corrigiendo hasta obtener una com pilación sin errores.

Para ejecutar el fichero resultante de la com pilación, invocarem os desde la lí­


nea de órdenes al intérprete de código de bytes ja v a con el nom bre de dicho fiche­
ro com o argum ento, en nuestro caso H olaM undo, y pulsarem os E ntrar para que
se m uestren los resultados.

En la figura siguiente se puede observar el proceso seguido para ejecutar H o­


laM undo desde la línea de órdenes. H ay que respetar las m ayúsculas y las m inús­
culas en los nom bres que se escriben. A sí m ism o, cabe resaltar que la extensión
.class no tiene que ser especificada.

Una vez ejecutado, se puede observar que el resultado es el m ensaje: H ola


m undo!!!.
CA PÍTU LO 1: FASES EN E L D E SA R R O L LO D E UN PRO G R A M A 15

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 *

C : \R.iptl1 \l ln !,iMundo> j.iu oc H o l a M u n d o . j o u o

C:\Cop 01\H o U M u n d o > 4 ir

E l u o l u n r n de I.i unid .nl C no t i e n e e t i u u c t a


E l n ú n c r o de s e r i e d e l u o l u n c n e s 2 E 1 I -071:8
d i r e c t o r i o de C : \ C d p 0 1 \ H o l o M u n d o

< 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

C : \ C o p 0 1 \ l l o lo M u n d o > jo v ii lio I¿ Mun d o


li n i o m u id o ! I !
C:\Cop01\lloloM undo

Biblioteca de funciones

Java carece de instrucciones de E/S, de instrucciones para m anejo de cadenas de


caracteres, etc. con lo que este trabajo queda para la biblioteca de clases provista
con el com pilador. U na biblioteca es un fichero separado en el disco (con exten­
sión .lib, .jar o .dll) que contiene las clases que definen las tareas m ás com unes,
para que nosotros no tengam os que escribirlas. C om o ejem plo, hem os visto ante­
riorm ente el m étodo p rin tln del objeto o u t m iem bro d e la clase S ystem . Si este
m étodo no existiera, sería labor nuestra el escribir el código necesario para visua­
lizar los resultados en la ventana.

En el código escrito anteriorm ente se puede observar que p ara utilizar un


m étodo de una clase de la biblioteca sim plem ente hay que invocarlo para un o b ­
jeto de su clase y pasarle los argum entos necesarios entre paréntesis. Por ejem plo:

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

Guardar el program a ejecutable en el disco

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

Al ejecutar el program a, pueden ocurrir errores durante la ejecución. Por


ejem plo, puede darse una división por cero. Estos errores solam ente pueden ser
detectados p o r Java cuando se ejecuta el program a y serán notificados con el c o ­
rrespondiente m ensaje de error.

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.

La form a m ás sencilla y eficaz para realizar este proceso, es u tilizar un pro­


gram a depurador. El entorno de desarrollo de Java proporciona para esto la utili­
dad jd b . Éste es un depurador de línea de órdenes un tanto com plicado de utilizar,
por lo que, en principio, tiene escasa aceptación. N orm alm ente los entornos de
desarrollo integrados que anteriorm ente hem os m encionado (excepto FreeJava)
incorporan las órdenes necesarias para invocar y depurar un 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 esde un entorno integrado, habrá que establecer las opción correspondiente


del com pilador.

U na vez com pilado el program a, se inicia la ejecución en m odo depuración y


se continúa la ejecución con las órdenes típicas de ejecución paso a paso.

Entorno de desarrollo integrado


Cuando se utiliza un entorno de desarrollo integrado lo prim ero que hay que hacer
una vez instalado dicho entorno es asegurarse de que las opciones que indican las
rutas de las herram ientas Java, de las bibliotecas, de la docum entación y de los
fuentes, o bien sim plem ente de la ruta donde se instaló el JD K , están establecidas.
Por ejem plo, si utiliza el entorno de desarrollo integrado p cG R A S P que se propor­
ciona en el CD, en el m enú File del m ism o encontrará una orden G lobal P refe-
C A PÍTU LO I : FASES EN E L D E SA R R O L LO D E UN PRO G R A M A 17

rences... que le perm itirá especificar la ruta de la carpeta donde ha instalado el


JD K . O tros entornos proporcionarán una orden O pciones o equivalente.

En la siguiente figura se puede observar el aspecto del entorno de desarrollo


integrado pcG R A SP .

p c G ÍIA S P (HuldM untio i<iva) « F ie l


P Temcíoie* P«*ec» £ompáei Qun y©tp

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.

p u b lic a ta tic v o id m am (Stnng[) args) xJ

System.out.println("Hola m u n d o ' ! 1" ) ;


1

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

Fea Holp. preti F1 L n l.C d l i r

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:

1. Suponiendo que ya está visualizado el entorno de desarrollo, añadim os un


nuevo fichero Java (File, N ew ), editam os el código que com pone la aplicación
y lo guardam os con el nom bre H olaM undo.java (F ile, Save).

2. A continuación, para com pilar la aplicación, ejecutam os la orden C om pile del


m enú Compiler.

3. Finalm ente, para ejecutar la aplicación, suponiendo que la com pilación se


efectuó satisfactoriam ente, seleccionarem os la orden Run Captured, o bien
Run, del m enú Run.

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:

1. Suponiendo que ya está visualizado el entorno de desarrollo, añadim os (File,


N ew ) y editam os cada uno de los ficheros que com ponen la aplicación y los
guardam os con los nom bres adecuados (File, Save).
1 8 JA VA : C U R SO DE PROGRAM ACIÓN

2. Para com pilar la aplicación visualizarem os el fichero que contiene la clase


aplicación (la clase que contiene el m étodo m a in ) y ejecutam os la orden
C om pile del m enú C ompiler. Se puede observar que todas las clases que son
requeridas por esta clase principal son com piladas autom áticam ente, indepen­
dientem ente de que estén, o no, en diferentes ficheros.

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.

IR Ed» ¥»“ T«nptate¡ Pnwcl ¡cmpi« Bun ¡úfn d ow i B*fc

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

| GenwaleCSD | RwnoveCSO | FontSce — |----------------- pÓ”

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

|a vac C :\T e m p \ C A p lic a c io n . ja v a

P r o c e s e F l n is h e d

fot Help, pret* F1

Para crear un proyecto, los pasos a seguir son los siguientes:

1. Suponiendo que ya está visualizado el entorno de desarrollo, se añaden (File,


N ew ) y editan cada uno de los ficheros que com ponen la aplicación y se guar­
dan con los nom bres adecuados (F ile, Save).

2. Después se crea un nuevo proyecto (P roject, N ew P roject W orkspace). En la


ventana que se visualiza, haciendo clic en el botón Brow se correspondiente,
se introduce un nom bre para el proyecto y se selecciona el nom bre de la clase
aplicación (la clase que contiene el m étodo m ain). Posteriorm ente, para vi-
CA PÍTU LO 1: FASES EN EL D ESA RRO LLO DE UN PROGRAM A 19

sualizar la ventana del proyecto, se ejecuta la orden Show Project W orkspace


W indow del m enú Project.

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.

class CAritm etica


I
/*
* Operaciones aritm éticas
*/
public static v o i d main ( S t r in g [] args)
I
int datol. dato2. resultado:

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.

¿Qué hace este programa?

F ijándonos en el m étodo principal, m a in , vem os que se han declarado tres varia­


bles enteras (de tipo in t): d a to l, dato2 y resultado.

in t datol. dato2, resultado;

El siguiente paso asigna el valor 20 a la variable d a to l y el valor 10 a la v a­


riable dato2.

d a t o l = 20:
d a t o 2 = 10:

A continuación se realiza la sum a de esos valores y se escriben los datos y el


resultado.

re su lta d o = datol + dato2;


S y s t e m . o u t . p r in t ln íd a t o l + " + " + dato2 + " = " + resultado):

El m étodo p r in tln escribe un resultado de la forma:

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

Un proceso sim ilar se sigue para calcular la diferencia, el producto y el co­


ciente.

Para finalizar, com pile, ejecute la aplicación y observe los resultados.

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.

V eam os un ejem plo. C onsidere una entidad bancaria. En ella identificam os


entidades que son cuentas: cuenta del cliente 1, cuenta del cliente 2, etc. Pues
bien, una cuenta puede verse com o un objeto que tiene unos atributos, nom bre,
núm ero de cuenta y saldo, y un conjunto de m étodos com o IngresarD inero, Reti-
rarD inero, A bonarlntereses, SaldoA ctual, Transferencia etc. En el caso de una
transferencia:

cuentaOl.Transferencia(cuenta02);

Transferencia sería el m ensaje que el objeto cuenta02 envía al objeto cuentaO l,


solicitando le sea hecha una transferencia, siendo la respuesta a tal m ensaje la eje­
cución del m étodo Transferencia. T rabajando a este nivel de abstracción, m ani­
pular una entidad bancaria resultará algo m uy sencillo.
24 JA V A: C U R SO DE PROGRAM A CIÓN

MECANISMOS BÁSICOS DE LA POO


Los m ecanism os básicos de la program ación orientada a objetos son: objetos,
m ensajes, m étodos y clases.

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.

En adición, las propiedades (atributos) definidas en la clase perm itirán alm a­


cenar inform ación para dicho objeto.
C A PÍTU LO 2: PRO G R A M A CIÓ N O R IEN TA D A A OBJETOS 2 5

C uando se diseña una clase de objetos, la estructura m ás interna del objeto se


oculta a los usuarios que lo vayan a utilizar, m anteniendo com o única conexión
con el exterior, los m ensajes. Esto es, los datos que están dentro de un objeto so­
lam ente podrán ser m anipulados por los m étodos asociados al propio objeto.

o bjeto

m e n s a je s d ato s

m é to d o s

Según lo expuesto, podem os decir que la ejecución de un program a orientado


a objetos realiza fundam entalm ente tres cosas:

1. C rea los objetos necesarios.


2. L os m ensajes enviados a unos y a otros objetos dan lugar a que se procese
internam ente la inform ación.
3. Finalm ente, cuando los objetos no son necesarios, son borrados, liberándo­
se la m em oria ocupada por los m ismos.

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.

Un objeto de una determ inada clase se crea en el m om ento en que se define


una variable de dicha clase. P or ejem plo, la siguiente línea declara el objeto
clienteOI de la clase o tipo CCuenta.

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 lgunos autores em plean el térm ino instancia (traducción directa de instan-


ce), en el sentido de que una instancia es la representación concreta y específica
de una clase; por ejem plo, clienteO I es un instancia de la clase C Cuenta. Desde
este punto de vista, los térm inos instancia y objeto son lo m ism o. El autor prefiere
utilizar el térm ino objeto, o bien ejem plar.

C uando escribe un program a utilizando un lenguaje orientado a objetos, no se


definen objetos verdaderos, se definen clases de objetos, donde una clase se ve
com o una plantilla para m últiples objetos con características sim ilares.
26 JA VA : C U R SO DE PROGRAM A CIÓN

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.

CÓMO CREAR UNA CLASE DE OBJETOS


Según lo expuesto hasta ahora, un objeto contiene, p o r una parte, atributos que
definen su estado, y por otra, operaciones que definen su com portam iento. T am ­
bién sabem os que un objeto es la representación concreta y específica de una cla­
se. ¿C óm o se escribe una clase de objetos? C om o ejem plo, podem os crear una
clase C O rdenador. A bra su entorno de program ación integrado favorito y escriba
paso a paso el ejem plo que a continuación em pezam os a desarrollar:

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 son las características individuales que diferencian un objeto de


otro. El color de una ventana W indow s, la diferencia de otras; el D .N .I. de una
persona la identifica entre otras; el m odelo de un ordenador le distingue entre
otros; etc.

La clase C O rdenador puede incluir los siguientes atributos:

0 M arca: M itac, Toshiba, Ast


0 Procesador: Intel, AM D
0 Pantalla: TFT, D STN , STN

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

0 D ispositivo: encendido, apagado


0 Presentación: activa, inactiva

T odos los atributos son definidos en la clase por variables:

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.

No vam os a profundizar en los detalles de la sintaxis de este ejem plo ya que


el único objetivo ahora es entender la definición de una clase con sus partes bási­
cas. El resto de la sintaxis y dem ás detalles se irán exponiendo poco a poco en su­
cesivos capítulos.

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.

C om o ejem plo, vam os a agregar a la clase C O rdenador un m étodo que res­


ponda a la acción de ponerlo en m archa:
28 JA V A: CU R SO DE PRO G R A M A CIÓ N

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

C om o se puede observar un m étodo consta d e su nom bre precedido por el tipo


del valor que devuelve cuando finalice su ejecución (la palabra reservada void in­
dica que el m étodo no devuelve ningún valor) y seguido p o r una lista de parám e­
tros separados p o r com as y encerrados entre paréntesis (en el ejem plo, no hay
parám etros). Los paréntesis indican a Java que el identificador (E ncenderO rdena-
dor) se refiere a un m étodo y no a un atributo. A continuación se escribe el cuerpo
del m étodo encerrado entre { y }. U sted ya conoce algunos m étodos, llam ados en
otros contextos funciones: seguro que conoce la función logaritm o que devuelve
un valor real correspondiente al logaritm o del valor pasado com o argum ento.

El m étodo E ncenderO rdenador com prueba si el ordenador está encendido; si


lo está, sim plem ente visualiza un m ensaje indicándolo; si no lo está, se enciende y
lo com unica m ediante un m ensaje.

A greguem os un m étodo m ás para que el objeto nos m uestre su estado:

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

El m étodo E stado visualiza los atributos específicos de un objeto. L a secuen­


cia de escape Vi, así se denom ina, introduce un retom o de carro m ás un avance de
línea (caracteres A SC II. CR LF).

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.

H agam os lo m ás sencillo, añadir el m étodo m a in a la clase C O rdenador. El


código com pleto, incluyendo el m étodo m ain . se m uestra a continuación:

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

public static v o i d main (Stringü args)


I
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 nador.Pan talla - "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 í );
M i O r d e n a d o r . E s t a d o ( );
)
3 0 JA V A: C U R SO DE PROGRAM A CIÓN

El m étodo m a in siem pre se declara público y estático, no devuelve un resul­


tado y tiene un parám etro args que es una m atriz de una dim ensión de cadenas de
caracteres. En los capítulos siguientes aprenderá para qué sirve. A nalicem os el
m étodo m a in para q ue tenga una idea de lo que hace:

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

En general, para acceder a un m iem bro de una clase (atributo o m étodo) se


utiliza la sintaxis siguiente:

nom bre_objeto, n o m b r e jn ie m b ro

G uarde la aplicación con el nom bre C O rdenador.java. D espués com pílela y


ejecútela. Se puede observar que los resultados son los siguientes:

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

El ordenador está encendido.

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

public class CMiOrdenador


I
public static v o i d main (StringU args)
I
// ...

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

else II s i no e s t á encendido, encenderlo.


I
OrdenadorEncendido = true;
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 s e ha e n c e n d i d o . " ) :
I
I

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

La aplicación C M iO rdenador.java que acabam os de com pletar tiene dos cla­


ses: la clase aplicación C M iO rdenador y la clase C O rdenador. O bserve que la
clase aplicación es pública (p u b lic) y la otra no. C uando incluyam os varias clases
en un fichero fuente, sólo una puede ser pública y su nom bre debe coincidir con el
del fichero donde se guardan. Al com pilar este fichero. Java creará tanto ficheros
.class com o clases separadas hay.

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.

La abstracción y la encapsulación están representadas por la clase. La clase es


una abstracción, porque en ella se definen las propiedades o atributos de un de­
term inado conjunto de objetos con características com unes, y es una encapsula­
ción porque constituye una caja negra que encierra tanto los datos que alm acena
cada objeto com o los m étodos que perm iten m anipularlos.

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

den tener m ás de una superclase, lo que se conoce com o herencia múltiple. En


este caso, una clase com parte los m étodos y propiedades de varias clases. E sta ca­
racterística, proporciona un poder enorm e a la hora de crear clases, pero com plica
excesivam ente la program ación, por lo que es de escasa o nula utilización. Java,
intentando facilitar las cosas, soluciona este problem a de com portam iento com ­
partido utilizando interfaces.

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.

Todo esto será objeto de un estudio am plio en capítulos posteriores.

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.

Un d estructor es un procedim iento especial de una clase que es llam ado a u ­


tom áticam ente siem pre que se destruye un objeto de esa clase. Su función es rea­
lizar cualquier tarea final en el m om ento de destruir 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

fraccionario sería 0,333333, valor m ás fácil de m anipular, pero a costa d e perder


precisión. Evidentem ente, 1 / 3 * 3 = 1, pero 0,333333 * 3 = 0,999999.

Pensando en un núm ero racional com o si de un objeto se tratara, es fácil de­


ducir que sus atributos son dos: el num erador y el denom inador. Y los m étodos
aplicables sobre los núm eros racionales son num erosos: sum a, resta, m ultiplica­
ción, sim plificación, etc. Pero en base a los conocim ientos adquiridos, sólo añadi­
rem os dos m étodos sencillos: uno, A signarD atos, para establecer los valores del
num erador y del denom inador; y otro, VisualizarRacional, para visualizar un nú­
m ero racional.

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:

v o i d A s i g n a r D a t o s t i n t num, int den)


(
N u me r a d o r = num;
i f ( d e n = = 0 ) den = 1 : //el d e n o m i n a d o r no p u e d e s e r cero
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 ()
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

public static v o i d main ( S t r in g [ ] args)


I
// P u n t o de e n t r a d a a l a a p l i c a c i ó n
C R a c i o n a l r l = new C R a c i o n a 1 C ) : // c r e a r un o b j e t o C R a c i o n a l

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 ;

U na vez editado el program a, guárdelo en el disco con el nom bre CRacio-


nal.java.
36 JA VA : C U R SO D E PROGRAM A CIÓN

¿Qué hace esta aplicación?

Fijándonos en el m étodo principal, m a in , vem os que se ha declarado un objeto r l


de la clase CRacional.

CRacional r l = new C R a c i o n a l O :

En el siguiente paso se envía el m ensaje A signarD atos al objeto r l . El objeto


responde a este m ensaje ejecutando su m étodo A signarD atos que alm acena el
valor 2 en su num erador y el valor 5 en su denom inador; am bos valores han sido
pasados com o argum entos.

r l .A s i g n a r D a t o s t 2 . 5 ) :

Finalm ente, se envía el m ensaje V isualizarR acional al objeto r l. El objeto


responde a este m ensaje ejecutando su m étodo VisualizarRacional que visualiza
sus atributos num erador y denom inador en form a de quebrado; en nuestro caso, el
núm ero racional 2/5.

r l . V i s u a l i z a r R a c i o n a l ( );

Para finalizar, com pile, ejecute la aplicación y observe que el resultado es el


esperado.

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

ELEMENTOS DEL LENGUAJE


En este capítulo verem os los elem entos que aporta Java (caracteres, secuencias de
escape, tipos de datos, operadores, etc.) para escribir un program a. El introducir
este capítulo ahora es porque dichos elem entos los tenem os que utilizar desde el
principio; algunos ya han aparecido en los ejem plos del capítulo 1 y 2. C onsidere
este capítulo com o soporte para el resto de los capítulos; esto es, lo que se va a
exponer en él, lo irá utilizando en m enor o m ayor m edida en los capítulos sucesi­
vos. Por lo tanto, lim ítese ahora sim plem ente a realizar un estudio con el fin de
inform arse de los elem entos con los que contam os.

PRESENTACIÓN DE LA SINTAXIS DE JAVA


Las palabras clave aparecerán en negrita y cuando se utilicen deben escribirse
exactam ente com o aparecen. En cam bio, el texto que aparece en cursiva, significa
que ahí debe ponerse la inform ación indicada por ese texto. Por ejem plo:
if (e x p r e s i ó n b oo leana)
s e n t e n c i a ( s ) a e j e c u t a r si la e x p r e s i ó n b o o l e a n a es v e r d a d :
else
s e n t e n c i á i s ) a e j e c u t a r si la e x p r e s i ó n b o o l e a n a es falsa;

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

C uando dos o m ás opciones aparecen entre llaves “ { }” separadas por “I”, se


elige una, la necesaria para la expresión que se desea construir. Por ejem plo:
constante_entera[ M I L I ] .
38 JA VA : C U R SO DE PROGRAM A CIÓN

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.

Letras, dígitos y otros


E stos caracteres son utilizados para form ar las constantes, los identificadores y las
p a labras clave de Java. Son los siguientes;

• Letras m ayúsculas de los alfabetos internacionales:


A - Z (son v á lid a s las letras acentuadas y la Ñ)

• Letras m inúsculas de los alfabetos internacionales:


a - z (son v á l i d a s las letras acentuadas y la ñ)

• D ígitos de los alfabetos internacionales, entre los que se encuentran:


0 1 2 3 4 5 6 7 8 9

• C aracteres: “ $” y cualquier carácter U nicode por encim a de 00C0.

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

L os caracteres espacio en blanco (A SC II SP), fabulador horizontal (A SC II HT),


avance de página (A SC II FF), nueva línea (A SC II LF), retom o de carro (A SC II
C R) o C R L F (estos dos caracteres son considerados com o uno solo: V?), son ca­
racteres denom inados espacios en blanco, porque la labor que desem peñan es la
m ism a que la del espacio en blanco: actuar com o separadores entre los elem entos
de un program a, lo cual perm ite escribir program as m ás legibles. P or ejem plo, el
siguiente código:

p u b l i c s t a t i c v o i d main ( S t r i n g f ] args) i System .out.p rin t(


" H o l a , qué t a l e s t á i s . \ n “ ); I

puede escribirse de una form a m ás legible así:

public s t a t ic v o i d main (StringH args)


I
System .out.printt"Hola, qué t a l está is.\n");
C A PÍTU LO 3: ELEM ENTOS D E L L EN G U A JE 3 9

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:

Caracteres especiales y signos de puntuación


Este grupo de caracteres se utiliza de diferentes form as; p o r ejem plo, p ara indicar
que un identificador es una función o un array; para especificar una determ inada
operación aritm ética, lógica o de relación, etc. Son los siguientes:

. . : : ? ' " ( ) [ ] ( ] <! | / +

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.

El lenguaje Jav a tiene predefinidas las siguientes secuencias de escape:

S ecuencia A S C II D efinición

\n CR+LF Ir al principio de la siguiente línea


\t HT T abulador horizontal
\b BS R etroceso (backspace)
\r CR R etorno de carro sin avance de línea
\f FF A lim entación de página (sólo para im presora)
1 C om illa sim ple
\" 1 C om illa doble
\\ \ B arra invertida (backslash)
\ddd C arácter A SC II. R epresentación octal
\ ud d d d C arácter A SC II. R epresentación Unicode
\u0007 BEL A lerta, pitido
\ u000 B VT T abulador vertical (sólo para im presora)
40 JA V A: C U R SO DE PROGRAM A CIÓN

O bserve en el ejem plo anterior la secuencia de escape Vz en la llam ada al


m étodo p rin t.

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:

datol = 20; d a t o 2 = 10: resu ltad o = datol + dato2;

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:

int datol. dato2, resultado:

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.

T ipos enteros: by te, s h o rt, int. lo n g y c h a r.


T ipos reales: flo a t y double.

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.

Se les llam a prim itivos porque están integrados en el sistem a y en realidad no


son objetos, lo cual hace que su uso sea m ás eficiente. M ás adelante verem os
tam bién que la biblioteca Java proporciona las clases: B ytc, C h a ra c te r, S h o rt,
In te g e r, L o ng. F lo a t, D o u b le y B oolean, para encapsular cada uno de los tipos
expuestos, proporcionando así una funcionalidad añadida para m anipularlos.
C A P ÍT U L O 3: ELEM ENTOS D E L LEN G U A JE 4 1

byte

El tipo b y te se utiliza para declarar datos enteros com prendidos entre -1 2 8 y


+¡27. Un byte se define com o un conjunto de 8 bits, independientem ente de la
plataform a en que se ejecute el código byte de Java. El siguiente ejem plo declara
la variable b de tipo b y te y le asigna el valor inicial 0. Es recom endable iniciar
toda variable que se declare.

byte b = 0 :

short

El tipo s h o rt se utiliza para declarar datos enteros com prendidos entre -3 2 7 6 8 y


+32767. Un valor s h o rt se define com o un dato de 16 bits de longitud, indepen­
dientem ente de la plataform a en la que resida el código byte de Java. El siguiente
ejem plo declara i y j com o variables enteras de tipo sh o rt:

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

En general, el uso de enteros de cualquier tipo produce un código com pacto y


rápido. A sí m ism o, podem os afirm ar que la longitud de un s h o rt es siem pre m e­
nor o igual que la longitud de un int.

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 a = -1L: /* L indica que la constante 1 es long */


l o n g b = 1 25:
42 JA V A: C U R SO D E PRO G R A M A CIÓ N

long c = 0xlF00230F; /* v a lo r en h e x a d e c i m a l */

En general, podem os afirm ar que la longitud de un in t es m enor o igual que la


longitud de un long.

char

El tipo c h a r es utilizado para declarar datos enteros en el rango \u 0 0 0 0 a \uF F F F


en U nicode (0 a 65535). L os valores 0 a 127 se corresponden con los caracteres
A SCII del m ism o código (ver los apéndices). El ju eg o de caracteres A SC II con­
form an una parte m uy pequeña del juego de caracteres Unicode.

c h a r c a r = 0;

En Java para representar los caracteres se utiliza el código Unicode. Se trata


de un código de 16 bits (esto es, cada carácter ocupa 2 bytes) con el único propó­
sito de internacionalizar el lenguaje. El código Unicode actualm ente representa
los caracteres de la m ayoría de los idiom as escritos conocidos en todo el mundo.

El siguiente ejem plo declara la variable ca r de tipo c h a r a la que se le asigna


el carácter ‘a ' com o valor inicial (observe que hay una diferencia entre "a" y a\ a
entre com illas sim ples es interpretada por el com pilador Java com o un valor, un
carácter, y a sin com illas sería interpretada com o una variable). Las cuatro decla­
raciones siguientes son idénticas:

char car = ' a ’ :


char c a r = 97; /* la ' a ' es el d e c i m a l 97 * /
char car = 0x0061; /* la ‘a ’ es el he xa d e c im a l 0061 */
char car = ’\u 0061’ : /* la ’a ' e s el Unicode 0061 */

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

El tipo d o u b le se utiliza para declarar un dato en com a flotante de 64 bits en el


form ato IE EE 754 (1 bit para el signo, 11 bits para el exponente y 52 para la
m antisa). L os datos de tipo d o u b le alm acenan valores con una precisión aproxi­
m ada de 16 dígitos. Para especificar explícitam ente que una constante (un literal)
es de tipo d o u b le, hay que añadir al final de su v alo r la letra ‘d ' o ‘D ’; p o r om i­
sión, una constante es considerada de tipo d o u b le. El siguiente ejem plo declara
las variables a, b y c, de tipo real de precisión doble:

double a = 3 . 1 4 1 5 9 : / * una c o n s t a n t e e s d o u b l e por om isión */


double b = 2.2e+5: / * 2 . 2 e - 5 - 2 . 2 p o r 10 e l e v a d o a 5 * /
double c = 2/3D:

boolean

El tipo b o o lean se utiliza para indicar si el resultado de la evaluación de una ex­


presión booleana es verdadero o falso. Los dos posibles valores de una expresión
booleana son tr u e y false. Los literales tr u e y false son constantes definidas co­
mo palabras clave en el lenguaje Java. Por tanto, se pueden u tilizar las palabras
tru e y false com o valores de retorno, en expresiones condicionales, en asignacio­
nes y en com paraciones con otras variables booleanas.

El contenido de una variable booleana no se puede convertir a otros tipos, pe­


ro sí se puede convertir en una cadena de caracteres.

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.

Un literal en Java puede ser: un entero, un real, un valor booleano, un carác­


ter, una cadena de caracteres y un valor nulo.
44 JAVA: C U R SO D E PROGRAM A CIÓN

Literales enteros
El lenguaje Java perm ite especificar un literal entero en base 10, 8 y 16.

En general, el signo + es opcional si el valor es positivo y el signo - estará


presente siem pre que el valor sea negativo. Un literal entero es de tipo in t a no ser
que su valor absoluto sea m ayor que el de un in t o se especifique el sufijo / o L,
en cuyo caso será long. Lo expuesto queda resum ido en la línea siguiente:

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:

4326 con stante entera int


4326L con stante entera long
3426000000 c o n sta n te entera long

Un literal entero octal puede tener uno o m ás dígitos del 0 a 7, precedidos por
0 (cero). Por ejem plo:

0326 constante entera i n t en b a s e 8

Un literal entero hexadecim al puede tener uno o m ás dígitos del 0 a 9 y letras


de la A a la F (en m ayúsculas o en m inúsculas) precedidos por Ox o OX (cero se­
guido de x). Por ejem plo:

256 número decimal 2 56


0400 número decimal 2 5 6 e x p r e s a d o en o c t a l
0x100 nú mero decimal 2 5 6 e x p r e s a d o en h e x a d e c i m a l
-0400 nú mero decimal - 2 5 6 e x p r e s a d o en o c t a l
-0x100 número decimal - 2 5 6 e x p r e s a d o en h e x a d e c i m a l

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 ]

donde exponente representa cero o m ás dígitos del 0 al 9 y E o e es el sím bolo de


exponente de la base 10 que puede ser positivo o negativo (2 E -5 = 2 x IQ-5 ). Si
CA PÍTU LO 3: ELEM EN TO S D E L LEN G U A JE 4 5

la constante real es positiva no es necesario especificar el signo y si es negativa


lleva el signo m enos (- ). Por ejem plo:

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:

17.24F constante real de t i p o float

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:

17.24D constante real de t i p o double

Literales de un solo carácter


Los literales de un solo carácter son de tipo c h a r. E ste tipo de literales está for­
mado por un único carácter encerrado entre com illas sim ples. U na secuencia de
escape es considerada com o un único carácter. A lgunos ejem plos son:

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

El valor de una constante de un solo carácter es el valor que le corresponde en


el ju eg o de caracteres de la m áquina.

Literales de cadenas de caracteres


Un literal de cadena de caracteres es una secuencia de caracteres encerrados entre
com illas dobles (incluidas las secuencias de escape com o \ ” ). Por ejem plo:

“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.printí"Escriba un nú me r o e n t r e 1 y 5\n0 pulse E n t r a r ” );

Las cadenas de caracteres en Java son objetos de la clase S t r in g que estudia­


rem os m ás adelante. Esto es, cada vez que en un program a se utilice un literal de
caracteres. Jav a crea de form a autom ática un objeto S t r in g con el valor del literal.

Las cadenas de caracteres se pueden concatenar (unir) em pleando el operador


+. Por ejem plo, la siguiente sentencia concatena las cadenas “ D istancia: ", distan­
cia, y “ Km.".

System.out.p rin tln ("D ista n cia : " + distancia + " Km.");

Si alguna de las expresiones no se corresponde con una cadena, com o se su­


pone que ocurre con distancia. Java la convierte de form a autom ática en una ca­
dena de caracteres. M ás adelante aprenderá el porqué de esto.

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

lo cual indica que un identificador consta de uno o m ás caracteres (véase el apar­


tado anterior “ Letras, dígitos y otros”) y que el p rim er carácter debe ser una letra,
el ca rácter d e subrayado o el carácter d ó la r ($). N o pueden com enzar por un dí­
gito ni pueden contener caracteres especiales (véase el apartado anterior "C aracte­
res especiales” ).

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.

Los identificadores pueden tener cualquier núm ero de caracteres. Algunos


ejem plos son:

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:

abstract default if pri vate throw


boolean do i mp1e me n t s protected throws
break double import publi c transient
byte el s e instanceof return try
case extends i nt short void
catch final interface stati c v o l a t i 1e
char f i n a 11 y 1 ong super whi 1 e
el a s s f 1o a t n a t i ve s wi t c h
const for new s y n c h r o n i zed
continué goto package thi s

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:

• C om entario tradicional. Un com entario tradicional em pieza con los caracte­


res /* y finaliza con los caracteres */. Estos com entarios pueden ocupar más
de una línea, pero no pueden anidarse. P or ejem plo:

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

DECLARACIÓN DE CONSTANTES SIMBÓLICAS


D eclarar una constante sim bólica significa d ec irle al com pilador Java el nombre
de la constante y su valor. Esto se hace utilizando el calificador fin a l y/o el static.

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

U na vez que se haya declarado una constante, por definición, no se le puede


asignar o tro valor. Por ello, cuando se declara una constante debe ser iniciada con
un valor. Por ejem plo, después de haber declarado cte3 según se m uestra en el
ejem plo anterior, una sentencia com o la siguiente daría lugar a un error:

cte3 - 3.14;

¿Por qué utilizar constantes?


U tilizando constantes es m ás fácil m odificar un program a. Por ejem plo, supon­
gam os que un program a utiliza N veces una constante de valor 3.14. Si hemos
definido dicha constante com o fin a l slalic double P i = 3.14 y posteriorm ente ne­
cesitam os cam biar el valor de la m ism a a 3.1416, sólo tendrem os que m odificar
una línea, la que define la constante. En cam bio, si no hem os declarado Pi, sino
que hem os utilizado el valor 3.14 directam ente N veces, tendríam os que realizar
N cam bios.

DECLARACIÓN DE UNA VARIABLE


U na variable representa un espacio de m em oria para alm acenar un v alor de un
determ inado tipo. El valor de una variable, a diferencia de una constante, puede
cam biar durante la ejecución de un program a. Para utilizar una variable en un
program a, prim ero hay que declararla. La declaración de una variable consiste en
enunciar el nom bre de la m ism a y asociarle un tipo:

tipo identificador [. ideriti f i c a d o r ] ...

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.

Por definición, una variable declarada dentro de un bloque, entendiendo por


bloque el código encerrado entre los caracteres '{ ’ y *}’, es accesible directa­
mente, esto es, sin un objeto, sólo dentro de ese bloque. M ás adelante, cuando
tratem os con objetos m atizarem os el concepto de accesibilidad.

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.

Iniciación de una variable


Las variables m iem bro de una clase son iniciadas por om isión por el com pilador
Java para cada objeto que se declare de la m isma: las variables num éricas con 0,
los caracteres con ‘Y)’ y las referencias a las cadenas de caracteres y el resto de las
referencias a otros objetos con nuil. T am bién pueden ser iniciadas explícitam ente.
En cam bio, las variables locales no son iniciadas por el com pilador Java. Por lo
tanto, es nuestra obligación iniciarlas, de lo contrario el com pilador visualizará un
m ensaje de erro r en todas las sentencias que hagan referencia a esas variables.

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)

CONVERSIÓN ENTRE TIPOS DE DATOS


Cuando Java tiene que evaluar una expresión en la que intervienen operandos de
diferentes tipos, prim ero convierte, sólo para realizar las operaciones solicitadas,
los valores de los operandos al tipo del operando cuya precisión sea m ás alta.
Cuando se trate de una asignación, convierte el v alor de la derecha al tipo de la
variable de la izquierda siem pre que no haya pérdida de inform ación. E n otro ca­
so, Java exige q ue la conversión se realice explícitam ente. L a figura siguiente re­
sume los tipos colocados de izquierda a derecha de m enos a m ás precisos; las
flechas indican las conversiones im plícitas perm itidas:

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

La expresión es convertida al tipo especificado si esa conversión está perm iti­


da; en otro caso, se obtendrá un error. La utilización apropiada de construcciones
cast garantiza una evaluación consistente, pero siem pre que se pueda, es m ejor
evitarla ya que suprim e la verificación de tipo proporcionada por el com pilador y
por consiguiente puede conducir a resultados inesperados, o cuando m enos, a una
pérdida de precisión en el resultado. Por ejem plo:

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

El siguiente ejem plo m uestra có m o u tilizar estos operadores. C om o ya hem os


venido diciendo, observe que prim ero se declaran las variables y después se reali­
zan las operaciones deseadas con ellas.

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.

En una asignación, el resultado obtenido en una operación aritm ética es c o n ­


vertido im plícita o explícitam ente al tipo de la variable que alm acena dicho re ­
sultado (véase "C onversión entre tipos de datos” ). Por ejem plo, del resultado de
x/y sólo la parte entera es asignada a c. y a que c es de tipo int. Esto indica que los
reales son convertidos a enteros, truncando la parte fraccionaria.

Un resultado real es redondeado independientem ente del valor de la prim era


cifra decim al suprim ida. O bserve la operación x/y para x igual a 2 e y igual a 3. El
resultado es 0.6666667 en lugar de 0.6666666.

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 ___________________________________________________

< ¿P rim er operando menor que el segundo?


> ¿P rim er operando m ayor que el segundo?
<= ¿P rim er operando m enor o igual que el segundo?
>= ¿P rim er operando mayor o igual que el segundo?
!= ¿P rim er operando distinto que el segundo?
==______________ ¿P rim er operando igual que el segundo?____________
54 JA VA : C U R S O DE PROGRAM A CIÓ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.

El resultado de una operación lógica es de tipo b o o lean . P or ejem plo:

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.

El siguiente ejem plo m uestra cóm o utilizar estos operadores:

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

Operadores a nivel de bits


Estos operadores perm iten realizar con sus operandos las operaciones A N D , OR,
XOR y desplazam ientos, bit por bit. Los operandos tienen que ser enteros.

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.

Los operandos tienen que ser de un tipo prim itivo entero.


56 JA VA : C U R S O DE PROGRAM A CIÓN

int a = 255. r = 0. m - 32:

r - a & 017: // i— 15. Po n e a c e r o t o d o s l o s b i t s d e a


II e x c e p t o l o s 4 b i t s de me n o r p e s o .
r - r | m: II r = 4 7 . Pone a 1 t o d o s l o s b i t s d e r que
II e s t é n a 1 en m.
r - a & -07; // r = 2 4 8 . Po ne a 0 l o s 3 b i t s de me n o r p e s o de a
r = a » 7: II r - 1 . D e s p l a z a m i e n t o de 7 b i t s a l a d e r e c h a .
r = m << 1: II r=64. Equivale a r = m * 2
r = m >> 1: II r=16. E q u iv a le a r = m / 2

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 .

El operador de increm ento increm enta su operando independientem ente de


que se utilice com o sufijo o com o prefijo; esto es, n + + y ++n producen el m ismo
resultado. Idem para el operador de decrem ento.

A hora bien, cuando el resultado de una operación de increm ento se asigna a


una variable, com o se puede o bservar en x = ++n y x = « + + , si el operador de
increm ento se utiliza com o prefijo prim ero se realiza la operación de increm ento y
después la asignación; y si se utiliza com o sufijo, prim ero se realiza la operación
de asignación y después la de increm ento. ídem para el operador de decrem ento.

Operador condicional
El operador condicional (?;), llam ado tam bién operador ternario, se utiliza en ex­
presiones condicionales, que tienen la form a siguiente:

operandoI ? operando2: operando3

La expresión o p era n d o l debe ser una expresión booleana. La ejecución se


realiza de la siguiente forma:

• Si el resultado de la evaluación de op era n d o l es tru e , el resultado de la ex­


presión condicional es operando2.

• Si el resultado de la evaluación de o p era n d o l es false. el resultado de la ex­


presión condicional es operando3.

El siguiente ejem plo asigna a m a yo r el resultado de (a > b ) ? a : b , que será a


si a es m ayor que b y b ú a no es m ay o r que b.

double a = 10.2. b = 20.5, m a y o r = 0;


m a y o r = ( a > b ) ? a : b;
58 JA VA : C U R S O DE PROGRAM A CIÓN

PRIORIDAD Y ORDEN DE EVALUACIÓN


La tabla q ue se presenta a continuación, resum e las reglas de prioridad y asociati-
vidad de todos los operadores. Las líneas se han colocado de m ayor a m enor prio­
ridad. Los operadores escritos sobre una m ism a línea tienen la m ism a prioridad.

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

U na ecuación se puede ver com o un objeto que envuelve el exponente, los


coeficientes y los m étodos que perm itan m anipularla. Para hacer sencillo el ejem ­
plo que tratam os de exponer, el exponente lo suponem os fijo de valor 3, los coefi­
cientes serán variables, y añadirem os dos m étodos: uno que perm ita establecer la
ecuación con la que deseam os trabajar y otro que perm ita evaluarla para un valor
de x dado. R esum iendo, los objetos C E cuacion tendrán unos atributos que serán
los coeficientes y unos m étodos E cuación y ValorPara para m anipularlos.

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.

El m étodo ValorPara evaluará la ecuación para el valor de x pasado com o ar­


gum ento. Este m étodo, utilizando la sentencia return. devolverá com o resultado
el valor calculado. O bserve que el tipo devuelto por el m étodo es double:

Tipo del valor Parámetro que s e pasará


J retornado J como argumento

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

Han aparecido algunos conceptos nuevos (argum entos pasados a un m étodo y


valor retornado por un m étodo). No se preocupe, sólo se trata de un prim er con­
tacto. M ás adelante estudiarem os todo esto con m ayor profundidad. Para una
m ejor com presión de lo dicho, piense en el m étodo o función llam ado logaritm o
que seguro habrá utilizado m ás de una v ez a lo largo de sus estudios. E ste m étodo
devuelve un valor real correspondiente al logaritm o del valor pasado com o argu­
mento: x = log(y). B ueno, pues com párelo con el m étodo ValorPara y com proba­
rá que estam os hablando de m étodos análogos.

Según lo expuesto y aplicando los conocim ientos adquiridos en el capítulo 2,


escribam os en prim er lugar la clase C E cuacion com o se m uestra a continuación.
Observe que no es pública.

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:

public class CMiApl icaciori


I
public static void m a in ( S t r in g [ ] args)
I
C E c u a c i o n e c l = new C E c u a c i o n ( );
e c l . E c u a c i ó n d , - 3 . 2 . 0. 7 ) ;

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

R ecuerde que el m étodo m a in ; es p o r donde em pieza a ejecutarse la aplica­


ción. Este m étodo crea un objeto e c l de la clase C E cuacion, envía al objeto e c l el
m ensaje E cuación para establecer los coeficientes de la ecuación y a continuación
le envía el m ensaje ValorPara con el objetivo de evaluar la ecuación para el valor
de x pasado com o argum ento.

U na vez escrita la aplicación debe guardarla con el nom bre C M iAplica-


cion.java (nom bre de la clase pública) y com pilarla. D espués puede ejecutarla y
observar los resultados. Incluso puede atreverse a evaluar otras ecuaciones para
distintos valores de x.

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

2. ¿Q ué resultados se obtienen al realizar las operaciones siguientes? Si hay errores


en la com pilación, corríjalos y dé una explicación de por qué suceden.

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

4. Escriba el valor A SC II de la ‘q' y de la 'Q ' sin consultar la tabla.

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.

6. E scriba el código necesario para evaluar la expresión:

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.

ESTRUCTURA DE UNA APLICACIÓN JAVA


Puesto que Jav a es un lenguaje orientado a objetos, un program a Java se com pone
solam ente de objetos. R ecuerde que un objeto es la concreción de una clase, y que
una clase equivale a la generalización de un tipo específico de objetos. La clase
define los atributos del objeto a sí com o los m étodos para m anipularlos. M uchas
de las clases que utilizarem os pertenecen a la biblioteca de Java, por lo tanto ya
están escritas y com piladas. P ero otras tendrem os que escribirlas nosotros m is­
m os, dependiendo del problem a que tratem os de resolver en cada caso.

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

U na clase que contiene un m étodo m a in es una plantilla para crear lo que


vam os a denom inar objeto aplicación, objeto que tiene com o m isión iniciar y fi-
64 JA V A: C U R SO DE PROGRAM A CIÓN

nalizar la ejecución de la aplicación. Precisam ente, el m étodo n ia in es el punto de


entrada y de salida de la aplicación.

Según lo expuesto, la solución de cualquier problem a no debe considerarse


inm ediatam ente en térm inos de sentencias correspondientes a un lenguaje, sino de
objetos naturales del problem a m ism o, abstraídos de alguna m anera, que darán
lugar a los objetos que intervendrán en la solución del program a. El em pleo de
este m odelo de desarrollo de program as, nos conduce al diseño y program ación
orientada a objetos, m odelo que ha sido em pleado para desarrollar todos los
ejem plos de este libro.

Para ex plicar cóm o es la estructura de un program a Java, vam os a plantear un


ejem plo sencillo de un program a que presente una tabla de equivalencia entre
grados centígrados y grados fa h ren h eit, com o indica la figura siguiente:

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

A nalicem os el problem a. ¿D e qué trata el program a? De grados. Entonces


podem os pensar en objetos “grados” que encapsulen un valor en grados centígra­
dos y los m étodos necesarios para asignar al objeto un valor en grados centígra­
dos, así com o para obtener tanto el dato grados centígrados com o su equivalente
en grados fa h renheit. En base a esto, podríam os escribir una clase C G rados como
se puede observar a continuación:

class CGrados
I
prívate f lo a t gradosC; // g r a d o s centígrados

public void C e n tíg ra d o sA sig n a rtflo a t gC)


I
// E s t a b l e c e r e l atributo grados centígrados
g r a d o s C = gC:
CA PÍTU LO 4 : ESTRU C TU R A DE UN PRO G R A M A 6 5

public float FahrenheitObtenerí)


I
// R e t o r n a r l o s g r a d o s f a h r e n h e i t equivalentes a gradosC
r e t u r n 9F/5F * g r a d o s C + 32:

public float Cent1grados0btener()


I
return gradosC; // r e t o r n a r los grados 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:

• gradosC , grados centígrados,


y una interfaz de acceso form ada p o r los m étodos:

• 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

increm entos de 6. El objeto C G rados responderá ejecutando los m étodos vincula­


dos con los m ensajes que recibe. Según esto, el código de la clase que dará lugar
al objeto aplicación puede ser el siguiente:

import j a v a . 1 ang.System : // i m p o r t a r la clase System

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:

public static void m a in (S trin g [] args)


I
// D e c l a r a c i ó n de v a r i a b l e s
C G r a d o s g r a d o s - new C G r a d o s ( ) :
int gradosCent = lim ln f e r io r :
f l o a t g r a d o s F a h r = 0:

while (gradosCent < - lim Su p e rio r) // m i e n t r a s hacer:

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

U na vez analizado el problem a, cree una nueva aplicación desde su entorno


de desarrollo y escriba la clase CGrados', observe que no es pública. A continua­
ción, escriba la clase aplicación C A pG rados en el m ism o fichero fuente; observe
CA PÍTU LO 4: ESTRU C TU R A DE UN PRO G R A M A 6 7

que es pública. D espués, guarde la aplicación que ha escrito en un fichero utili­


zando com o nom bre el de la clase aplicación; esto es, CApGrados.jcivci. Final­
m ente, com pile y ejecute la aplicación.

N o se preocupe si no entiende todo el código. A hora lo que im porta es que


aprenda cóm o es la estructura de un program a, no por qué se escriben unas u otras
sentencias, cuestión que aprenderá m ás tarde en éste y en sucesivos capítulos.
Este ejem plo le servirá com o plantilla para inicialm ente escribir sus propios pro­
gram as. Posiblem ente su prim era aplicación utilice solam ente un objeto aplica­
ción, pero con este ejem plo tendrá un concepto m ás real de lo que es una
aplicación Java.

En el ejem plo realizado podem os observar que una aplicación Java consta de:

• Sentencias im p o rt (para establecer vínculos con otras clases de la biblioteca


Jav a o realizadas por nosotros).
• U na clase aplicación pública (la que incluye el m étodo m ain).
• O tras clases no públicas.

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:

• D efiniciones y/o declaraciones.


• S entencias a ejecutar.

E n un fichero se pueden incluir tantas definiciones de clase com o se desee,


pero sólo una de ellas puede ser declarada com o pública (pu b lic). R ecuerde que
cada clase pública debe ser guardada en un fichero con su m ism o nom bre y exten­
sión .java.

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.

Paquetes y protección de clases


Un paquete es un conjunto de clases, lógicam ente relacionadas entre sí, agrupadas
bajo un nom bre (por ejem plo, el paquete ja v a .io agrupa las clases que perm iten a
un program a realizar la entrada y salida de inform ación); incluso, un paquete pue­
de contener a otros paquetes. A nálogam ente a com o las carpetas o directorios
ayudan a o rganizar los ficheros en un disco duro, los paquetes ayudan a organizar
las clases en grupos para facilitar el acceso a las m ism as cuando las necesitem os
en un program a. A prenderá a crear paquetes m ás adelante, ahora es suficiente con
que aprenda a utilizar los paquetes de la biblioteca de Java.
68 JA VA : CU R SO DE PROGRAM A CIÓN

La propia biblioteca de clases de Java está organizada en paquetes dispuestos


jerárquicam ente. En la figura siguiente se m uestran algunos de ellos:

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.

Para referirnos a una clase de un paquete, tenem os que hacerlo utilizando su


nom bre com pleto, excepto cuando el paquete haya sido im portado im plícita o ex­
plícitam ente, com o verem os a continuación. P or ejem plo, ja v a .la n g.S y ste n i hace
referencia a la clase Sy ste m del paquete ja v a .la n g ("java.lang" es el nom bre
com pleto del paquete lang).

Las clases que guardam os en un fichero cuando escribim os un program a,


pertenecen al paquete predeterminado sin nom bre. Por ejem plo, las clases CGra-
dos y CApGrados de la aplicación anterior pertenecen, por om isión, a este pa­
quete. D e esta form a Java asegura que toda clase pertenece a un paquete.

Protección de una clase


La protección de una clase determ ina la relación que tiene con otras clases de
otros paquetes. D istinguim os dos niveles de protección: de paquete y público.
U na clase con nivel de protección de paquete sólo puede ser utilizada por las cla­
ses de su paquete (no está disponible para otros paquetes, ni siquiera para los sub-
paquetes). En cam bio, una clase pública puede ser utilizada por cualquier otra
clase de otro paquete. ¿Q ué se entiende p o r utilizar? Q ue una clase puede crear
objetos de otra clase y m anipularlos utilizando sus m étodos.

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

2. Im portando la clase, com o se indica en el párrafo siguiente, lo que posibilita


referirse a ella sim plem ente por su nom bre. Por ejem plo:

System.out.println(gradosFahr);

Para im portar una clase de un paquete desde un program a utilizarem os la


sentencia im p ort. En un program a Java puede aparecer cualquier núm ero de sen­
tencias im p o rt, las cuales deben escribirse antes de cualquier definición de clase.
Por ejem plo:

i mpor t j a v a . l a n g . S y s t e m : // i m p o r t a r la clase System

public class CApGrados


1
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 “ );
11...
\

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

public class CApGrados


I
I I ...
S y s t e m , o u t . p r i n t l n( g r a d o sC e n t + " C" + " \ t ” + g r a d o s F a h r + " F " ) ;
11...
I

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:

A dem ás, una variable puede ser iniciada en la propia definición:

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:

La definición de un m étodo, declara el m étodo y adem ás incluye el cuerpo del


m ism o. En cam bio, la declaración de un m étodo se corresponde con la cabecera
de dicho m étodo (su aplicación podrá verla en clases abstractas e interfaces).
CA PÍTU LO 4: ESTRU C TU R A DE UN PROGRAM A 7 1

public float FahrenheitObtenert)


I
// R e t o r n a r l o s g r a d o s f a h r e n h e i t e q u i v a l e n t e s a gradosC
r e t u r n 9 F / 5 F * g r a d o s C + 32;

La declaración o la definición de una variable pueden realizarse a nivel ele la


clase (atributos de la clase) o a nivel de! m étodo (dentro de la definición de un
m étodo). Pero, la definición de un m étodo, siem pre ocurre a nivel de la clase.

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)

public void unMe todo ( . l i s t a de p a r á m e t r o s )


- I
Nivel del m é to d o D e c l a r a c i ó n de v a r i a b l e s

S e n t e r ic i as

En un m étodo, las definiciones o declaraciones se pueden realizar en cual­


quier lugar; o m ejor dicho, en el lugar ju sto donde se necesiten y no necesaria­
mente al principio del m étodo, antes de todas las sentencias.

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

Sentencia compuesta o bloque


U na sentencia com puesta o bloque, es una colección de sentencias sim ples inclui­
das entre llaves - { } -. U n bloque puede contener a otros bloques. U n ejem plo de
una sentencia de este tipo es el siguiente:

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.

Un m odificador es una palabra clave que m odifica el nivel de protección pre­


determ inado del m étodo. Véase el apartado “ P rotección de los m iem bros de una
clase" expuesto un poco m ás adelante.

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

L a sentencia re tu rn puede ser o no la últim a y puede aparecer m ás de una vez


en el cuerpo del m étodo. En el caso de que el m étodo no retorne un valor (void),
se puede om itir o especificar sim plem ente return. P or ejem plo:

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:

public void CentigradosAsignar(float gC)


I
// E s t a b l e c e r e l atributo grados centígrados
g r a d o s C = gC:
1

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:

public static void m a in (S trin g [] args)


I
// C u e r p o d e l mét od o
I

C om o se puede observar, el m étodo m a in es público (pu b lic), estático


(static), no devuelve nada (v o id ) y tiene un argum ento de tipo S t r in g que alm a­
cenará los argum entos pasados en la línea de órdenes cuando se invoca a la apli­
cación para su ejecución, concepto que estudiarem os posteriorm ente en otro
capítulo. Para m ás detalles, puede ver un poco m ás adelante los apartados “ Pro­
tección de los m iem bros de una clase” y “M iem bro de un objeto o de una clase” .

Crear objetos de una clase

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.

Cómo acceder a los miembros de un objeto


Para acceder desde un m étodo de la clase aplicación o de cualquier otra clase a un
m iem bro (atributo o m étodo) de un objeto de otra clase diferente se utiliza la sin­
taxis siguiente: objeto.m iem bro. Por ejem plo:

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.

Cuando el m iem bro accedido es un m étodo, la interpretación que se hace en


program ación orientada a objetos es que el objeto ha recibido un m ensaje, el es­
pecificado p o r el nom bre del m étodo, y responde ejecutando ese m étodo. Los
m ensajes que puede recibir un objeto se corresponden con los nom bres de los
m étodos de su clase. P or ejem plo, una sentencia com o:

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

se interpreta com o que el objeto grados recibe el m ensaje C entígradosAsignar.


Entonces el objeto responde a ese m ensaje ejecutando el m étodo de su clase que
tenga el m ism o nom bre. Lógicam ente, com o el m étodo se ejecuta para un objeto
concreto, el cuerpo del m ism o no necesita especificar explícitam ente de qué o b ­
jeto es el m iem bro accedido. Esto es, en el ejem plo siguiente se sabe que gradosC
pertenece al objeto que está respondiendo al m ensaje CentígradosAsignar.

public void C e n tig ra d o sA sig n a r(flo a t gC)


I
// E s t a b l e c e r el atributo grados centígrados
g r a d o s C - gC:
I

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.

Protección de los miembros de una clase


Los m iem bros de una clase son los atributos y los m étodos, y su nivel de protec­
ción determ ina quién puede acceder a los m ism os. Los niveles de protección a los
que nos referim os son: de p a q u ete, público, privado y protegido. D e este último
hablarem os en un capítulo posterior.

Por ejem plo, en la clase C G rados de la aplicación realizada al principio de


este capítulo, hem os definido los atributos privados y los m étodos, públicos:

class CGrados
I
prívate f lo a t gradosC; // g r a d o s centígrados

p u b líc void C e n tíg ra d o sA sig n a rtflo a t gC)


I
// E s t a b l e c e r el atributo grados centígrados
g r a d o s C = gC:
I
// ...

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.

Si un m étodo de otra clase, por ejem plo el m étodo m a in de la clase C A pG ra­


dos, incluyera una sentencia com o la siguiente,

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.

U n m iem bro de una clase declarado p ú b lico es accesible desde cualquier m é­


todo definido dentro o fuera de la clase o paquete actual. Por ejem plo, en la clase
CApG rados, se puede observar cóm o el objeto grados de la clase C G rados creado
en el m étodo m a in accede a su m étodo C entígradosA signar con el fin de m odifi­
car el valor de su m iem bro privado gradosC .
C A PÍTU LO 4: ESTRU C TU R A DE UN PRO G R A M A 7 7

public class CApGrados


I
// ...
public static void m a in ( S t r in g t ] args)
I
// D e c l a r a c i ó n de v a r i a b l e s
C G r a d o s g r a d o s = new C G r a d o s O :
II...
while ( g r a d o s C e n t < = 1 i i n S u p e r i o r ) // w h i l e ... hacer:
I
// 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 centígrados
grados.CentIgradosAsignar(gradosCent)
II...

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.

El nivel de protección predeterm inado para un m iem bro de una clase es el de


paquete. Un m iem bro de una clase con este nivel de protección puede ser accedi­
do desde todas las otras clases del m ism o paquete.

Miembro de un objeto o de una clase


Sabem os que una clase agrupa los atributos y los m étodos que definen a los obje­
tos de esa clase. Pero, cada objeto que creem os de esa clase ¿m antiene una copia
tanto de los atributos com o de los m étodos? L ógicam ente, cada objeto m antiene
su propia copia de los atributos para alm acenar sus datos particulares; pero, de los
m étodos sólo hay una copia para todos los objetos, lo cual tam bién es lógico, por­
que cada objeto sólo requiere utilizarlos; por ejem plo, cuando necesite m odificar
sus atributos. D esde este análisis se dice que los m iem bros son del objeto; esto es,
un m ism o atributo tiene un valor específico para cada objeto, y un objeto ejecuta
un m étodo en respuesta a un m ensaje recibido.
78 JA VA : C U R SO DE PROGRAM A CIÓN

Esta form a de concebir los objetos puede suponer, en ocasiones, un desperdi­


cio de espacio de alm acenam iento; por ejem plo, volviendo a la clase COrdenador
que expusim os en el capítulo 2, podríam os pensar en añadir un nuevo atributo que
fuera el tiem po de garantía. Si suponem os que este tiem po es el m ism o para todos
los ordenadores, sería m ás eficiente definir un atributo que no form ara parte de la
estructura de cada objeto, sino que fuera com partido por todos los objetos. En este
caso, direm os que el atributo es d e la clase de los objetos, no del objeto.

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.

Para acceder a un atributo static de la clase puede utilizar un objeto de la cla­


se, o bien el nom bre de la clase com o puede verse en el ejem plo siguiente:

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

public static v o i d main (Stringf] args)


I
II G a r a n t í a e x i s t e a u n q u e no h a y a o b j e t o s definidos de l a clase
C O r d e n a d o r . G a r a n t í a - 1:

U tilizar una expresión com o M ¡Ordenador. G arantía, siendo M iO rdenador un


objeto de la clase C O rdenador, aunque sea correcta, no se aconseja porque puede
resultar engañosa. Parece que nos estam os refiriendo al atributo G arantía del o b ­
je to M iO rdenador, cuando en realidad nos estam os refiriendo a todos los objetos
que el program a haya creado de la clase C O rdenador.

A nálogam ente, un m étodo declarado static es un m étodo de la clase. Este tipo


de m étodos no se ejecutan para un objeto particular, sino que se aplican en gene­
ral donde se necesiten, lo que im pide que puedan acceder a un m iem bro del obje­
to. Una aplicación puede acceder a un m étodo estático de la m ism a form a que se
ha expuesto para un atributo estático.

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

se hubiera utilizado un m étodo no static, tendría que ser invocado a través de un


objeto de la clase, lo que, siendo correcto, resultaría engañoso.

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

public static void E s t a b l e c e r G a r a n t 1 a ( b y t e g)


I
Garantía - g; // G a r a n t í a es un m i e m b r o de l a clase
1
1

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.

G ráficam ente puede im aginarse una referencia y el objeto referenciado, ubi­


cados en algún lugar del espacio de m em oria correspondiente a su aplicación, así:

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

En realidad una referencia es la posición de m em oria donde se localiza un


objeto. O bservará que anteriorm ente nos hem os referido a la referencia grados
com o el objeto grados. Esto es una form a de abreviar que no crea confusión, ya
que grados es única y referencia un único objeto C G rados. U na expresión com o
“el objeto referenciado por la variable grados" resulta dem asiado larga y no
aporta m ás inform ación. Expresándonos en estos térm inos, cuando se asigne un
objeto a otro, o bien se pasen objetos com o argum entos a m étodos, lo que se están
copiando son referencias, no el contenido de los objetos.

El siguiente ejem plo aclarará este concepto. Se trata de la aplicación C Racio­


nal que expusim os al final del capítulo 2, com puesta p o r la clase C R acional a la
que hem os añadido un nuevo m étodo estático que perm ite sum ar dos núm eros ra­
cionales, devolviendo com o resultado el núm ero racional resultante de la sum a.

class CRacional
I
prívate int Numerador:
prívate int Denominador:

public void A sig n a rD a to stin t num. int den)


I
N u me r a d o r = num:
i f ( d e n = = 0 ) den = 1; //el d e n o m i n a d o r no pu ede s e r c e r o
D e n o m i n a d o r = den;
I

public void VisualizarRacional( )


I
S y s te m .o u t .p rin tln (N u m e ra d o r + “/" + Denominador):
I
CA PÍTU LO 4: ESTRU C TU R A DE UN PRO G R A M A 8 1

public static CRacional SumarCCRacional a. CRacional b)


I
C R a c i o n a l r = new C R a c i o n a l O :
i n t num = a . N u m e r a d o r * b . D e n o m i n a d o r +
a . Denominador * b.Numerador:
i n t den = a . D e n o m i n a d o r * b . D e n o m i n a d o r ;
r.A signa rD a to sfn u m , den);
r e t u r n r:

public static v o i d main (String[] args)


I
// P u n t o de e n t r a d a a l a a p l i c a c i ó n
C R a c i o n a l r l . r2:
r l = 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
rl .AsignarD atos(2, 5):
r2 = r l :

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.

A nalizada la clase C R acional pasem os a estudiar el m étodo m a in . La prim era


parte de este m étodo declara dos variables r l y r2 de tipo C R acional, crea un
nuevo objeto r l de tipo C R acional asignándole el valor 2/5. y asigna el valor de
r l a r2.

A continuación, asigna a r l un nuevo valor 3/7, ¿cuál es el valor de r2? C om ­


probam os que es el m ism o que el de r l . ¿Q ué ha ocurrido? Q ue cuando se asignó
82 JA VA : CU R SO DE PROGRAM A CIÓN

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

Si realm ente lo que deseam os es que r l y r2 señalen a objetos separados, hay


que utilizar new con am bas referencias para crear objetos separados:

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

C om o hem os visto, una variable de un tipo referenciado se puede asignar a


otra del m ism o tipo. En cam bio, no existe aritm ética de referencias (por ejem plo,
a una referencia no se le puede sum ar un entero) ni tam poco se puede asignar di­
rectam ente un entero a una referencia.

Pasando argumentos a los métodos


La segunda parte del m étodo m a in del ejem plo anterior, crea un objeto r2 y le
asigna el valor 2/5. A continuación, invoca al m étodo estático S um ar pasándole
com o argum entos los objetos r l y r2 que querem os sum ar. El resultado devuelto
por Sum ar será un objeto C Racional que quedará referenciado p o r r3.

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.

C uando se invoca a un m étodo, el prim er argum ento es pasado al prim er pa­


rám etro, el segundo argum ento es pasado al segundo parám etro y así sucesiva­
m ente. En Jav a todos los argum entos que son objetos son pasados por referencia.

public static CRacional SumaríCRacional a, CRacional b)


I
C R a c i o n a l r = new C R a c i o n a l ( );
i n t num = a . N u me r a do r * b . D e n o m i n a d o r +
a .D en omin ado r * b.N um e ra do r;
i n t den = a . D e n o m i n a d o r * b . D e n o m i n a d o r ;
r . A s i g n a r D a t o s t n u m , den):
r e t u r n r;
I

A continuación, el m étodo Sum ar utiliza new para crear un nuevo objeto r al


que asigna el resultado de la sum a de los objetos a y b. F inalm ente devuelve r.
O tra vez m ás lo que se devuelve es una referencia que Se copia en r3. Finalizado
este proceso la variable r desaparece por ser local, no sucediendo lo m ism o con el
objeto que señalaba, ya que ahora está señalado por r3.

El recolector de basura de Java elim inará un objeto cuando no exista ninguna


referencia al m ism o.

PROGRAMA JAVA FORMADO POR MÚLTIPLES FICHEROS


Según lo que hem os visto, un program a Java es un conjunto de objetos que se
com unican entre sí. Para crear los objetos, escribim os plantillas que denom inam os
clases. Por ejem plo, en la aplicación acerca de núm eros racionales escribim os una
sola clase, pero en la aplicación acerca de conversión de grados centígrados a
F ahrenheit, escribim os dos clases. En am bas aplicaciones, alm acenam os todo su
código en un único fichero .java. Esto no debe inducim os a pensar que todo pro­
gram a tiene que estar escrito en un único fichero. De hecho no es así, ya que ge-
84 JA V A: C U R SO DF. PROGRAM A CIÓN

neralm ente se alm acena cada clase en un único fichero para favorecer su m ante­
nim iento y posterior reutilización.

Com o ejem plo, reconstruyam os la aplicación C R acional creando ahora dos


clases separadas: C R acional y C Aplicacion.

La clase C R acional incluirá su estructura de datos y su interfaz pública, ex ­


cepto el m étodo m a in que será ahora incluido en C Aplicacion. C uando haya es­
crito la clase C R acional guárdela en el fichero C Racional.java.

public class CRacional


I
private int Numerador;
prívate int Denominador:

public void AsignarDatostint num. i n t den)


I
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 n o puede s e r c e r o
D e n o m i n a d o r = den;

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

public static CRacional SumartCRacional a. CRacional b)


I
C R a c i o n a l r - new C R a c 1 o n a l ( ) :
i n t num = a . N u m e r a d o r * b . D e n o m i n a d o r +
a . Denominador * b.Numerador;
i n t den - a . Den ominador * b . D e n o m i n a d o r ;
r.Asigna rD atostn um , den);
r e t u r n r;

Escriba ahora la clase C Aplicacion que se m uestra a continuación y guárdela


en el fichero C Aplicacion.java.

public class CAplicacion


I
p u b l i c s t a t i c v o i d main ( S t r i n g f ] a r g s )
i
// P u n t o de e n t r a d a a l a a p l i c a c i ó n
C Ra cio nal r l . r2;
CA PÍTU LO 4: ESTRU C TU R A D E UN PROGRAM A 8 5

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

CRa cio nal r3:


r?. = 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
r2 .A sig n a rD a to s(2 , 5):
r3 = C R a c i o n a l . S u m a r í r l , r 2 ); // r 3 = 3 / 7 + 2/5
r3.VisualizarRacional(): // s e v i s u a l i z a 2 9 / 3 5

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)

— public v o i d unMe todo ( . l i s t a de p a r á m e t ro s )


V a ria b le lim itada
a un m é to d o Variables locales

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

U na variable declarada dentro de un m étodo es una variable local al método.


L os parám etros de un m étodo son tam bién variables locales al m étodo. Y una va­
riable declarada dentro de un bloque correspondiente a una sentencia com puesta
tam bién es una variable local a ese bloque.

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.

U na vez abstraídas las características generales de la clase de objetos cuentas


bancarias, el paso siguiente es escribir el código que da lugar a la im plem entación
de dicha clase. Ésta puede ser m ás o m enos así:
/**
* E s t a c l a s e i m p l e m e n t a una c u e n t a b a n c a r i a que
* s i m u l a e l c o m p o r t a m i e n t o b á s i c o de una c u e n t a
* a b i e r t a en una e n t i d a d b a n c a r i a c u a l q u i e r a .
*/
public class CCuentaBancaria
I
p riv a t e double t i p o D e l n t e r é s :
p rív a t e double saldo :

public void EstablecerTipoDelnteréstdouble ti)


I
i f ( t i < 0)
I
System .out.printlnU El t i p o de i n t e r é s no puede s e r n e g a t i v o " ) :
r e t u r n : // r e t o r n a r
CA PÍTU LO 4: ESTRU C TU R A DE UN PROGRAM A 8 7

ti poDelnterés = t i ;

public void IngresarDinero(doub1e ingreso)


I
saldo += ingreso:

public void R e tira rD in e roid o u b le cantidad)


I
if ( saldo - cantidad < 0)
1
System .out.println("N o tiene saldo s u f i c i e n t e " );
return;
I
// Hay s a l d o s u f i c i e n t e . R etirar la cantidad,
saldo - - cantidad:
I

public double S a ld o A c t u a l()


I
return saldo:
I

public void A b o n a rIn te re se s()


I
s a ld o += s a ld o * tipoDelnterés / 100:

public static v o i d main (StringH args)


I
// A b r i r una c u e n t a c o n 1 . 0 0 0 . 0 0 0 a un 2%
C C u e n t a B a n c a r i a C u e n t a O l - new C C u e n t a B a n c a r i a t );
C u e n ta O l. In gre sa rD i n e r o ( 1000000):
CuentaOl.EstablecerTipoDeInterés(2):

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

A nalicem os brevem ente el código. El m étodo E stablecerTipoD ehiterés veri­


fica si el valor pasado com o argum ento es negativo, en cuyo caso lo notifica y
term ina. Si es positivo, lo asigna al m iem bro tipoD elnterés.
88 JA V A : C U R SO D E PROGRAM A CIÓN

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.

El m étodo SaldoA ctual devuelve el valor del saldo actual en la cuenta.

El m étodo A bonarlntereses acum ula los intereses sobre el saldo actual.

Finalm ente, el m étodo m a in crea e inicia un objeto de la clase C CuentaBan-


caria y realiza sobre el m ism o las operaciones program adas con el fin de com pro­
bar su correcto funcionam iento.

EJERCICIOS PROPUESTOS
1. E scriba la aplicación C ApG rados.java y com pruebe los resultados.

2. En el capítulo 1 hablam os acerca del depurador. Si su entorno integrado favorito


aporta la funcionalidad necesaria para depurar un program a, pruebe a ejecutar la
aplicación C ApG rados.java paso a paso y verifique los valores que van tom ando
las variables a lo largo de la ejecución.

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.

4. C argue en su entorno de desarrollo integrado la aplicación C ApG rados.java y


m odifique la sentencia:

return 9F/5F * g r a d o s C + 32;

correspondiente al m étodo F ahrenheitO btener de la clase C G rados, com o se


m uestra a continuación:

return 9/5 * g r a d o s C + 32;

Después, com pile y ejecute la aplicación. Explique lo que sucede.

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

CLASES DE USO COMÚN


A unque las clases que hem os aprendido a escribir en los capítulos anteriores son
la base de nuestras aplicaciones, la potencia, en la práctica, del lenguaje Java vie­
ne dada por su biblioteca de clases. Hay dos paquetes que destacan p o r las clases
de propósito general que incluyen: ja v a .io y ja v a.lan g.

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

El paquete ja v a .la n g contiene clases que se aplican al lenguaje m ism o. Por


ejem plo, clases especiales que encapsulan los tipos prim itivos de datos, la clase
Sy ste m q ue proporciona los objetos para m anipular la entrada/salida (E/S) están­
dar, clases para m anipular cadenas de caracteres, una clase que proporciona los
m étodos correspondientes a las funciones m atem áticas de uso m ás frecuente, una
clase para analizar otras clases, etc.

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.

DATOS NUMÉRICOS Y CADENAS DE CARACTERES


La finalidad de una aplicación es procesar datos que, generalm ente, serán obteni­
dos de algún m edio externo p o r la propia aplicación (por ejem plo, del teclado o de
un fichero en disco) y procesados p o r la m ism a con el fin de obtener unos resulta­
dos. E stos datos se pueden clasificar en: num éricos y cadenas d e caracteres.
90 JA V A : C U R S O DE PROGRAM A CIÓN

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:

variable operador_de_asignación valor

U na sentencia de asignación es asim étrica. Esto quiere decir que se evalúa la


expresión de la derecha y el resultado se asigna a la variable especificada a la iz­
quierda. P o r ejem plo:

d = a + b * c: //el valor de a + b * c s e a s i g n a a d

Pero no sería válido escribir:

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:

double radio, área:


// .. .
área = 3.141592 * radio * radio:

Las cadenas de caracteres serán alm acenadas en objetos de la clase S t r in g o


en m atrices, cuyo estudio se ha pospuesto para un capítulo posterior. U n objeto de
la clase S t r in g se define y se le asigna un valor, así:

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

Lógicam ente, cuando la variable tiene asignado un valor y se le asigna uno


nuevo, el valor anterior es destruido y a que el valor nuevo pasa a ocupar la m ism a
localización de m em oria. En el ejem plo siguiente, se puede observar con respecto
a la situación anterior, que el contenido de n va r se m odifica con un nuevo valor
20, y que la referencia sv a r tam bién se m odifica; ahora contiene la referencia a un
nuevo objeto S t r in g "adiós” .

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.

La com unicación entre el origen de cierta inform ación y el destino, se realiza


m ediante un flu jo de inform ación (en inglés stream ).

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

U n flu jo es un objeto que hace de interm ediario entre el program a, y el origen


o el destino de la inform ación. Esto es, el program a leerá o escribirá en el flu jo sin
im portarle desde dónde viene la inform ación o a dónde va y tam poco im porta el
tipo de los datos que se leen o escriben. Este nivel de abstracción hace que el pro­
gram a no tenga que saber nada ni del dispositivo ni del tipo de inform ación, lo
que se traduce en una facilidad m ás a la hora de escribir program as.
92 JA V A: C U R SO DE PROGRAM A CIÓN

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.

El m étodo m ás im portante de esta clase es read. Este m étodo se presenta de


tres form as:

public int r e a d O th rows IO E x c e p tio n


public int r e a d { b y t e [ ] b ) th rows IO E x c e p tio n
public int r e a d ( b y t e [ ] b, i n t off. i n t len) t h r o w s I O E x c e p t i o n

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.

A nálogam ente, la clase R e a d e r es una clase abstracta que es superclase de


todas las clases que representan un flujo para leer caracteres desde un origen. Sus
94 JA V A : C U R SO DE PROGRAM A CIÓN

m étodos son análogos a los de la clase In p u tS tre a m , con la diferencia de que


utilizan parám etros de tipo c h a r en lugar de byte.

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.

El m étodo m ás im portante de esta clase es write. E ste m étodo se presenta de


tres formas:

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

b y t e [ ] b = new b y t e [ 1 2 8 1 ; II m a t r i z 'b ' de 128 b y t e s


flujoS.w rite(b ):

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.

A nálogam ente, la clase W r it e r es una clase abstracta que es superclase de to ­


das las clases que representan un flujo para escribir caracteres a un destino. Sus
C A PÍTU LO 5: CL A SES DE USO C O M Ú N 9 5

métodos son análogos a los de la clase O u tp u tS tre a m , con la diferencia de que


utilizan parám etros de tipo c h a r en lugar de byte.

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.

Las excepciones en Java son objetos de subclases de T h ro w a b le . Por ejem ­


plo, el paquete ja v a .io define una clase de excepción general denom inada IO E x -
eeption para excepciones de entrada salida.

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

En el ejem plo anterior, el m anejo de la excepción se ha reducido a visualizar


un m ensaje del erro r ocurrido.
96 JA V A: C U R SO DE PRO G R A M A CIÓ N

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.

Flujos estándar de E/S


La biblioteca de Java proporciona tres flujos estándar, m anipulados por la clase
S y ste m del paquete ja v a .Ia n g, que son autom áticam ente abiertos cuando se inicia
un program a y cerrados cuando éste finaliza:

• System .in. R eferencia a la entrada estándar del sistem a, que normalmente


coincide con el teclado. Se utiliza para leer datos introducidos p o r el usuario.

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

Los siguientes ejem plos ilustran la utilización de los flujos in y out:

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

El m étodo read devuelve un entero (in t) correspondiente al valor A SC II del


carácter leído. A hora este valor puede ser convertido a otro tipo com o byte:

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

Determinar la clase a la que pertenece un objeto

La clase O b je c t del paquete ja v a .la n g es la clase raíz de la jerarq u ía de clases de


Java. Esto quiere decir que el resto de las clases se deriva directa o indirectam ente
de esta clase, lo que a su vez significa que todas heredan todos sus m iem bros. Por
lo tanto, todos los objetos disponen de los m étodos proporcionados p o r Object.

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

La línea anterior indica que g e tC la s s devuelve un objeto de la clase C la ss,


O bjetoC lass, cuyos m étodos perm itirán obtener inform ación acerca de la clase del
objeto referenciado por cualquierO bjeto. Por ejem plo, el m étodo ge tN a m e de­
vuelve una cadena correspondiente al nom bre de la clase; g e tM e th o d s devuelve
una m atriz de la clase M e t h o d con los nom bres de todos los m étodos, etc.

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

El m étodo m a in de la aplicación anterior, prim ero solicita un dato que sera


introducido a través del teclado, después visualiza el dato, y finalm ente o b tien e)
visualiza los nom bres de las clases de los objetos correspondientes a los flujos
estándar. C uando ejecute la aplicación el resultado será sim ilar al siguiente:

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

La capacidad de conocer detalles de otras clases (incluidas las nuestras) a tra


vés de una clase habilitada por Java se conoce com o reflexión.

BufferedlnputStream

La clase B u ffe r e d ln p u tS tre a m se deriva indirectam ente de In p u tS tre a m , por lo


tanto hereda todos los m iem bros de ésta: por ejem plo, el m étodo re a d expuesto
anteriorm ente. Esta clase, aunque no aporta m étodos nuevos, sí aporta una carac­
terística muy interesante de la que se benefician todos sus m étodos: un buffer que
actúa com o una m em oria interm edia para lecturas futuras. Para entender esto ob­
serve la figura siguiente:

Según el esquem a anterior, cuando una aplicación ejecute una sentencia de


entrada (que solicite datos) los datos obtenidos del origen pueden ser depositados
en el buffer en bloques m ás grandes que los que realm ente está leyendo la aplica­
ción (por ejem plo, cuando se leen datos de un disco la cantidad m ínim a de infor­
m ación transferida es un bloque equivalente a una unidad de asignación). Esto
aum enta la velocidad de ejecución porque la siguiente vez que la aplicación nece­
site m ás datos no tendrá que esperar por ellos porque ya los tendrá en el buffer.
Por otra parte, cuando se trate de una operación de salida, los datos no serán en­
viados al destino hasta que no se llene el b uffer (o hasta que se fuerce el vaciado
del m ism o im plícita o explícitam ente), lo que reduce el núm ero de accesos al dis­
positivo físico vinculado que siem pre resulta m ucho m ás lento que los accesos a
m em oria, aum entado por consiguiente la velocidad de ejecución.

C uando el origen es el teclado y el destino el program a, el esquem a es el


m ism o. Esto perm ite introducir los datos por anticipado para una aplicación en
ejecución de la que se sabe que m ás adelante va a solicitarlos a través del teclado.
CA PÍTU LO 5: CL A SES DE U SO C O M Ú N 9 9

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

Bajo un flujo de la clase B u ffe re d R e a d e r subyace otro flujo de caracteres (objeto


de una clase derivada de R e ad e r), o bien de bytes (objeto de una clase derivada
de In p u tS tre a n i). Esto es, cada petición de lectura hecha a un flujo de la clase
B u ffe re d R e a d e r es dirigida a otro flujo de caracteres o de bytes subyacente.

} ( Flujo \ r x
__________ I F |ui ° | L O rig en

s u b y a c e n te r

V J V J

La figura anterior traducida a código dependiente de los flujos m encionados


en el párrafo anterior, puede interpretarse así:

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

La clase In p u t S t r e a m R e a d e r establece un puente para pasar flujos de bytes


a flujos de caracteres.

c a ra c te re s bytes

La clase B u ffe re d R e a d e r proporciona m étodos análogos a B u ffe r e d ln p u t­


Stre am , y adem ás otros com o re ad Lin e . Este m étodo perm ite leer una línea de
texto que devuelve en un objeto de la clase S trin g . Se entiende por línea de texto
la cadena form ada por los caracteres que hay hasta encontrar uno de los siguien­
tes: V ’, 'Vi' o am bos; estos caracteres son leídos pero no alm acenados.
1 0 0 JA VA : C U R SO DE PROGRAM A CIÓN

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

A nalicem os el m étodo m a in de la aplicación anterior. Prim eram ente define


un flujo de entrada, flu jo E , del cual se podrán leer líneas de texto. Después, se
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. La últim a parte del cuerpo de m a in lee
una línea de texto introducida a través del teclado y la visualiza.

PrintStream

La clase P rin tS tr e a m se deriva indirectam ente de O u tp u tS tre a m , por lo tanto


hereda todos los m iem bros de ésta; por ejem plo el m étodo w rite expuesto ante­
riorm ente. O tros m étodos de interés que aporta esta clase, que ya hem os utilizado,
son; p rin t y p rin tln . La sintaxis para estos m étodos es la siguiente:

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:

System .out.p rintí"El valor no p u e d e s e r n e g a t i v o \ n " ):


C A P ÍT U L O 5: CL A SES DE U SO C O M Ú N 101

es equivalente a esta otra:

System .out.println("El v a l o r no p u e d e s e r n e g a t i v o " ) :

En el ejem plo anterior, se puede observar que p r in t añade al final de la cade­


na de caracteres un carácter 'Vi' que p rin tln no añade.

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

Los resultados que produce la aplicación anterior son los siguientes:

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

O bserve que se puede im prim ir un objeto; el p rim er m étodo p r in tln imprime


un objeto S trin g . C uando se utiliza p r in t o p rin tln para im prim ir un objeto, el
dato im preso depende del tipo del objeto. En el ejem plo se puede observar que la
im presión de un objeto S t r in g hace que se im prim a la cadena de caracteres que
alm acena. S in em bargo, la im presión de un objeto C la s s daría lugar a que se im­
prim iera una cadena de caracteres correspondiente al nom bre de la clase del ob­
jeto que estuviera siendo inspeccionado.

La clase análoga a P rin tS tr e a m es P rin tW r ite r, clase derivada de YVriter,


pero los m étodos proporcionados por am bas son prácticam ente los m ism os, por lo
que no com entarem os esta últim a. U na diferencia entre am bas es que cuando se
ejecuta un m étodo de P rin tS tre a m , el b uffer de salida es vaciado autom ática­
m ente (los datos se m uestran), no sucediendo lo m ism o con P r in t W r ite r; en este
caso habría que forzar el vaciado del b uffer de salida invocando a su método
flush. P or ejem plo:

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

Trabajar con tipos de datos primitivos


H agam os un breve recorrido por la jerarq u ía de clases vista desde un esquema
gráfico para ver dónde se sitúan las que hem os com entado;
C A P ÍT U L O 5: CL A SES DE USO COM ÚN 103

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.

Clases que encapsulan los tipos prim itivos

El paquete ja v a .Ia n g proporciona las clases Byte, C h a ra c te r, Sh o rt. Integer,


L o n g , Flo at, D o u b le y Boolean, que encapsulan cada uno de los tipos prim itivos,
proporcionando así una funcionalidad añadida para m anipularlos. Analicem os,
por ejem plo, la clase Integer.

Un objeto de la clase In te g e r contiene un atributo de tipo int, que es el obje­


tivo de la clase. A dem ás, proporciona otros atributos y varios m étodos útiles para
tratar con un entero; por ejem plo, para convertir un int en un S t r in g o un S t r in g
en un int. A lgunos de ellos son los siguientes:
1 0 4 JA V A: C U R SO DE PROGRAM A CIÓN

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.

El resto de las clases tienen m étodos análogos. N o obstante, es necesario re­


saltar que las clases Flo at y D o u b le no tienen un m étodo parse... En cam bio, in­
cluyen otros atributos que pueden observarse en la tabla siguiente. Por ejemplo,
para la clase F lo at (ídem para la clase D ou b le ):

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:

1. D efinir un flujo de entrada de la clase Bu ffe re d R e ad e r.


2. L eer la cadena de caracteres.

3. C onvertir el objeto S t r in g en un entero.

El siguiente código corresponde a los puntos enunciados:

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

En el ejem plo anterior se observa que al no disponer ia clase F lo a t de un


m étodo análogo a p a rse ln t, se ha tenido que recurrir a crear un objeto F lo a t a
partir de la cadena de caracteres, para después obtener el v alo r float que encap-
sula dicho objeto.

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

Partiendo de la cadena devuelta por el m étodo anterior, podem os escribir


tam bién, por ejem plo, un m étodo d a to ln t que la convierta en un núm ero entero y
devuelva este valor com o resultado:

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

Si el valor devuelto p o r el m étodo dato no fuera válido para convertirlo en un


núm ero entero, el m étodo p a rse ln t lanzaría una excepción de tipo Num berFor-
m a tE x c e p tio n que podríam os m anejar. Para no com plicar el tem a que estamos
exponiendo, y puesto que dedicarem os un capítulo posterior a explicar las excep­
ciones, si se lanza una excepción del tipo descrito, el m étodo sim plem ente devol­
verá un valor significativo (una constante m iem bro de la clase). Según esto
podríam os m odificar el m étodo anterior así:

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

public class Leer


1
public static String da t o ( )
I
String 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 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 );
// 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 t ):
I
catch(IOException 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 í )):
I
return sdato: // d e v o l v e r el dato te cl ea d o

public static short datoShortO


I
try
I
return S h o r t .pars eS h o rt( da to ( )):
I
c a t c h t N u m b e r F o r m a t E x c e p t i o n e)
I
return S h o r t . MI N _ V A L U E ; // v a l o r rnás p e q u e ñ o

public static int datoíntO


I
try
I
return I n t e g e r . p a r s e l n t ( d a t o ( )):
I
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 I n t e g e r . M I N _ V A L U E ; // v a l o r más p e q u e ñ o
108 JA V A: C U R SO DE PROGRAM A CIÓN

public static long datoLon gO


I
try
I
return Long.pars eLongtd ato()):
I
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 Long.MIN_VALUE; // v a l o r más pe q u e ñ o

public static float datoFloatO


I
try
I
F l o a t f = new F l o a t ( d a t o ( ) ) ;
r e t u r n f . f l o a t V a l u e ( ):
')
c a t c h i N u m b e r F o r m a t E x c e p t i o n e)
I
return Float.NaN; // No e s un Númer o; valor float.

public static double da t o Do u bl e O


{
try
I
D o u b l e d = new D o u b l e ( d a t o ( ) ) :
r e t u r n d . d o u b l e V a l u e t ):
1
c a t c h i N u m b e r F o r m a t E x c e p t i o n e)
I
return Double.NaN; // No e s un Nú me r o ; valor double.
I
I
I

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:

nom breC lase.nom breM étodo

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

public class LeerDatos


I
public static void m a in (S trin g [] args)
I
s h o r t d a t o _ s h o r t = 0;
i n t d a t o _ i n t - 0:
l o n g d a t o _ l o n g - 0:
f l o a t d a t o _ f l o a t = 0:
d o u b l e d a t o _ d o u b l e = 0:

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.

¿DÓNDE SE UBICAN LAS CLASES QUE DAN SOPORTE?


Para que Jav a pueda utilizar una clase debe conocer dónde está alm acenada en el
sistem a de ficheros. De otra form a, cuando se com pile el program a se obtendrá un
erro r indicando que esa clase no existe. Java utiliza dos elem entos para localizar
las clases: el nom bre del paquete y las rutas especificadas por la variable CLASS-
PATH.
1 10 JA V A: CU R SO D E PRO G R A M A CIÓ N

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.

Si recuerda, en el capítulo 1, al explicar cóm o se com pilaba y ejecutaba un


program a, se dijo que había que establecer la variable de entorno PATH. Pues
bien, para establecer la variable C LA SSP A TH proceda de form a análoga. Por
ejem plo, si alm acenam os las clases que vayan a ser com partidas por otros pro­
gram as, com o es el caso de la clase Leer, en la carpeta jd k l.3 \m isC la se s, asigne a
la variable C LA SSP A TH esta ruta:

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.

CARÁCTER FIN DE FICHERO

Desde el punto de vista de un usuario de una aplicación, un dispositivo de entrada


o de salida estándar es tratado por el lenguaje Java com o si de un fichero de datos
en el disco se tratara. Un fichero de datos no es m ás que una colección de infor­
m ación. Los datos que introducim os por el teclado son una colección de inform a­
ción y los datos que visualizam os en el m onitor son tam bién una colección de
inform ación.
F in a l del
.. iNambie ' fichero
.. Ihlambm__
..iMomliie___
IN ü m L t e _____
Nombre
Acidos
Principio del
------- ' Dirección
fichero

T odo fichero tiene un principio y un final. ¿C óm o sabe un program a que está


leyendo datos de un fichero, que se ha llegado al final del m ism o y por lo tanto no
hay m ás datos? P or una m arca de fin de fichero. En el caso de un fichero grabado
en un disco esa m arca estará escrita al final del m ism o. En el caso del teclado la
inform ación procede de lo que nosotros tecleam os, por lo tanto si nuestro progra­
ma requiere detectar la m arca de fin de fichero, tendrem os que teclearla cuando
dem os por finalizada la introducción de inform ación. Esto se hace pulsando las
teclas C trl+ D en UNIX o C trl+ Z en una aplicación de consola en W indows.
CA PÍTU LO 5: CL A SES D E U SO COM ÚN 111

Y a que un fichero o un dispositivo siem pre es m anejado a través de un flujo,


hablar del final del flujo es sinónim o de hablar del fichero. P or eso, de ahora en
adelante nos referirem os al flujo en lugar de al fichero o dispositivo vinculado.

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

public class Test


I
public static void m ain (S tring [] args)
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 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 l u j o S = System.out:

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:

1. Introduciendo un dato válido:

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

2. Pulsando las teclas C írl+ Z (m arca de final del flujo):

P r e c i o : ( s e p u l s a n las teclas Ctrl+¿)


NaN
Continua la a p l i c a c i ó n

U na aclaración. L a aplicación anterior utiliza el operador tem ario (?:) para


verificar si se llegó al final del flujo, lo que sucederá cuando se pulsen las teclas
Ctrl+Z. La expresión booleana sdato != nuil será true si se introdujo un dato vá­
lido, en cuyo caso se asignará a precio el resultado de la expresión (new
F!oat(sdato)).floatValue()\ y será false si se pulsaron las teclas C trl+ Z, en cuyo
caso se asignará a precio el valor NaN.

La expresión (new Float(sdato)).floatV alue() es equivalente a:

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 :

En capítulos posteriores utilizarem os C trl+ Z com o condición para finalizar la


entrada de un núm ero de datos, en principio indeterm inado.

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

C uando se ejecute el m étodo read de la aplicación anterior, si tecleam os la


opción b y pulsam os la tecla Entrar.

b [ E n tr a r ]
CA PÍTU LO 5: CLASES DE USO C O M Ú N 1 13

antes de la lectura, el buffer de entrada contendrá la siguiente inform ación:

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

flu jo S .p rin t í"P re c io : "):


s d a t o = 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 l n í " C o n t i n u a la aplicación"): 1
I
catch (IOException ignorada)! 1

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:

Opción (a, b o c): b


Precio: Continua la a p l i c a c ió n

A la vista del resultado, se observa que cuando se ejecutó re a d L in e no se


detuvo la ejecución de la aplicación para introducir el dato solicitado ¿P or qué?
Porque los caracteres sobrantes \r y Vi son válidos para el m étodo re a d L in e . R e­
cuerde q ue este m étodo perm ite leer una cadena de caracteres hasta encontrar V
(CR), 'Vi' (L F ) o W i ' (C R LF); estos caracteres son leídos pero no alm acenados.
P o r este m otivo es p o r lo que este m étodo no necesita esperar a que introduzca­
mos un carácter para la variable sdato. R ecuerde tam bién, que cuando explicam os
anteriorm ente las clases B u ffe r e d ln p u tS tre a m y B u ffe re d R e a d e r dijim os que
un buffer perm itía, entre otras cosas, introducir los datos por anticipado. En este
caso, nuestra intención no era ésa. pero la form a en la que hem os introducido un
dato nos han conducido a ello.

La solución al problem a planteado es lim piar los caracteres indeseables del


buffer de entrada. Hay dos form as sencillas de hacer esto. U na es utilizar el propio
m étodo r e a d L in e para hacer una lectura “en falso” , con la única intención de ex­
traer todos los caracteres que haya, y otra es utilizar los m étodos sk ip y available.
1 1 4 JA V A: C U R SO D E PRO G R A M A CIÓ N

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

flu jo S.p rin t("P re cio : "):


s d a t o = f 1u j o E . r e a d L i n e ( ):
f l u j o S . p r i n t l n ( " C o n t i n u a l a a p i i c a c i ó n " ):
I
catch (IOException ignorada)! I

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 clase M a t h contiene m étodos para ejecutar operaciones num éricas ele­


m entales tales com o raíz cuadrada, exponencial, logaritm o, y funciones trigono­
m étricas. Por ejem plo:

double raíz_cuadrada. n = 345.0;


r a i z _ c u a d r a d a = M a t h . s q r t ( n ):
S y s t e m . o u t . p r i n t l n ( " L a r a i z cua dr a da de " + n + “ es " + r a i z _ c u a d r a d 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

double P I V alor del núm ero n.


tip o a b s (tipo a) V alor absoluto de a. El tipo , igual en todos los
casos, puede ser: double, float, int o long.
double eeil(double a) V alor d o u b le sin decim ales m ás pequeño que
es m ayor o igual que a.
double flo o ríd o u b le a) V alor d o u b le sin decim ales m ás grande que es
m enor o igual que a.
tipo m a \(tip o a , tipo b) V alor m ayor de a y b. El tipo, igual en todos
los casos, puede ser: double. float, in t o long.
tipo m in (/íp o a , tipo b) V alor m enor de a y b. El tipo, igual en todos
los casos, puede ser: double. float. in t o long.
double r a n d o m O V alor aleatorio m ayor o igual que 0.0 y m enor
que LO.
double rin t(d o u b le a) V alor d o u b le sin decim ales m ás cercano a a
(redondeo de a).
long ro u n d íd o u b le a) V alor lo n g m ás cercano a a.
int ro u n d (flo a t a ) V alor in t m ás cercano a a.
double sq rt(d o u b le a) R aíz cuadrada de a (a no puede ser negativo).
double exp(double a) V alor de e“.
double log(double «) L ogaritm o neperiano (natural) de a.
double pow (double a, double b) V alor de a'}.
double IE E E re m a in d e r(
d o u b le/ / , double f2 )
Resto de una división entre núm eros reales:
c = fl/f2 , siendo c el valor entero m ás cercano
al valor real de fJ/J2; por lo tanto, el resto pue­
de ser positivo o negativo.
double aeosfdouble a) A rco, de 0 .0 a n , cuyo coseno es a.
double asin (d o u b le a) A rco, de -n/2 a n /2 , cuyo seno es a.
double a tan (d o u b le a) A rco, de -n/2 a Tt/1, cuya tangente es a.
double atan 2 (d o u b le a, double b) C onvierte las coordenadas rectangulares (b , a)
a polares: (r, 0).
double sin(double a) Seno de a radianes.
double cos(double a) C oseno de a radianes.
double tan (d o u b le a) T angente de a radianes.
double to D eg reesfdouble rads) C onvertir un ángulo en radianes a grados.
double to R ad ian sfd o u b le grados) C onvertir un ángulo en grados a radianes.
1 1 6 JA V A: C U R SO D E PROGRAM A CIÓN

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.

La fórm ula utilizada para el cálculo de los intereses es:

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.

La solución de este problem a puede ser de la siguiente form a:

• Prim ero definim os las variables que vam os a u tilizar en los cálculos.

double c, intereses, capital:


f l o a t r;
i n t t;

• A continuación leem os los datos c, r y 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 X a n u a l d e l : " ) :
r = L e e r . d a t o F l o a t t );
System.o ut.p r i n t ( "\nDurante cuántos d ías : ” ):
t = L e e r . d a t o l n t í ):

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

• Finalm ente, escribim os el resultado.

System .out.printlnt"Intereses producidos... " + intereses);


S y s t e m . o u t . p r i n t l n ( " C a p i t a l acumulado " + capital):
CA PÍTU LO 5: CLASES D E USO C O M Ú N 1 17

O bserve que el desarrollo de una aplicación, en general, consta de tres blo­


ques colocados en el siguiente orden:

ENTRADA PROCESO S A L ID A |

La aplicación com pleta se m uestra a continuación. O bservar q ue se ha utiliza­


do para la entrada de datos los m étodos de la clase L eer im plem entada anterior­
m ente en este m ism o capítulo.

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

System.out.printlní"Intereses producidos... " + intereses):


S y s tem .o ut.p r i n t í n ( " C a p i t a l acumulado " + capital);
I

2. R ealizar u na aplicación que dé com o resultado las soluciones reales x , y x 2 de una


ecuación de segundo grado, de la forma:

ax2 + bx + c = O

Las soluciones de una ecuación de segundo grado vienen dadas p o r la fórmula:

- b ± y l b : —4 a c
1 18 JAVA: C U R SO DE PROGRAM A CIÓN

Las soluciones son reales sólo si b 2-4 a c es m ayor o igual que 0. C on lo


aprendido hasta ahora, la solución de este problem a puede desarrollarse de la
form a siguiente:

• Prim ero definim os las variables necesarias para los cálculos:

d o u b l e a. b, c. d. xl, x2:

• A continuación leem os los coeficientes a , b y c de la ecuación:

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

• Si hay raíces reales las calculam os aplicando la fórm ula.

d = Math.sqrt(d):
x l = ( - b + d) / ( 2 * a ) :
x2 = ( - b - d) / ( 2 * a ) :

El m étodo s q r t calcula la raíz cuadrada de su argum ento. En el ejem plo, se


calcula la raíz cuadrada de d y se alm acena el resultado de nuevo en d.

• Por últim o escribim os los resultados obtenidos.

System.out.printlní"xl = " + xl + ", x2 = " + x 2 ) ;

La aplicación com pleta se m uestra a continuación:


CA PÍTU LO 5: CLASES DE USO COM ÚN 1 19

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

3. Realizar una aplicación que evalúe el polinom io


p = 3x5 - 5 x 3 + 2.x - 7
y visualizar el resultado con el siguiente form ato:
Para x = valor, 3 x A 5 - 5 x A3 + 2 x - 7 = resultado
1 2 0 JA V A: C U R SO DE PROGRAM A CIÓN

4. R ealizar la m ism a aplicación anterior, pero em pleando ahora coeficientes varia­


bles a, b y c.
5. Ejecute la siguiente aplicación, explique io que ocurre y realice las m odificacio­
nes que sean necesarias para su correcto funcionam iento.
i mport j a v a . i o . * :

public class Test


I
public static void m ain(S tring [] args)
I
1n 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 ) :
PrintStream f l u j o S = System.out;
c h a r c a r = 0;
try
I
flu joS.print("Carácter: "):
c a r = ( c h a r ) f l u j o E . r e a d í );
flujoS.println(car);
flu joS.print("Carácter: "):
c a r = ( c h a r ) f 1 u j o E . r e a d ( ):
flujoS.println(car);
I
catch(IOException ignorada) (I
)

6. Indique qué resultado da la siguiente aplicación. A continuación ejecute la aplica­


ción y com pare los resultados.
i mport j a v a . i o . * :

public class Test


I
public static void m a in(S tring [] args)
I
PrintStream f l u j o S = System.out:

char c a r i = ’A ’ , c a r 2 - 65, c a r 3 = 0:

car3 = ( c h a r X c a r l + ‘a ’ - ' A ') ¡


flujoS.println(car3 + " " + ( i n t ) c a r 3);
car3 = ( c h a r )(c a r 2 + 32):
flujoS.p r i n t l n ( c a r 3 + “ " + ( i n t ) c a r 3 );
CA PÍTU LO 6
© FJ. Ccb/tUos/RA-MA

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.

A sí m ism o, en m ás de una ocasión necesitarem os ejecutar un conjunto de


sentencias un núm ero determ inado de veces, o bien hasta que se cum pla una con­
dición im puesta por nosotros. Por ejem plo, en el capítulo anterior hem os visto
cóm o leer un carácter de la entrada estándar. Pero si lo que querem os es leer, no
un carácter sino todos los que escribam os por el teclado hasta detectar la m arca de
fin de fichero, tendrem os que utilizar una sentencia repetitiva.

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

donde condición es una expresión booleana, y sentencia I y sentencia 2 repre­


sentan a una sentencia sim ple o com puesta. C ada sentencia sim ple debe finalizar
con un punto y com a.

U na sentencia if se ejecuta de la form a siguiente:

1. Se evalúa la condición.

2. Si el resultado de la evaluación de la condición es verdadero (true) se ejecuta­


rá lo indicado por la sentencia 1.

3. Si el resultado de la evaluación de la condición es falso (false), se ejecutará lo


indicado por la sentencia 2, si la cláusula else se ha especificado.

4. Si el resultado de la evaluación de la condición es falso, y la cláusula else se


ha om itido, la sentencia I se ignora.

5. E n cualquier caso, la ejecución continúa en la siguiente sentencia ejecutable


que haya a continuación de la sentencia if..

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:

En este ejem plo, la condición viene im puesta por la expresión x != 0. Enton­


ces b = a / x , que sustituye a la sentencia 1 del form ato general, se ejecutará si la
expresión es verdadera (x distinta de 0) y no se ejecutará si la expresión es falsa
(x igual a 0). En cualquier caso, se continúa la ejecución en la línea siguiente, b =
b + 1. V eam os otro ejem plo:

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

En el ejem plo siguiente, la condición viene im puesta p o r la expresión a != 0


&& b != 0. Si al evaluar la condición se cum ple que a y b son distintas de cero,
entonces se ejecuta la sentencia x = i. En otro caso, la sentencia x = i se ignora,
continuando la ejecución en la línea siguiente.

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

En este caso, suponiendo p o r ejem plo que a es de tipo in t, el com pilador


m ostrará un m ensaje de error indicando que no puede convertir un in t a boolean,
porque la sentencia anterior es equivalente a escribir:

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

En este otro ejem plo que se m uestra a continuación, la sentencia r e tu r n se


ejecutará solam ente cuando c a r sea igual al carácter V .

if (car == 's ')


return:

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:

Al evaluarse las condiciones anteriores, pueden presentarse los casos que se


indican en la tabla siguiente:

condición I condición 2 se ejecuta: sentencia I sentencia 2


F F no sí
F V no sí
V F no no
V V sí no

(V = verdadero, F = falso, no = no se ejecuta, sí = sí se ejecuta)

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;

A hora podríam os dudar de a qué if pertenece la cláusula else. C uando en el


código de un program a aparecen sentencias i f ... else anidadas, la regla para dife­
renciar cada una de estas sentencias es que “cada else se corresponde con el if
más próxim o que no haya sido em parejado". Según esto la cláusula else está em-
C A P ÍT U L O 6: SEN TEN CIA S DE CO N TRO L 125

parejada con el segundo if. Entonces, al evaluarse ahora las condiciones I y 2,


pueden presentarse los casos que se indican en la tabla siguiente:

condición I condición 2 se ejecuta: sentencia 1 sentencia 2


F F no no
F V no no
V F no sí
V V sí no

(V = verdadero, F = falso, no = no se ejecuta, sí = s í se ejecuta)

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.

A sí m ism o, si en el ejem plo siguiente ocurre que a no es igual a 0, la ejecu­


ción 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

Si en lugar de la solución anterior, lo que deseam os es que se ejecute s = s +


a cuando a no es igual a 0, entonces tendrem os que incluir entre llaves el segundo
if sin la cláusula else; esto es:

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

C om o ejercicio sobre la teoría expuesta, vam os a realizar una aplicación que


dé com o resultado el m enor de tres núm eros a, b y c. La form a de proceder es
com parar cada núm ero con los otros dos una sola vez. L a sim ple lectura del códi­
go que se m uestra a continuación es suficiente para entender el proceso seguido.

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

public static void m a in ( S t r in g [ ] args)


I
float a. b. c. me n o r ;

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

La evaluación de esta estructura sucede así: si se cum ple la condición 1, se


ejecuta la sentencia 1 y si no se cum ple se exam inan secuencialm ente las condi­
ciones siguientes hasta el últim o else, ejecutándose la sentencia correspondiente al
prim er else if, cuya condición sea cierta. Si todas las condiciones son falsas, se
ejecuta la sentencia n correspondiente al últim o else. En cualquier caso, se conti­
núa en la prim era sentencia ejecutable que haya a continuación de la estructura.
Las sentencias 1, 2 ,.... n pueden ser sentencias sim ples o com puestas.

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

La solución de este problem a puede ser de la form a siguiente:

• 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 continuación leem os los datos ar, cc y pu.

System .out.print("Código a r t i c u l o ........... ">


128 JA V A: C U R SO DE PROGRAM A CIÓN

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

• C onocidos los datos, realizam os los cálculos y escribim os el resultado.

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 :

System .out.print("Código a r t ic u l o ")


a r = L e e r . d a t o l n t í ):
S y s t e m . o u t .p r in t ( " C a n t id a d comprada ")
c c = 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 ( " 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 í ):
S y s t e m . o u t . p r i n t l n ( );

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

donde expresión es una expresión entera de tipo c h a r, b y te, s h o rt o in t y expre­


sión-constante es una constante tam bién entera y de los m ism os tipos. T anto la
expresión com o las expresiones constantes son convertidas im plícitam ente a int.
Por últim o, sentencia es una sentencia sim ple o com puesta. En el caso de tratarse
de una sentencia com puesta, no hace falta incluir las sentencias sim ples entre {}.

L a sentencia sw itch evalúa la expresión entre paréntesis y com para su valor


con las constantes de cada case. La ejecución de las sentencias del bloque de la
sentencia sw itch , com ienza en el case cuya constante coincida con el valor de la
expresión y continúa hasta el final del bloque o hasta una sentencia que transfiera
el control fuera del bloque de sw itch ; por ejem plo, b re a k . La sentencia sw itch
puede incluir cualquier núm ero de cláusulas case.

Si no existe una constante igual al valor de la expresión, entonces se ejecutan


las sentencias que están a continuación de d e fa u lt, si esta cláusula ha sido especi­
ficada. La cláusula d e fa u lt puede colocarse en cualquier parte del bloque y no ne­
cesariam ente al final.
1 3 0 JA V A: C U R SO DE PRO G R A M A CIÓ N

En una sentencia sw itch es posible hacer declaraciones en el bloque de cada


case, igual que en cualquier otro bloque, pero no al principio del bloque switch,
antes del prim er case. Por ejem plo:

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

El erro r que se ha presentado en el ejem plo anterior puede solucionarse así:

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.

La solución de este problem a puede ser de la siguiente forma:

• 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

• A continuación leem os los datos m es y año.

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

• D espués com param os el m es con las constantes 1, 2, ..., 12. Si m es es 1, 3, 5,


7, 8, 10 ó 12 asignam os a d ía s el valor 31. Si m es es 4, 6, 9 u 11 asignam os a
días el valor 30. Si m es es 2, verificarem os si el año es bisiesto, en cuyo caso
asignam os a días el valor 29 y si no es bisiesto, asignam os a d ía s el valor 28.
Si m es no es ningún valor de los anteriores enviarem os un m ensaje al usuario
indicándole que el m es no es válido. T odo este proceso lo realizarem os con
una sentencia switch.

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.

• Por últim o si el m es es válido, escribim os el resultado solicitado.

if ( mes > - 1 && mes < = 1 2 )


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 +
" t i e n e " + d i a s + ’ d i a s ’ ):

El program a com pleto se m uestra a continuación:


13 2 JAVA: C U R SO DE PROGRAM A CIÓ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 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

public static void m a in(S tring [] args)


I
int días = 0 . mes - 0. a ñ o = 0:

S y s t e m . o u t . p r i n t ( " Me s ( # ) : " ) : mes - L e e r . d a t o l n t t ):


System.out.print("Año ( IHHHt): “ ) : a ñ o = L e e r . d a t o l n t ( );

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.

La sentencia b re a k que se ha puesto a continuación de la cláusula d efault no


es necesaria; sim plem ente obedece a un buen estilo de program ación. A sí, cuando
tengam os que añadir otro caso ya tenem os puesto bre ak. con lo que hem os elim i­
nado una posible fuente de errores.

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:

donde condición es cualquier expresión booleana y sentencia es una sentencia


sim ple o com puesta.

La ejecución de la sentencia w hile sucede así:


1. Se evalúa la condición.
2. Si el resultado de la evaluación es false (falso), la sentencia no se ejecuta y se
pasa el control a la siguiente sentencia en el program a.
3. Si el resultado de la evaluación es true (verdadero), se ejecuta la sentencia y
el proceso descrito se repite desde el punto 1.

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 - '\0’:


try
I
S y s t e m . o u t . p r i n t ( " \ n O e s e a c o n t i n u a r s / n ( s i o n o ) “ ):
c a r » ( c h a r ) S y s t e m . i n . r e a d ( );
// 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 v a i l a b l e O ) :
w h i l e ( c a r ! = ’ s ‘ && c a r ! = ’ n ' )
I
S y s t e m . o u t . p r in t ( "\nDesea c o n tin u a r s/n (si o no) ");
c a r - ( c h a r ) S y s t e m . i n . r e a d ( ):
S y s t e m . i n . s k i p ( S y s t e m . i n . a v a i 1a b l e ( ) ) ;

catch UOException ignorada) 11


13 4 JA V A : C U R SO D E PROGRAM A CIÓN

Observe que antes de ejecutarse la sentencia w hile se visualiza el mensaje


“D esea continuar s/n (sí o no)” y se inicia la condición; esto es, se asigna un ca­
rácter a la variable c o r que interviene en la condición de la sentencia while.

L a sentencia w hile se interpreta de la form a siguiente: m ientras el valor de


ca r no sea igual ni al carácter V ni al carácter V , visualizar el m ensaje “Desea
continuar s/n (sí o no)” y leer otro carácter. Esto obliga al usuario a escribir el ca­
rácter ‘s ' o 'n en m inúsculas.

El ejem plo expuesto, puede escribirse de form a m ás sim plificada así:

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

public class CAscii


I
// C ó d i g o A S C I I de c a da uno de l o s c a r a c t e r e s de un t e x t o
public s t a t i c void m a in ( S t r i n g [ ] args)
I
c h a r c a r = 0: // c a r = c a r á c t e r n u l o (\0)

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

A continuación ejecutam os la aplicación. Introducim os, p o r ejem plo, el ca­


rácter ‘a ' y observam os los siguientes resultados:

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 :

Este resultado dem uestra que cuando escribim os ‘a ’ y pulsam os la tecla E n ­


tra r para validar la entrada, sólo se visualiza el código A SC II de ese carácter; los
caracteres V y Vi introducidos al pulsar E ntrar son ignorados porque así se ha
program ado. C uando se han leído todos los caracteres del flujo de entrada, se so­
licitan nuevos datos. Lógicam ente, habrá com prendido que aunque se lea carácter
a carácter se puede escribir, hasta pulsar E ntrar, un texto cualquiera. P or ejem plo:

Introduzca una c a d e n a de t e x t o : hola[Entrar]


El código ASCII de h e s 104
El código ASCII de o e s 111
El código ASCII de 1 e s 108
El código ASCII de a e s 97
Introduzca una cadena de 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 . * ;

public class CAsci i


I
// C ó d i g o A S C I I de c a d a uno de l o s c a r a c t e r e s de un t e x t o
public s t a t i c void m a in(String[] args)
I
final char eof = ( c h a r ) - l;
c h a r c a r = 0 ; // c a r = c a r á c t e r nulo (\0)
try
I
S y s t e m . o u t . p r i n t l n ( " I n t r o d u z c a una c a d e n a d e t e x t o . " ) :
Syste m .o u t .p rin tln ! "P a ra terminar pulse 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 ( )) ! - e of)
I
if ( 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 ) c a r ) ;

catch(IOException ignorada) II

U na solución posible de esta aplicación es la siguiente:

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

public s t a t ic void m ain (S trin g[] args)


I
int i = 1. j = 1:
while ( i < - 3 ) // m i e n t r a s i sea menor o i g u a l que 3
I
System .out.print("Para i = “ + i +
w h i l e ( j <= 4 ) // m i e n t r a s j sea menor o i g u a l que 4
I
S y s t e m . o u t . p r i n t C j - * + j + ",
j + + ; // aumentar j en una u n id ad
I
S y s t e m . o u t . p r i n t l n ( ); // a v a n z a r a una nueva l i n e a
i + + : // aumentar i en una u n id ad
j = 1; // i n i c i a r j de n u ev o a 1

Al ejecu tar este m étodo se obtiene el siguiente resultado:

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.

C om o aplicación de lo expuesto, vam os a realizar un program a que im prim a


los núm eros z, com prendidos entre 1 y 50, que cum plan la expresión:

r = x2 + y 2

donde z, x e y son núm eros enteros positivos. El resultado se presentará de la for­


ma siguiente:

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

L a solución de este problem a puede ser de la siguiente form a:

• Prim ero definim os las variables que vam os a utilizar en los cálculos.

1 n t x - l . y - l . z - 0 :

• A continuación escribim os la cabecera de la solución.

System.out.p r i n t l n ( " Z \ t " + " X \ t " + "Y");


S y s t e m , o u t . p r i n t l n ( " _________________________ " ) ;

• D espués, para x = 1, e y = 1 , 2 ,3 , .... para x = 2, e y = 2, 3, 4, .... para x = 3,


e y = 3 ,4 , .... hasta x = 50, calculam os la y j x 2 + y 2 ; llam am os a este valor z
(observe que y es igual o m ayor que x para evitar que se repitan pares de valo­
res com o x= 3, y= 4 y x= 4, y= 3). Si z es exacto, escribim os z, x e y . Esto es.
para los valores descritos de x e y, hacem os los cálculos:

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.

El program a com pleto se m uestra a continuación:

public class CPitagoras


I
// Teo r ema d e P i t á g o r a s
public s t a t i c void m a in ( S tr in g [] args)
1
i n t x = l . y = l , z = 0:
System .out.println(’Z\t" + " X\t ” + "Y");
S y s t e m , o u t . p r i n t l n( “__________________________ " ) ;

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

SENTENCIA do ... while


La sentencia d o ... w hile ejecuta una sentencia, sim ple o com puesta, una o más
veces dependiendo del valor de una expresión. Su sintaxis es la siguiente:

do
s e n te n c ia -,
wh i l e ( c o n d i c i ó n );

donde condición es cualquier expresión booleana y sentencia es una sentencia


sim ple o com puesta. O bserve que la estructura d o ... w hile finaliza con un punto y
com a.

La ejecución de una sentencia d o ... w h ile sucede de la siguiente form a:


1. Se ejecuta el bloque (sentencia sim ple o com puesta) de do.

2. Se evalúa la expresión correspondiente a la condición de finalización del bu­


cle.

3. Si el resultado de la evaluación es false (falso), se pasa el control a la si­


guiente sentencia en el program a.
4. Si el resultado de la evaluación es tr u e (verdadero), el proceso descrito se re­
pite desde el punto 1.

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

C uando se utiliza una estructura d o ... w hile el bloque de sentencias se ejecuta


al m enos una vez, porque la condición se evalúa al final. En cam bio, cuando se
140 JAVA: C U R SO D E PROGRAM A CIÓN

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.

C om o ejercicio, vam os a realizar un program a que calcule la raíz cuadrada de


un núm ero n por el m étodo de N ew ton. Este m étodo se enuncia así: sea r, la raíz
cuadrada aproxim ada de n. La siguiente raíz aproxim ada r(+; se calcula en función
de la anterior así:

rM =

El proceso descrito se repite hasta que la diferencia en v alo r absoluto de las


dos últim as aproxim aciones calculadas, sea tan pequeña com o nosotros queramos
(teniendo en cuenta los lím ites establecidos por tipo de datos utilizado). Según
esto, la últim a aproxim ación será una raíz válida, cuando se cum pla que:

abs(r¡ ■ ru , ) < £

La solución de este problem a puede ser de la siguiente forma:

• 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

• A continuación leem os los datos n , aprox y epsilon.

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

• D espués, se aplica la fórm ula de Newton.

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.

• C uando la condición especificada en la estructura d o ... w hile m ostrada ante­


riorm ente sea falsa, el proceso habrá term inado. S ólo queda im prim ir el re­
sultado.

System.out.println("La raíz c u a d r a d a de ” + n + ” e s " + aprox);

El program a com pleto se m uestra a continuación. Para no perm itir la entrada


de núm ero negativos, se ha utilizado una estructura d o ... w hile que preguntará
por el valor solicitado m ientras el introducido sea negativo.

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


p u b l i c c l a s s CRaizCuadrada
I
// R a í z cuadrada. M é t o d o de Newt on.

public static void m a in ( S t r in g [] args)


I
double n: // númer o
double aprox: // apro xi maci ón a la r a í z cuadrada
double antaprox: // a n t e r i o r aproximacióna la raíz cuadrada
double epsilon: II c o e f i c i e n t e de e r r o r

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

Si ejecuta este program a para un valor de n igual a 10, obtendrá la siguiente


solución:

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.

La ejecución de la sentencia fo r sucede de la siguiente forma:


1. Se inician las variables v i, v2, ...

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 + ’ ') :

En el siguiente ejem plo se puede observar la utilización de la com a com o se­


parador de las variables de control y de las expresiones que hacen que evolucio­
nen los valores q ue intervienen en la condición de finalización.

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

C om o aplicación de la sentencia fo r vam os a im prim ir un tablero de ajedrez


en el que las casillas blancas se sim bolizarán con una B y las negras con una N.
A sí m ism o, el program a deberá m arcar con * las casillas a las que se puede m over
un alfil desde una posición dada. La solución será sim ilar a la siguiente:

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

D esarrollo del program a:

• 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

• L eer la fila y la colum na en la que se coloca el alfil.

System.out.printí” fila "); f a l f i l = L e e r . d a t o l n t t ):


System.out.p r i n t ( " c o l u mn a "); c a l f i l = L e e r . d a t o l n t t );

• Partiendo de la fila 1, colum na 1 y recorriendo el tablero p o r filas,

for (fila = 1: fila <= 8: fila++)


I
for ( c o l u m n a = 1; c o l u mn a < = 8 : col umna++)
I
// P i n t a r e l t a b l e r o de a j e d r e z
I
S y s t e m . o u t . p r i n t l n ( ): // c a m b i a r d e f i l a
I

im prim ir un *, una B o una N dependiendo de las condiciones especificadas a


continuación:

0 Im prim ir un * si se cum ple, que la sum a o diferencia de la fila y colum na


actuales, coincide con la sum a o diferencia de la fila y colum na donde se
coloca el alfil.
CA PÍTU LO 6: SEN TEN CIA S DE C O N TR O L 145

0 Im prim ir una B si se cum ple que la fila m ás colum na actuales es par.


0 Im prim ir una N si se cum ple que la fila m ás colum na actuales es im par.

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

El program a com pleto se m uestra a continuació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 CLASSPATH


II
public class CAjedrez
I
// I m p r i m i r un t a b l e r o de a j e d r e z ,
public s t a t i c void m a in ( S t r in g [] args)
I
i n t 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
i n t f i l a , c o l u m n a ; // p o s i c i ó n a c t u a l del a l f i l

S y s t e m . o u t . p r i n t l n í " P o s i c i ó n del a l f i l : " ) ;


System.out.print(" fila “ ) ; f a l f i l = 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 o l u mn a " ) ; c a l f i l = 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 l n í ) ; // d e j a r una l i n e a en b l a n c o

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

A nteriorm ente vim os que la sentencia b re a k finaliza la ejecución de una senten­


cia sw itch. Pues bien, cuando se utiliza b re a k en el bloque correspondiente a una
sentencia while, do, o for, hace lo m ism o: finaliza la ejecución del bucle.

C uando las sentencias sw itch, while, do, o fo r estén anidadas, la sentencia


b re a k solam ente finaliza la ejecución del bucle donde esté incluida.

Por ejem plo, el bucle interno de la aplicación C Pitagoras desarrollada ante­


riorm ente, podría escribirse tam bién así:

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.

public class Test


1
public s t a t i c void m a in (S trin g [] args)
i
f o r ( i n t n = 0: n < = 1 0 0 : n + + )
I
II S i n no e s m ú l t i p l o de 5. s i g u i e n t e iteración
i f (n X 5 != 0 ) c o n t i nu é :
// I mp f i me el s i g u i e n t e m ú l t i p l o de 5
System.out.printlntn + " ");
CA PÍTU LO 6: SEN TEN CIA S DE C O N TR O L 147

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

Si ejecu ta u na aplicación que contenga el código anterior, obtendrá el si­


guiente resultado:

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

L a solución visualizada dem uestra que cuando la condición (x*y+ z)% 11 = = 0


se cum ple, b re a k interrum pe la ejecución de los tres bucles, continuando la eje­
cución en la sentencia siguiente al bucle m ás extem o.
148 JA V A: C U R SO DE PROGRAM A CIÓN

Si en el código anterior se sustituye b re a k por continué, la solución sería esta


otra:

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

SENTENCIAS t r y ... catch

En el capítulo anterior expusim os que cuando durante la ejecución de un progra­


ma ocurre un error que im pide su continuación, Java lanza una excepción que ha­
ce que se visualice un m ensaje acerca de lo ocurrido y se detenga la ejecución.
C uando esto ocurra, si no deseam os que la ejecución del program a se detenga,
habrá que utilizar try para poner en alerta a la aplicación acerca del código que
puede lanzar una excepción y utilizar catch para capturar y m anejar cada excep­
ción que se lance. Por ejem plo, si ejecuta la aplicación Test que se m uestra un po­
co m ás adelante, lanzará la excepción del tipo A rith m e tic E x c e p tio n que se indica
a continuación:

E x c e p t io n in th read "m a in " j a v a . 1 a n g .A r i t h m e t i c E x c e p t i o n : / by z e r o


at T e s t . m a in ( T e s t .ja v a :9 )

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.

public e lass Test


I
public static void m a in ( S t r in g [] args)
I
in t datol - 0, dato2 - 0. dato3:

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

M odifiquem os la aplicación con la intención de capturar la excepción lanza­


da. El resultado puede ser el siguiente:

public class Test


I
public static void m a in ( S trin g [] args)
I
int datol = 0. dat o2 = 0. dato3 - 0:

System.out.p rin tln ("S e inicia la aplicación"):


try
I
d a t o l ++:
dato3 = datol / d a t o 2 :
dato2++;
// O t r a s s e n t e n c i a s
I
c a t c h í A r i t h m e t i c 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 de t i p o A r i t h m e t i c E x c e p t i o n
S y s t e m . o u t .p r in t ln í"E r r o r : " + e .ge tM essageí));
dato3 = d a t o l ;
1
System.out.printlnídatol + " " + dato2 + " " + dato3);
I
I

A hora, si la sentencia clato3 = d a to l / d a lo ! d a lugar a una división p o r cero,


Java detendrá tem poralm ente la ejecución de la aplicación y lanzará una excep­
ción de tipo A rith m e tic E x c e p tio n que será capturada por la sentencia catch. La
ejecución de la aplicación se reanudará a partir de la prim era sentencia pertene­
ciente al bloque catch y continuará hasta el final de la aplicación. Se puede ob­
servar que la opción que se ha tom ado ante la excepción lanzada ha sido suponer
una división entre 1; esto es: dato3 = d a to l. El resultado cuando finalice la apli­
cación será:

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

teniendo en cuenta los siguientes casos:

1. Si a es igual a 0 y b es igual a 0, im prim irem os un m ensaje diciendo que la


ecuación es degenerada.

2. Si a es igual a 0 y b no es igual a 0, existe una raíz única con valor - c / b .

3. En los dem ás casos, utilizarem os la fórm ula siguiente:

- b ± j b 2 - 4 ac

L a expresión d = b 2 - 4ac se denom ina discrim inante.

• Si d es m ayor o igual que 0 entonces hay dos raíces reales.

• Si d es m enor que 0 entonces hay dos raíces com plejas de la forma:

x + yj, * - yj
Indicar con literales apropiados los datos a introducir, así com o los resultados
obtenidos.

La solución de este problem a puede ser de la siguiente form a:

• 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

• A continuación leem os los datos a, b y c.

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

• Leídos los coeficientes, pasam os a calcular las raíces.

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

• Im prim ir las raíces reales.

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

• Im prim ir las raíces com plejas conjugadas.

System .out.printlní"Raíces complejas:"):


Sys te m .o u t .p rin tln ír e + ’ + " + Math.abs(im) + " j");
Sys te m .o u t .p rin tln ír e + " - " + Math.abs(im) + " j");

El program a com pleto se m uestra a continuació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 C L AS S PATH


//
public class CEcuacion2Grado
I
// C a l c u l a r l a s r a í c e s de una e c u a c i ó n de 2® g r a d o
public s t a t i c void m a in ( S t r in g [] args)
I
double a. b. c: // c o e f i c i e n t e s d e l a e c u a c i ó n
double d: // d i s c r i m i n a n t e
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 raíz

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:

I n t r o d u c i r te x to . Para f i n a l i z a r pulsar Ctrl+z.


En l a U n i v e r s i d a d ha y muc h o s
e s t u d i a n t e s de T e l e c o m u n i c a c i ó n
[ C t r l ] [ z]

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

La solución de este problem a puede ser de la siguiente form a:

• Prim ero definim os las variables que vam os a utilizar en el program a.

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

• A continuación leem os el texto carácter a carácter.

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

• Si la m arca de fin de fichero está justam ente a continuación de la últim a pala­


bra (no se pulsó E ntrar después de la últim a palabra), entonces se sale del bu­
cle w hile sin verificar si esta palabra tenía o no cuatro vocales diferentes. Por
eso este proceso hay que repetirlo fuera del while.

if ( ( a + e + i + o + u ) >-4) np++;

• Finalm ente, escribim os el resultado.

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 con " +


”4 v o c a l e s d i s t i n t a s : " + n p ) ;

El program a com pleto se m uestra a continuación.

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

Intr od u cir texto. Pulse [Entrar] d e s p u é s de c a da linea.


Para f i n a l i z a r p u l s a r C t r l + z .

E s t e pr og r ama c u e n t a l o s c a r a c t e r e s , las palabras y


l a s l i n e a s de un d o c u me n t o .
[ C t r l ] [z]
80 13 2

El program a com pleto se m uestra a continuación. C om o ejercicio analice paso


a paso el código del program a y justifique la solución presentada com o ejem plo
anteriorm ente.

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

S y s t e m . o u t . p r i n t l n í n c a r a c t e r e s + " " + n p a l a b r a s + " " +

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

La solución de este problem a puede ser de la siguiente form a:

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

• A continuación presentam os el m enú en la pantalla para poder elegir la opera­


ción a realizar.

o p e r a c i ón = m e n ú ( ):

El m étodo m enú será definido com o un m étodo s ta tic de la clase aplicación


para que se pueda invocar sin tener que definir un objeto de esa clase. La de­
finición de este m étodo puede ser así:

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

Systern.out.printlní "\t2. r e s t a r " ) ;


S y s t e m . o u t . p r i n t í n ( " \ t 3 . muí t i p l i c a r " ) ;
S y s t e m . o u t . p r i n t l n i ”\ t 4 . d i v i d i r " ) :
S y s t e m . o u t . p r i n t í n ( " \ t 5 . s a l i r " );
S y s t e m . o u t . p r i n t í " \ n S e l e c c i o n e la o p e ra c i ón deseada: "):
o p = L e e r . d a t o l n t í );
I
w h i 1e ( o p < 1 || op > 5 ) :
r e t u r n op:

• Si la operación elegida no ha sido salir, leem os los operandos d a to ! y dato2.

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

• A continuación, realizam os la operación elegida con los datos leídos e im pri­


m im os el resultado.

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

El program a com pleto se m uestra a continuación.

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:

public static void m a in ( S t r in g [] args)


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

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.

2. R ealizar un program a que perm ita evaluar la serie:


1 6 0 JA V A : C U R SO DE PRO G R A M A CIÓ N

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:

1 7 + 1 0 + 1970= 1997 = > 1 + 9 + 9 + 7 = 26 = > 2 + 6 = 8

lo que quiere decir que su núm ero de T arot es el 8.

R ealizar un program a que pida una fecha, de la forma:

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.

4. R ealizar un program a que genere la siguiente secuencia de dígitos:

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.

5. R ealizar un program a para ju g a r con el ordenador a acertar núm eros. El ordenador


piensa un núm ero y nosotros debem os de acertar cuál es, en un núm ero de inten­
tos determ inado. Por cada intento sin éxito el ordenador nos irá indicando si el
núm ero especificado es m ayor o m enor que el pensado p o r él. El núm ero pensado
por el ordenador se puede obtener m ultiplicando p o r una constante el valor de­
vuelto por el m étodo ra n d o m de la clase M a th , y los núm eros pensados por no­
sotros los introducirem os p o r el teclado.

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:

• ‘A ’ es m enor que ‘a ’; es equivalente a decir que 65 es m enor que 97, porque


el valor ASCII de ‘A ’ es 65 y el de ‘a ’ es 97.

• ‘A ’ + 3 es igual a 'D '; es equivalente a decir que 65 + 3 es igual a 68, y este


valor es el código A SC II del carácter 'D '.
CA PÍTU LO 7
© F.J.Cebalos/RA-MA

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.

En este capítulo, aprenderá a registrar conjuntos de valores, todos del m ism o


tipo, en unas estructuras de datos llam adas m atrices. A sí m ism o, aprenderá a re­
gistrar cadenas de caracteres, que no son m ás que conjuntos de caracteres, o bien,
si lo prefiere, m atrices de caracteres.

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

INTRODUCCIÓN A LAS MATRICES


U na m atriz es una estructura hom ogénea, com puesta p o r varios elem entos, todos
del m ism o tipo y alm acenados consecutivam ente en m em oria. C ada elem ento
puede ser accedido directam ente p o r el nom bre de la variable m atriz seguido de
uno o m ás subíndices encerrados entre corchetes.

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.

D esde un punto de vista m atem ático, en m ás d e una ocasión necesitarem os


u tilizar variables subindicadas tales com o:

v = [a 0 , a , , a 2,

en el caso de un subíndice, o bien

°00 ü ()\ a 02 a oj a,0/1


a i0 a il a !2 ax
m-

a l0 an a¡2 ... at,y ... ain

si se utilizan dos subíndices. E sta m ism a representación se puede utilizar desde un


lenguaje de program ación recurriendo a las m atrices que acabam os de definir y
que a continuación se estudian.

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.

Entonces, las m atrices según su dim ensión se clasifican en unidim ensionales


y m ultidim ensionales; y según su contenido, en num éricas, de caracteres y de re ­
ferencias a objetos.

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.

MATRICES NUMÉRICAS UNIDIMENSIONALES


Igual que sucede con otras variables, antes de utilizar una m atriz hay que decla­
rarla. La declaración de una m atriz especifica el nom bre de la m atriz y el tipo de
elem entos de la misma.

Para crear y utilizar una m atriz hay que realizar tres operaciones: declararla,
crearla e iniciarla.

Declarar una matriz


La declaración de una m atriz de una dim ensión, se hace indistintam ente de una de
las dos form as siguientes:

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.

L as siguientes líneas de código son ejem plos de declaraciones de matrices:

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.

N otar q ue las declaraciones no especifican el tam año de la m atriz. El tam año


será especificado cuando se cree la m atriz, operación que se hará durante la ejecu­
ción del program a.

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

Crear una matriz


Después de haber declarado una m atriz, el siguiente paso es crearla o construirla.
C rear una m atriz significa reservar la cantidad de m em oria necesaria para conte­
ner todos sus elem entos y asignar al nom bre de la m atriz una referencia a ese blo­
que. Esto puede expresarse genéricam ente así:

nombre - new t i p o [ tamaño];

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

L as siguientes líneas de código declaran y crean las m atrices expuestas en los


ejem plos anteriores:

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

Iniciar una matriz


U na m atriz es un objeto; p o r lo tanto, cuando es creada, sus elem entos son auto­
m áticam ente iniciados, igual que sucedía con las variables m iem bro de una clase.
Si la m atriz es num érica, sus elem entos son iniciados a 0 y si n o es num érica, a un
valor análogo al 0; p o r ejem plo, los caracteres son iniciados al valor ‘\u 0 0 0 0 \ un
elem ento booleano a false y las referencias a objetos, a nuil.

Si deseam os iniciar una m atriz con otros valores diferentes a los predeterm i­
nados, podem os hacerlo de la siguiente form a:

flo atü temperatura = 1 1 0 . 2F. 1 2 . 3F. 3.4F. 1 4 . 5F, 1 5 . 6F. 16.7FI:

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.

Acceder a los elementos de una matriz

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

no es m ás que una variable subindicada; por lo tanto, se puede utilizar exacta­


m ente igual que cualquier otra variable. Por ejem plo, en las operaciones que se
m uestran a continuación intervienen elem entos de una matriz:

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

¿C óm o podem os asegurarnos de no exceder accidentalm ente el final de una


m atriz? V erificando la longitud de la m atriz m ediante la variable estática length.
que puede ser accedida por cualquier m atriz. Ésta es el único atributo soportada
por las m atrices. Por ejem plo:

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

Métodos de una matriz


La clase genérica “m atriz” proporciona un conjunto de m étodos que ha heredado
de la clase O b je ct del paquete ja v a.lan g. Entre ellos cabe ahora destacar e quals
(boolean equals(O bject objj) y clone (O bject clo n ef)). El prim ero perm ite verifi­
car si dos referencias se refieren a un m ism o objeto, y el segundo perm ite duplicar
un objeto (vea en el capítulo siguiente “ La clase O b je c t”).
CA PÍTU LO 7: M A TRICES 169

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

Trabajar con matrices unidimensionales


P ara practicar la teoría expuesta hasta ahora, vam os a realizar un program a que
asigne datos a una m atriz unidim ensional m de nE lem entos elem entos y, a conti­
nuación, com o com probación del trabajo realizado, escriba el contenido de dicha
m atriz. La solución será sim ilar a la siguiente:

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

El paso siguiente es asignar un valor desde el teclado a cada elem ento de la


m atriz.

for (i - 0: i < nElementos: i++)


I
System.out.p r i n t í " m [ " + i + "] - "):
m [ i ] = L e e r . d a t o l n t í ):
1 7 0 JA V A: C U R SO DE PROGRAM A CIÓN

U na vez leída la m atriz la visualizam os p ara com probar el trabajo realizado.

f o r ( i = 0: i < nE lem entos: i + + )


S y s t e m . o u t . p r i n t ( m [ i ] + " ” ):

El program a com pleto se m uestra a continuación:

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

S ystem .out.p rin tln (" Introducir los valores de l a m a t r i z . ” ):


f o r ( i = 0: i < nE lem entos: i + + )
I
System .out.printt"m [" + i + "] = ");
m [ i ] = L e e r . d a t o I n t ( ):

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

Igual que hicim os en el program a anterior, en prim er lugar crearem os una


m atriz nota con un núm ero determ inado de elem entos solicitado a través del te­
clado. N o se perm itirá que este valor sea negativo. En este caso interesa que la
m atriz sea de tipo float para que sus elem entos puedan alm acenar un valor con
decim ales. Tam bién definirem os un índice i para acceder a los elem entos de la
m atriz, y una variable sum a para alm acenar la sum a total de todas las notas.
C A PÍTU LO 7: M A TRICES 171

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.

for (i - 0; i < nota.length: i++)


I
S y s t e m . o u t . p r i n t í " N o t a media de l alumno " + (i+1) + ");
n o t a [ i ] = L e e r . d a t o F l o a t ( ):
I

Finalm ente se sum an todas la notas y se visualiza la nota m edia. La sum a se


alm acenará en la variable sum a. U na variable utilizada de esta form a recibe el
nom bre de acum ulador. Es im portante que observe que inicialm ente su valor es
cero.

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

El program a com pleto se m uestra a continuación.

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

for (i = 0: i < nota.length; i++)


1
S y s te m , o u t . p r i n t C N o t a media del a l u mn o " + ( i + 1) + ” : ” ):
n o t a [ i ] = L e e r . d a t o F l o a t í );

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

Los dos bucles fo r de la aplicación anterior podrían reducirse a uno com o se


indica a continuación. N o se ha hecho por m otivos didácticos.

for (i = 0: i < nota.length: 1++)


I
S y s t e m . o u t . p r i n t í " N o t a media de l alumno ” + ( i + 1 ) + ": "):
n o ta [i] = Leer.d a to Flo a t();
suma + = n o t a [ 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]

l a s m a t r i c e s mas u t i l i z a d a s son las unidim ensionales


y la s bidim ensional e s .

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

A ntes de em pezar el problem a, vam os a analizar algunas de las operaciones


q ue después utilizarem os en el program a. Por ejem plo, la expresión:

’z ' ■ 'a ' + 1

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 .

Por la m ism a razón, si realizam os las declaraciones,

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

la siguiente sentencia asigna a c[97¡ el valor 10.

c [ * a *] - 10:

y esta otra sentencia que se m uestra a continuación realiza la m ism a operación,


lógicam ente, suponiendo que ca r tiene asignado el carácter 'a ' .

c[car] = 10:

Entonces, si leem os un carácter (de la ‘a ’ a la ‘z ’),

c a r = ( c h a r ) S y s t e m . i n . r e a d ( ):

y a continuación realizam os la operación,

c[car]++:

¿qué elem ento de la m atriz c se ha increm entado? La respuesta es el de subíndice


igual al código correspondiente al carácter leído. H em os hecho coincidir el c a ­
rácter leído con el subíndice de la m atriz. A sí cada vez que leam os una 'a ' se in­
crem entará el contador c¡9 7 ] o lo que es lo m ism o c [ 'a ']; tenem os entonces un
contador de V . A nálogam ente direm os para el resto de los caracteres.

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

c[car ' a ']+ + :


1 7 4 JA V A: C U R SO DE PROGRAM A CIÓN

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.

El program a com pleto se m uestra a continuación.

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

// V i s u a l i z a r l a f r e c u e n c i a con l a que han a p a r e c i d o l o s c a r a c t e r e s


f o r ( c a r = ' a ' : c a r <= ’z ’ : c a r + + )
System.out.printí" ” + c[car - ' a' ] ) :
S y s t e m . o u t . p r i n t í n i ):
1
)

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 :

System.out.printlní"Fin del proceso."):

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:

cha r [ ] c a d e n a = l'a', 'b', ’c ' . *d * I :

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

cha r [ ] c a d e n a = 197, 98. 99. 1001:

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

Si se crea una m atriz de caracteres y se le asigna un núm ero de caracteres m e­


nor que su tam año, el resto de los elem entos quedan con el valor ‘\ 0 ’ con el que
fueron iniciados. Por ejem plo:
1 7 6 JA V A: C U R SO DE PROGRAM A CIÓN

char[] cadena - new charflO];


c a d e n a C O ] - ’a ‘ : c a d e n a f l ] = ’b ’ ; c a d e n a [ 2 ] = ’c ’ : c a d e n a [ 3 ] = 'd':
S y s t e m . o u t . p r i n t l n ( c a d e n a );

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 om o ya se expuso al hablar de las m atrices num éricas, un intento de acceder


a un valor de un elem ento con un subíndice fuera de los lím ites establecidos al
crear la m atriz, daría lugar a que Java lanzara una excepción durante la ejecución.

Leer y escribir una cadena de caracteres


En el capítulo 5, cuando se expusieron los flujos de entrada, vim os que una form a
de leer un carácter del (lujo in era utilizando el m étodo read. Entonces, leer una
cadena de caracteres supondrá ejecutar repetidas veces la ejecución de read y al­
m acenar cada carácter leído en la siguiente posición libre de una m atriz de carac­
teres. Por ejem plo:

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

w hile ( ( c a r = S y s t e m . i n . r e a d ( )) != ’ \ r ’ && i < cadena. length)


cadena[i++] = (ch arle ar:

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

// 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 lu j o S = System.out:

String cadena: // v a r i a b l e p a r a a l m a c e n a r un a linea de t e x t o


try
1
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 : “ ):
c a d e n a = f 1u j o E . r e a d L i n e ( ) ; // l e e r una l i n e a de t e x t o
flujoS.println(cadena): // e s c r i b i r l a l i n e a l e í d a
I
catch (IOException ignorada) 1 I

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.

C om parando el m étodo read con el m étodo re a d L in e , se puede observar que


este últim o proporciona una form a m ás cóm oda de leer cadenas de caracteres de
un flujo y adem ás, devuelve un objeto S t r in g cuyos m étodos, com o verem os a
continuación, hacen m uy fácil la m anipulación de cadenas.

U na m atriz de caracteres tam bién puede ser convertida en un objeto S trin g,


según se m uestra a continuación. P or ejem plo:

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

Trabajar con cadenas de caracteres


El siguiente ejem plo lee una cadena de caracteres y a continuación visualiza el
sím bolo y el valor A SC II de cada uno de los caracteres de la cadena. La solución
será de la forma:

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

E l problem a consiste en definir una cadena de caracteres, cadena, y asignarle


datos desde el teclado utilizando el m étodo re a d . U na vez leída la cadena, se ac­
cede a cada uno de sus elem entos (no olvide que son elem entos de una m atriz) y
por cada uno de ellos se visualiza su contenido y el valor A S C II correspondiente.

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

C uando un usuario ejecute este program a, se le solicitará que introduzca una


cadena. Por ejem plo:

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

M inusculasM ayuscu las(caden a):

C om o se puede observar en el código m ostrado a continuación, el m étodo re ­


cib irá una referencia a la cadena que se desea pasar a m ayúsculas. A continua­
ción, accederá al prim er elem ento de la m atriz y com probará si se trata de una
m inúscula, en cuyo caso cam biará el valor A SC II alm acenado en dicho elem ento
por el valor A SC II correspondiente a la m ayúscula. Esto es:

static void M in u sculasM ayuscu las(char[] str)


I
i n t i = 0 . desp = ’ a ' - ’A ' ;
f o r ( i = 0 : i < s t r . l e n g t h && s t r [ i ] ! = , \ 0 ‘ ; i++)
i f ( s t r [ i ] > = ' a ' && s t r [ i 3 < = ’ z ' )
s t r [ i ] = ( c h a r ) ( s t r [ i ] - desp):
180 JAVA: C U R SO DE PROGRAM A CIÓN

O bserve que cuando se llam a al m étodo M inusculasM ayusculas, lo que en


realidad se pasa es una referencia al com ienzo de la m atriz. P or lo tanto, el m éto­
do llam ado y el m étodo que llam a, trabajan sobre la m ism a m atriz, con lo que los
cam bios realizados p o r uno u otro son visibles para am bos.

El program a com pleto se m uestra a continuación.

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

public s t a t ic void m a in (S trin g [] args)


I
c h a r [ ] c a d e n a = 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 m atriz

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

La solución que se ha dado al problem a planteado n o contem pla los caracteres


típicos de nuestra lengua com o la ñ o las vocales acentuadas. E ste trabajo queda
com o ejercicio para el lector.

La utilización de m atrices de caracteres para la solución de problem as puede


ser am pliam ente sustituida por objetos de la clase S trin g . La gran cantidad y va­
riedad de m étodos aportados p o r esta clase facilitarán enorm em ente el trabajo con
cadenas de caracteres, puesto que, com o ya sabem os, un objeto S trin g encapsula
una cadena de caracteres.
C A P ÍT U L O 7: M A TRICES 181

Clase String

La clase S trin g , que pertenece al paquete ja v a .la n g, proporciona m étodos para


exam inar caracteres individuales de una cadena de caracteres, com parar cadenas,
buscar y extraer subcadenas, copiar cadenas y convertir cadenas a m ayúsculas o a
m inúsculas. A continuación verem os algunos de los m étodos m ás com unes de la
clase S tr in g . P ero antes sepa que un objeto S t r in g representa una cadena de ca­
racteres no m odificable. Por lo tanto, una operación com o convertir a m ayúsculas
no m odificará el objeto original sino que devolverá un nuevo objeto con la cadena
resultante de esa operación.

A sí m ism o, el lenguaje Java proporciona el operador + para concatenar o bje­


tos S trin g , así com o soporte para convertir otros objetos a objetos S trin g . Por
ejem plo, en la siguiente línea d e código. Java debe convertir las expresiones que
aparecen entre paréntesis en objetos S trin g . antes de realizar la concatenación.

S y s t e m . o u t . p r in t ln ( "Dim ensión de l a m a t r i z : " + cadena.length);

La concatenación de objetos S t r in g está im plem entada a través de la clase


S t r in g B u ff e r y la conversión, a través del m étodo to S tr in g heredado de la clase
O bject. T anto la clase com o el m étodo citados serán estudiados a continuación.

R ecuerde que para acceder desde un m étodo de la clase aplicación o de cual­


quier otra clase a un m iem bro (atributo o m étodo) de un objeto de otra clase dife­
rente se utiliza la sintaxis objeto.m iem bro. La interpretación que se hace en
program ación orientada a objetos es que el objeto ha recibido un m ensaje, el es­
pecificado por el nom bre del m étodo, y responde ejecutando ese m étodo. Los
m étodos static son una excepción a la regla (puede obtener m ás inform ación en el
apartado “ M iem bro de un objeto o de una clase" del capítulo 4).

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 s t r l = "abe” : // c r e a un S t r i n g " a b e "


String s t r 2 = new S t r i n g ( " d e f ” ); // c r e a un S t r i n g " d e f ”
String s t r 3 = new S t r i n g ( s t r l ) : II c r e a un n u e v o S t r i n g "abe"

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

La m ism a operación puede ser realizada utilizando la expresión s t r l = s tr l lo


cual ya fue expuesto en el apartado “ R eferencias a objetos” del capítulo 4.

String concat(String str)

Este m étodo devuelve com o resultado un nuevo objeto S t r in g resultado de con­


catenar el S t r in g especificado a continuación del objeto S t r in g q ue recibe el m en­
saje concat. P or ejem plo, la prim era línea de código que se m uestra a
continuación da com o resultado “A yer llovió” y la segunda “ A yer llovió m ucho” .

System .out.println("Ayer".concat(" llovió")):


System.out.pri n t ln ( "A y e r ". c o n c a t ( " 11o v i ó " . c o n c a t ( " m u c h o ") ) ) :

Si alguno de los S t r in g tienen longitud 0, se concatena una cadena nula. Este


otro ejem plo que se m uestra a continuación construye un objeto “a b e d e f' resulta­
do de concatenar s tr l y str2, y asigna a s tr l la referencia al nuevo objeto.

Strin g s t r l = "abe", str2 = "def";


strl = strl .concat(str2):

int com pareTo(String otroString)

Este m étodo com para lexicográficam ente el S t r in g especificado, con el objeto


S t r in g que recibe el m ensaje c o m p a r e T o (el m étodo e q u a ls realiza la m ism a
operación). El resultado devuelto es un entero:

< 0 si el S t r in g que recibe el m ensaje es m enor que el otroString,


= 0 si el S t r in g que recibe el m ensaje es igual que el otroString y
> 0 si el S t r in g que recibe el m ensaje es m ayor que el otroString.
C A PÍTU LO 7: M A TRICES 183

En otras palabras, el m étodo c o m p a r e T o perm ite saber si una cadena está en


orden alfabético antes (es m enor) o después (es m ayor) que otra y el proceso que
sigue es el m ism o que nosotros ejercitam os cuando lo hacem os m entalm ente,
com parar las cadenas carácter a carácter. La com paración se realiza sobre los va­
lores U nicode de cada carácter. El siguiente ejem plo com para dos cadenas y es­
cribe “abcde" porque esta cadena está antes por orden alfabético.

S tr in g s t r l - "abcde”, str2 = "abcdefg":


i f ( s t r l . c o m p a r e T o ( s t r 2 ) < 0)
System .out.p rin tln (strl):

El m étodo c o m p a r e T o diferencia las m ayúsculas de las m inúsculas. Las m a­


yúsculas están antes por orden alfabético. Esto es así porque en la tabla U nicode
las m ayúsculas tienen asociado un valor entero m enor que las m inúsculas. El si­
guiente ejem plo no escribe nada porque “abe” no está antes por orden alfabético
que “A be”.

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

Si en vez de utilizar el m étodo c o m p a r e T o se u tiliza el m étodo com pare-


T o Ig n o r e C a s e no se hace diferencia entre m ayúsculas y m inúsculas. El resultado
de ejecutar el siguiente program a es que s tr l y str2 son iguales.

public class Test


I
public static void m a in (S trin g [] args)
I
String s t r l = " L a p r o v i n c i a d e S a n t a n d e r e s muy b o n i t a " :
String s t r 2 = “ La p r o v i n c i a de S ANTANDER e s muy b o n i t a " :

S t r i n g strtemp;
in t resultado:

resultado = s t r l . com pareToIgnoreCase(str2):

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.

El siguiente ejem plo escribe com o resultado: L o n g i t u d : 39

S t r i n g s t r l = " L a p r o v i n c i a de S a n t a n d e r e s muy b o n i t a " :


S y ste m .o u t .p rin t'In C 'L o n g itu d : " + s t r l . l e n g t h t )):

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.

El siguiente ejem plo alm acena en s tr l la cadena str2 en m ayúsculas.

S tr in g s t r l . s tr2 = "Santander, tu eres novia del m ar...":


s t r l = s t r 2 . t o U p p e r C a s e t ):

String trim()

Este m étodo devuelve un objeto S t r in g resultado de elim inar los espacios en


blanco que pueda haber al principio y al final del objeto S t r in g que recibe el m en­
saje trim .

boolean startsW ith(String prefijo)

Este m étodo devuelve un valor true si el prefijo especificado coincide con el


principio del objeto S t r in g que recibe el m ensaje sta rtsW ith .

boolean endsW ith(String sufijo)

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

String substring(int Indicelnicial, int IndiceFinal)

Este m étodo retorna un nuevo S t r in g que encapsula una subcadena de la cadena


alm acenada por el objeto S t r in g q ue recibe el m ensaje su b strin g . La subcadena
em pieza en In d icelnicial y se extiende hasta IndiceF inal - I, o hasta el final si In-
diceF inal no se especifica.

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

char charAt(int índice)

Este m étodo devuelve el carácter que está en la posición especificada en el objeto


S t r in g que recibe el m ensaje c h a rA t. El índice del prim er carácter es el 0. Por lo
tanto, el parám etro índice tiene que estar entre los valores 0 y le n gth () - 1. de lo
contrario Jav a lanzará un excepción.

int indexOf(int car)

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.

int indexOf(String str)

Este m étodo devuelve el índice de la prim era ocurrencia de la subcadena especifi­


cada por str en el objeto S t r in g que recibe el m ensaje in d e x O f. Si s tr no existe
in d e x O f devuelve -1. Puede com enzar la búsqueda por el final en lugar de ha­
cerlo por el principio utilizando el m étodo la stln d e x O f.

String repiace(char car, char nuevoCar)

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

static String valueOf(tipo dato)

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.

A lgunos de los m étodos m ás interesantes que proporciona la clase S t r in g B u ­


ffer, perteneciente al paquete ja v a .la n g, son los siguientes:

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

S y s t e m . o u t . p r i n t l n ( s t r b 3 + " ” + s t r b 3 . 1 e n g t h ( )+ " " + s t r b 3 . c a p a c i t y ( )):


i
La ejecución de las líneas de código del ejem plo anterior, da lugar a los si­
guientes resultados:

0 16
0 80
a b c d e 5 21

A la vista de los resultados podem os deducir que cuando S t r in g B u f f e r se in­


voca sin argum entos construye un objeto vacío con una capacidad inicial para 16
caracteres; cuando se invoca con un argum ento entero, construye un objeto vacío
con la capacidad especificada; y cuando se invoca con un S t r in g com o argum ento
construye un objeto con la secuencia de caracteres proporcionada por el argu­
m ento y una capacidad igual al núm ero de caracteres alm acenados m ás 16.

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

Este m étodo devuelve la capacidad en caracteres U nicode (tipo c h a r) del objeto


S tr in g B u ffe r que recibe el m ensaje 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.

StringBuffer insert(int índice, tipo x)

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

El siguiente ejem plo crea un objeto S t r in g B u f f e r con la cadena “M es de del


año", a continuación inserta la cadena “ Abril ” a partir de la posición 7, y final­
m ente añade al final, la cadena representativa del entero 2002. El resultado será
“M es de Abril 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 ( " M e s de " . l e n g t h O , " A b r i l " ) ; // " M e s de ” . l e n g t h ( ) = 7
strb.append(2002);

StringBuffer delete(int p1, int p2)

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

StringBuffer replace(int p1, int p2, String str)

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

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 d e d e l año "):


s t r b . insert.( 7 . " A b r i l " ) ;
strb.append(2002);
s t r b . r e p l a c e ( 7 . 13. "Ma yo " ) :

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

String substring(int Indicelnicial, int IndiceFinal)

E ste m étodo retorna un nuevo S t r in g que encapsula una subcadena de la cadena


alm acenada por el objeto S t r in g B u f fe r que recibe el m ensaje su b strin g. La sub­
cadena em pieza en Indicelnicial y se extiende hasta IndiceF inal - 1, o hasta el fi­
nal si IndiceF inal no se especifica.

char charAt(int índice)

Este m étodo devuelve el carácter que está en la posición especificada en el objeto


S tr in g B u ffe r que recibe el m ensaje c h a rA t. El índice del p rim er carácter es el 0.
P o r lo tanto, el parám etro índice tiene que estar entre los valores 0 y le n gth () - 1.

void setCharAt(int índice, char caí)

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

Este m étodo devuelve com o resultado un nuevo S t r in g copia del objeto S t r in g ­


B u ffe r que recibe el m ensaje to Strin g.

El siguiente ejem plo copia la cadena alm acenada en strb en str.

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 ( " a b c d e " ):


S t r i n g s t r = s t r b . t o S t r i n g ( );

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

Un objeto S tr in g T o k e n iz e r se construye a partir de un objeto S trin g . Por


ejem plo:

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

C uando ejecutem os las cuatro líneas de código correspondientes a los dos


ejem plos anteriores, el resultado que se m ostrará será el siguiente:

un o,
dos.
tres
y
cuatro

Tam bién se pueden especificar los delim itadores en el instante de construir el


objeto S trin g T o k e n iz e r. Por ejem plo, la siguiente línea de código especifica co­
m o delim itadores la com a y el espacio en blanco:

cadena - new S t r i n g T o k e n i z e r ( " u n o . d o s . 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

La diferencia con respecto a la versión anterior es que ahora no aparece la


com a com o parte integrante de los elem entos, ya que se ha especificado co m o de­
lim itador y los delim itadores no aparecen. Si querem os que los delim itadores ap a­
rezcan com o un elem ento m ás, basta especificar tr u e com o tercer argum ento:

cadena - new S t r i n g T o k e n i z e r ( ”u n o . dos. tres y cuatro". true):

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

Conversión de cadenas de caracteres a datos numéricos


C uando una cadena de caracteres representa un núm ero y se necesita realizar la
conversión al valor num érico correspondiente, p o r ejem plo, para realizar una ope­
ración aritm ética con él, hay que u tilizar los m étodos apropiados proporcionados
por las clases del paquete ja v a .la n g : Byte, C h a ra c te r, S h o rt, In te ge r, L o n g.
Flo at, D o u b le y Boo lean . Para m ás detalles, recurra al capítulo 5, donde fueron
expuestos los m étodos aludidos. Por ejem plo:

String s trl - "1234":


i n t d a t o l = I n t e g e r . p a r s e l n t ( s t r l ): // c o n v e r t i r a entero

String str2 - "12.34";


f l o a t d a t o 2 = (new F l o a t ( s t r 2 ) ) . f 1o a t V a l u e ( ) : // c o n v e r t i r a float

MATRICES DE REFERENCIAS A OBJETOS


Según lo estudiado a lo largo de este capítulo podem os decir que cada elem ento
de una m atriz unidim ensional es de un tipo prim itivo, o bien una referencia a un
objeto. Entonces ¿cóm o procederíam os si necesitáram os alm acenar las tem peratu­
ras m edias de cada día durante los 12 m eses de un año?, o bien ¿cóm o procede­
ríam os si necesitáram os alm acenar la lista de nom bres de los alum nos de una
determ inada clase? R azonando un poco, llegarem os a la conclusión de que utilizar
m atrices unidim ensionales para resolver los problem as planteados supondrá pos­
teriorm ente un difícil acceso a los datos alm acenados; esto es, responder a las
preguntas: ¿cuál es la tem peratura m edia del 10 de m ayo?, o bien ¿cuál es el
nom bre del alum no núm ero 25 de la lista? será m ucho m ás sencillo si los datos
los alm acenam os en form a de tabla; en el caso de las tem peraturas, una tabla de
12 filas (tantas com o m eses) por 31 colum nas (tantas com o los días del m es más
largo); y en el caso de los nom bres, una tabla de tantas filas com o alum nos, y
tantas colum nas com o el núm ero de caracteres del nom bre m ás largo. P or lo tanto,
una solución fácil para los problem as planteados exige el uso de m atrices de dos
dim ensiones.

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.

Matrices numéricas multidimensionales


La definición de una m atriz num érica de varias dim ensiones se hace de la forma
siguiente:

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,

— ► moo moi m 02 fila 0

mío mu m t2 fila 1

Evidentem ente, el tipo de los elem entos de la m atriz referenciada por m es


int[] y el tipo de los elem entos de las m atrices referenciadas por m /0 ] y m [ l ] es
int. A dem ás, puede com probar la existencia y la longitud de las m atrices unidi­
m ensionales referenciadas por m , m [0] y m [ I ] utilizando el código siguiente:

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

C om o ejem plo de aplicación de m atrices m ultidim ensionales, vam os a reali­


zar un program a que asigne datos a una m atriz m de dos dim ensiones y a co n ti­
nuación escriba las sum as correspondientes a las filas de la m atriz. La ejecución
del program a presentará el aspecto siguiente:

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.

int n f ila s . ncols; // f i l a s y c o l u m n a s de l a m a t r i z


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 negativo
194 JA V A: C U R SO D E PROGRAM A CIÓN

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

El paso siguiente es asignar un valor desde el teclado a cada elem ento de la


matriz.

f o r ( f i l a = 0: f i l a < n fila s; fila++)


f o r ( co l = 0; col < ncols: col++)
I
S y ste m .o u t.p rin tt "m [" + f i l a + + col + "] = "):
m [ f i l a ] [ c o l ] = L e e r . d a t o F l o a t t ):
I

U na vez leída la m atriz, calculam os la sum a de cada fila y visualizam os los


resultados para com probar el trabajo realizado.

for (fila = 0; fila < nfilas; 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 l a ] [ c o l ];
S y s t e m . o u t . p r i n t l n ( “Suma de 1 a f i 1 a " + f i 1 a + " : ” + sum afila):

El program a com pleto se m uestra a continuación.

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

System .out.p rin tín(" Introd u cir los valores de l a m atriz.");


f o r ( f i l a = 0: f i l a < n f i l a s ; f i l a + + )
I
for (col - 0; col < ncols: col++)
I
System .out.printí"m [" + f i l a + " ] [ " + col + "] = ");
m[f1l a ] [ c o l ] = L e e r.d a to F lo a t();

// 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 y s t e m . o u t . p r i n t l n t " S u m a de l a fila " + fila + ": " + sumafila):


I
System .out.p rin tín("\nFin del proceso."):

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.

for (fila = 0: fila < nfilas: fila++)


I
s u m a f i 1 a = 0;
f o r ( c o l = 0; col < ncols; col++)
I
System .out.printt"m [" + f i l a + "][" + col + ”] = " ) ;
1 9 6 JA VA : C U R S O DE PROGRAM ACIÓ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

No obstante, esta form a de proceder presenta una diferencia a la hora de vi­


sualizar los resultados, y es que la sum a de cada fila se presenta a continuación de
haber leído los datos de la misma.

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.

Matrices de cadenas de caracteres


L as m atrices de cadenas de caracteres son m atrices m ultidim ensionales, general­
m ente de dos dim ensiones, en las que cada fila se corresponde con una cadena de
caracteres. E ntonces según lo estudiado, una fila puede ser un objeto m atriz uni­
dim ensional, un objeto S trin g o un objeto S trin g B u ffe r.

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:

char[)[] nombre__matri z = new ch a r [ f i I a s ] [ 1ongi t u d _ f i 1a ] ;

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

trices unidim ensionales de C elem entos de tipo c h a r. G ráficam ente podem os


im aginarlo así:

mo ------------------- * mo.o mo.i mo.2 m o.c-i fila 0


mt ------- ► m i.o m i.i m i .2 m t.c -i fila 1
m2 ------------------- ►
m 2.o m 2.i m 2.2 m 2.c-i fila 2
------------------- *
... ...
*
itif -i niF-i.p m F- i.i niF-i.2 m p-i.c-i fila F-1

Evidentem ente, el tipo de los elem entos de la m atriz referenciada p o r m es


c h a r[] y el tipo de los elem entos de las m atrices referenciadas por m [0], m [ l] ......
es char. D esde nuestro punto de vista, es m ás fácil im aginarse una m atriz de ca­
denas de caracteres com o una lista. Por ejem plo, la m atriz m del ejem plo anterior
estará com puesta por las cadenas de caracteres rn[0], m [ l] , m [2], m i3 ], etc.

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.

P ara ilustrar la form a de trabajar con cadenas de caracteres, vam os a realizar


un program a que lea una lista de nom bres y los alm acene en una m atriz. Una vez
construida la m atriz, visualizarem os su contenido.

La solución tendrá el aspecto siguiente:

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

La solución pasa p o r realizar los siguientes puntos:

1. D efinir una m atriz de cadenas, los índices y dem ás variables necesarias.

2. E stablecer un bucle para leer las cadenas de caracteres utilizando el m étodo


re a d . La entrada de datos finalizará al introducir la m arca de fin de fichero.

3. P reguntar al usuario del program a si quiere visualizar el contenido de la ma­


triz.

4. Si la respuesta anterior es afirm ativa, establecer un bucle para visualizar las


cadenas de caracteres alm acenadas en la m atriz.

El program a com pleto se m uestra a continuación.

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

S y s t e m . o u t . p r i r j t l p í " E s c r i b a l o s nombres que desea i n t r o d u c i r . " ) ;


S y s t e m . o u t . p r i n t l n ( " P u e d e f i n a l i z a r p u l s a ndo l a s t e c l a s [ C t r l ] [ Z ] . " ) :
f o r ( f i l a - (ti f i l a < n F i l a s : T i l a + + )
, u r.jlri o n n u -.*» i.r.d-.b a.«p vjr-ciia -
f l u j o S . p r i n t í " N o m b r e [ ” + f i l a + ”]: " ) ;
n C a r s L e i d o s = f l u j o E . r e a d í n o m b r e [ f i 1a ] » 0 . n C a r s P o r F i l a ) ;
// S i s e p u l s ó [ C t r l ] [ Z ] , s a l i r d e l b u c l e
i f (nCarsLeidos = e o f ) break;
// E l i m i n a r l o s c a r a c t e r e s CR LF
n o m b r e [ f i 1 a ] [ n C a r s L e i d o s - 1] = * \ 0 ’ ;
n o m b r e [ f i l a ] [ n C a r s L e i d o s - 2 ] = * \ 0 *:
1
flu jo S.p rin tí"\n \n ");
n F i l a s = f i l a : // nú me r o d e f i l a s leidas
char respuesta:
do
fl.:-.
f l u j o S . p r i n t ( " ¿ D e s e a v i s u a l i z a r el conteni do de l a m a t r i z ? ( s /n ) : " ) ;
respuesta = ( ( f lu j o E . r e a d L in e í) ) .toLoW erCaseí) ) .ch a rA t(O );
I .ífV j .*<>■, , ;r,' -i, . . , '! ,,rr: ^ . ¡
w h i l e ( r e s p u e s t a ! = ' s ' && r e s p u e s t a != 'n'),;
i f ( respuesta == ’s ' )
I
// V i s u a l i z a r l a l i s t a de n o m b r e s
f l u j o S . 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 : f i l a + + )
f l u j o S . p r i n t í n í n o m b r e t f i l a ] );

catch (IOException ignorada) 1 I

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.

Siguiendo con el análisis del program a anterior, la entrada de datos finalizará


cuando se haya introducido la m arca de fin de fichero, o bien cuando se hayan in­
troducido la totalidad de los nom bres.

A sí m ism o, una v ez finalizada la entrada de datos, se lanza una pregunta acer­


ca d e si se desea visualizar el contenido de la m atriz. En este caso la respuesta te­
cleada se obtiene con re a d L in e . C om o este m étodo lee hasta el carácter ‘\n ‘
inclusive, utilizam os el m étodo c h a r A t para obtener del S t r in g devuelto por rea­
d L in e , el prim er carácter leído, que deberá ser una ‘s’ o bien una ‘n ’.

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.

El proceso que seguirem os para solucionar el problem a planteado es el si­


guiente:

• D efinim os la m atriz de referencias a las m atrices unidim ensionales que serán


las filas de una supuesta lista.

char[ ] [] nombre = new c h a r [ n F i 1 a s ] [ ] :

N o asignam os m em oria para cada una d e las cadenas porque no conocem os su


longitud {nombre( 0 / = nuil, n o m b re/1] = nuil, etc.). P or lo tanto, este proce­
so lo desarrollarem os paralelam ente a la lectura de cada una de ellas.
C A P ÍT U L O 7: M A TRICES 201

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.

char[] un Nombre = new c h a r [ 8 1 ] ¡

U na vez leída la cadena, conocerem os cuántos caracteres se han leído; enton­


ces, reservam os m em oria (new ) para alm acenar ese núm ero de caracteres, a l­
m acenam os la referencia al bloque de m em oria reservado en el siguiente
elem ento vacío de la m atriz de referencias nom bre y copiam os unN om bre en
el nuevo bloque asignado (fila de la m atriz nom bre). Este proceso lo repeti­
rem os para cada uno de los nom bres que leam os.

for (fila = 0; fila < nFilas: fila++)


I
f l u j o S . p r i n t ( " N o m b r e t " + f i l a + ”]: "):
nCarsLeidos = flujoE.read(unNombre. 0, n C a r s P o r F i 1 a ) :
// S i s e p u l s ó [ C t r l ] [ Z ] , s a l i r d e l bucle
i f ( n C a rs L e id o s == e o f) break:

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

G ráficam ente puede im aginarse el proceso descrito de acuerdo a la siguiente


estructura de datos:

unNombre J \r \n

nombre nombrep c a r m e n filaO


nombret F r a n c i s e o fila 1
nombre? J e s ú s fila 2

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.

U na vez leída la m atriz la visualizam os si la respuesta a la petición de realizar


este proceso es afirm ativa.

El program a com pleto se m uestra a continuación.


202 JA V A: C U R SO DE PROGRAM A CIÓN

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 ] [ ] :

S y s t e m . o u t . p r i n t l n ( " E s c r i b a l o s nombres que desea i n t r o d u c i r . " ) ;


S y s t e m . o u t . p r i n t l n t " P u e d e f i n a l i z a r p u l s a ndo l a s t e c l a s [ C t r l ] C Z ] . " ) ;
f o r ( f i l a = 0; f i l a < n F i l a s : f i l a + + )
I
flujoS.printf"N om bre[" + f ila + "]: ");
n C a r s L e i d o s = f l u j o E . r e a d ( u n N o m b r e , 0. n C a r s P o r F i l a ) :
// S i s e p u l s ó [ C t r l 3 [ Z J . s a l i r d e l b u c l e
i f (n C a rsL e id o s == e of) break:
// A ñ a d i r el nombre l e i d o a l a m a t r i z ; nombre

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

Matrices de objetos String


En el apartado anterior hem os aprendido a m anipular cadenas de caracteres a ni­
vel de carácter. Pero Java proporciona las clases S t r in g y S t r in g B u f f e r para ha­
cer de las cadenas de caracteres objetos con sus atributos particulares, los cuales
podrán ser accedidos por los m étodos de sus clases. D esde este nivel de abstrac­
ción m uchos de los problem as que se han presentado anteriorm ente y que hem os
tenido que resolver, ahora sim plem ente no aparecerán con lo que la im plem enta-
ción del program a resultará m ás sencilla.

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:

• D efinim os la m atriz de objetos S trin g :

Strin gf] no mb r e = new S t r i n g [ n F i 1 a s ] ;

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.

for (fila = 0; fila < nFilas; fila++)


(
flujoS.print("Nom breC" + f ila + "]: "):
n o m b r e [ f i l a ] = f 1u j o E . r e a d L i n e ( );
// S i s e p u l s ó [ C t r l ] [ Z ] , s a l i r d e l bucle
i f ( n o m b r e í f i 1a ] - - n u i l ) b r e a k :
1

G ráficam ente puede im aginarse el proceso descrito de acuerdo a la siguiente


estructura de datos, aunque para trabajar resulte m ás fácil pensar en una ma­
triz unidim ensional cuyos elem entos nom bre[0], n o m b r e [l] , etc. son cadenas
de caracteres.
204 JA VA : C U R S O DE PROGRAM A CIÓN

nom breo o b je to Strinq


n o m b rei o b je to Strinq
n o m b re 2 o b je to String

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

El program a com pleto se m uestra a continuación.

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 y s t e m . o u t . p r i n t l n C ' E s c r i b a l o s nombres que desea i n t r o d u c i r . " ) :


S y s t e m . o u t . p r i n t l n í " P u e d e f i n a l i z a r p u l s a nd o l a s t e c l a s [ C t r l ] [ Z ] . “ );
f o r ( f i l a = 0: f i l a < n F i l a s : f i l a + + )
I
flujoS.print("N om bre[" + f ila + "]: "): ___________
n o m b r e [ f i l a ] - f 1u j o E . r e a d L i n e ( );
I I S i s e p u l s ó [ C t r l ] CZ ] . s a l i r d e l bucle
i f ( n o m b r e C f i 1a ] - - n u i l ) b r e a k :
I
f l u j o S . p r i n t í “\ n \ n " ) :
n F i l a s = f i l a : // nú mero de f i l a s leídas
II ...
CA PÍTU LO 7: M A TRICES 2 0 5

// c o n t i n ú a igual q u e en l a versión anterior


1
catch (IOException ignorada) I )

Si en lugar de utilizar objetos S tr in g utilizam os objetos S trin g B u ffe r, las


m odificaciones son m ínim as. P uede verlas en el código m ostrado a continuación:

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 ;

for (fila = 0: fila < nFilas; fila++)


I
flujoS.print("Nom breC" + f i l a + "]: "):
// S i s e p u l s ó [ C t r l ] C Z 3. s a l i r d e l b u c l e
i f ( ( s N o m b r e = f 1u j o E . r e a d L i n e ( ) ) — n u i l ) break:
n o m b r e [ f i l a ] = new S t r i n g B u f f e r ( s N o m b 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.

La solución de este problem a puede ser de la siguiente forma:

• D efinim os la m atriz que va a co ntener la lista de valores y el resto de las va­


riables necesarias en el program a.

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

f 1 o a t [ ] d a t o - new f l o a t [ n E l e i n e n t o s ] ; // c r e a r l a m a triz dato


i n t i = 0; / / subíndice
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

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

'for (i = 0; i < dato.length; i++)

System .out.printt"dato[" + i + "]= "):


d a t o [ i ] = L e é r . d a t o F l o a t ( );
i f ( F l o a t . i s N a N ( d a t o [ i ] ) ) b r e a k ; // s a l i r del bucle
1

nEle mentos = i : // nú mero de v a l o r e s leídos

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.«•

• U na vez leída la lista d e valores, calculam os el m áxim o y el m ínim o. Para ello


suponem os inicialm ente que el p rim er valor es el m áxim o y el m ínim o (com o
si todos los valores fueran iguales). D espués com param os cada uno de estos
dos valores con los restantes de la lista. El v alo r de la lista com parado pasará
a ser el nuevo m ayor si es m ás grande que el m ayor actual y pasará a ser el
nuevo m enor si es m ás pequeño que el m enor actual.

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

• Finalm ente, escribim os el resultado.

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

E l program a com pleto se m uestra a continuación.


CA PITU LO 7: M A TRICES 2 0 7

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

Sy ste m .o u t.p rin tln (" In tro d u c ir los valores.\n" +


"Para f i n a l i z a r p u l s e [ E n t r a r ] " ),;¡ .¡i,,.;,t , .
for (i = 0 ; i < dato, length: i++)
I
S y s t e m . o u t . p r i n t ( ”d a t o [ " + i + " ] = "):
d a t o [ 1 ] = L e e r . d a t o F l o a t ( );
i f ( F l o a t . i s N a N ( d a t o C i] ) ) break:
I
nElementos = i: // nú me r o de v a l o r e s leídos > ...

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

Para realizar este problem a, en función de lo expuesto necesitam os una m atriz


de enteros de dos dim ensiones. C ada elem ento actuará com o contador de la pareja
de letras correspondiente. Por lo tanto, todos los elem entos de la m atriz deben
valer inicialm ente cero.

in t[][] t a b l a = new i n t [ ' z ' ' a ’+ l ] [ ’z ' - ' a ' + 1]:

Para que la solución sea fácil, aplicarem os el concepto de m atrices asociativas


visto anteriorm ente en este m ism o capítulo; es decir, la pareja de letras a contabi­
lizar serán los índices del elem ento de la m atriz que actúa com o contador de dicha
pareja. O bserve la tabla anterior y vea que el contador de la pareja aa es el ele­
m ento (0.0) de la supuesta m atriz. Esto supone restar una constante de valor '« ’ a
los valores de los índices (carant, ca r) utilizados para acceder a un elem ento. La
variable carant contendrá el prim er carácter de la pareja y ca r el otro carácter.

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 ' ] + + :

El problem a com pleto se m uestra a continuación.


C A P ÍT U L O 7: M A TRICES 2 0 9

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 nalizando el código que m uestra la tabla de frecuencias, observam os un


prim er bucle fo r que visualiza la cabecera “a b c esta prim era línea especifica
el segundo carácter de la pareja de letras que se contabiliza; el prim er carácter
aparece a la izquierda de cada fila de la tabla. D espués observam os dos bucles fo r
anidados cuya función es escribir los valores de la m atriz tabla p o r filas; nótese
que antes de cada fila se escribe el carácter prim ero de las parejas d e letras que se
contabilizan en esa línea.

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.

R ealizar un program a que lea los pesos e im prim a el histogram a correspondiente.


Suponer que los pesos están com prendidos entre los valores 10 y 100 Kg. E n el
histogram a sólo aparecerán los pesos que se corresponden con 1 o m ás alum nos.

2. R ealizar un program a que lea una cadena de n caracteres e im prim a el resultado


que se obtiene cada vez que se realice una rotación de un carácter a la derecha so­
bre dicha cadena. El proceso finalizará cuando se haya obtenido nuevam ente la
cadena de caracteres original. Por ejem plo,

HOLA AHOL LAHO OLAH HOLA


CA PÍTU LO 7: M A TRICES 2 1 1

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.

6. A nalice el program a que se m uestra a continuación e indique el significado que


tiene el resultado que se obtiene.

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

public static byte HaceAlgo(byte car)


I
r e t u r n ( b y t e ) ( ( ( c a r & 0x01) << 7) | ( ( c a r & 0x02) << 5) |
( ( c a r & 0x04) << 3) j ( ( c a r & 0x08) << 1) j
' ! ( ( c a r ■& 0x10) » 10 j ( ( c a r ’0x200: » ' 3) \
:u-io/í ( ( c a r 0x40) ¡».:5i>i I - ( ( . c a r ¿ 0x80>:¿>>,.7)■):
212 JA VA : C U R S O DE PROGRAM A CIÓN

pu blic s t a t ic void main(S t r i n g [ ] args)


i
byte 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 e un c a r á c t e r A S C I I : “ );
c a r = ( b y t e J S y s t e m . i n . r e a d í ):
V i s u a 1 i z a r ( c a r );
S y s t e m . o u t . p r i n t l n ( ”\ n C a r á c t e r r e s u l t a n t e : “ );
car = H aceA lgo(car);
V isu alizar(car):
I
catch (IOException ignorar)!I

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

se guardará en una m atriz unidim ensional así: ■valor

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

int C r e a r M a t r i ztlni ( i n t [ ] m. int f i , int co):

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

int V isu alizar(in t f. int c. i n t [ ] m);


CA PÍTU LO 7: M ATRICES 2 1 3

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.

PASAR UNA MATRIZ COMO ARGUMENTO A UN MÉTODO


En el capítulo 4 se expuso cóm o definir un m étodo en una clase y se explicó cóm o
pasar argum entos a un m étodo. R ecuerde que los objetos pasados a los parám etros
de un m étodo son siem pre referencias a dichos objetos, lo cual significa que cual­
q u ier m odificación que se haga a esos objetos dentro del m étodo afecta al objeto
original, y las m atrices son objetos. En cam bio, las variables de un tipo prim itivo
se pasan por valor, lo cual significa que se pasa una copia, por lo que cualquier
m odificación que se haga a esas variables dentro del m étodo no afecta a la varia­
ble original.

En m ás de una ocasión, trabajando con cadenas de caracteres hem os pasado


com o argum ento una m atriz. S irva de ejem plo el m étodo M inusculasM ayusculas
expuesto en el apartado “T rabajar con cadenas de caracteres” del capítulo ante-
216 JA V A: C U R SO D E PROGRAM A CIÓN

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.

public class Test


I
static void Muí t i p l i c a r P o r D o s M a t r i z 2 D ( d o u b l e [ ] [ ] x)
I
for (in t f = 0; f < x.length; f++)
I
f o r ( i n t c = 0: c < x [ 0 ] .1e n g t h : c++)
X [ f ] [ c ] * = 2:

public static void m a in (S trin g [] args)


!
double[][] m = 1110, 20. 30}. 14 0 , 50. 601);

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

La aplicación anterior se ejecuta de la form a siguiente: el m étodo m a in crea e


inicia una m atriz m de dos dim ensiones de tipo d o u b le. D espués invoca al m étodo
M ultiplicarP orD osM atriz2D pasando com o argum ento la m atriz m; esto im plica
que el m étodo tenga un parám etro declarado así: d o u b lef / / / x. Por ser m un obje­
to, el parám etro x recibe una referencia a la m atriz m; esto es, a- alm acenará la po­
sición de m em oria de dónde se encuentra la m atriz, no una copia de su contenido.
Por lo tanto, ahora el m étodo M uItiplicarP orí)osM atriz2D tiene acceso a la m is­
ma m atriz q ue el m étodo m ain . G ráficam ente puede im aginárselo así:
C A PÍTU LO 8: M ÉTO D O S 2 1 7

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

¿C uál es el resultado? Q ue cuando el m étodo m a in visualice los elem entos de


la m atriz m , éstos aparecerán con los cam bios introducidos por el m étodo M ulti-
plicarP orD osM atriz2D . Esto es, am bos m étodos trabajan sobre la m ism a matriz.

MATRIZ COMO VALOR RETORNADO POR UN MÉTODO


Según vim os en el capítulo 4, un m étodo puede retornar un valor de cualquier tipo
prim itivo, o bien una referencia a cualquier clase de objetos. Por lo tanto, en el
caso de que un m étodo devuelva una m atriz, lo que realm ente devuelve es una
referencia a la m atriz. A clarem os esto con un ejem plo.

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.

public class Test


(
s t a t i c d o u b l e [ ] [ ] C o p i a r M a t r i z 2 D ( d o u b l e [ ] [ ] x)
i
d o u b l e [ ] [ ] z = new d o u b l e í x . l e n g t h ] [ x [ 0 ] . l e n g t h ] ;

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

public static void m a in (S trin g [] args)


I
d o u b l eC ] C 3 mi = M IO . 20. 30). 14 0 . 50, 60ll;

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

La aplicación anterior se ejecuta de la form a siguiente: el m étodo m a in crea e


inicia una m atriz de dos dim ensiones de tipo d o u b le referenciada por m i , y decla­
ra una referencia m 2 a una m atriz d e dos dim ensiones del m ism o tipo. Después
invoca al m étodo C opiarM atriz2D pasando com o argum ento la m atriz m i. Esto
im plica que ese m étodo tenga un parám etro declarado así: double[/[ ] x , para que
pueda recibir una referencia a la m atriz m i. A continuación, C opiarM atriz2D crea
una m atriz z de las m ism as características que x, copia los elem entos d e x en z y
devuelve com o resultado z. F inalm ente, la referencia devuelta p o r CopiarM a-
triz2D es alm acenada p o r el m étodo m a in en m 2, que com o com probación visua­
liza esa matriz.

Evidentem ente, el m étodo C opiarM atriz2D podría haberse diseñado según el


siguiente prototipo, trabajo que se deja com o ejercicio p ara el lector.

static void C o p ia rM a tfiz2 D (d o u b le [][] destinó. double[][] origen)

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:

double[][] mi = 1 1 1 0 , 2 0 . 3 0 1 , 1 40, 5 0 . 6011:


double[][] m2 = ( d o u b l e [ ] [ ] ) m l . c l o n e t ) :
for (in t f - 0: f < m l . l e n g t h ; f + + )
m2[f] = tint[])m l[f].clone t):

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:

void arraycopytObject origen, in t posición_origen,


Object d e stin o , in t p o sic ió n _ d e stin o .
int longitud)
C A PÍTU LO 8: M ÉTODOS 2 1 9

donde origen es la m atriz origen de los datos, posición.jorigen el índice de inicio


en la m atriz origen, destino es la m atriz destino de los datos, posición_destino el
índice de inicio en la m atriz destino y longitud es el núm ero de elem entos que se
desean copiar.

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

REFERENCIA A UN TIPO PRIMITIVO


C uando un m étodo Java invoca a otro m étodo y le pasa un argum ento de un tipo
prim itivo, pasa una copia de ese argum ento. P or ejem plo:

pu b lic s t a t ic void Increm entarlO (int param)


,1
p a r a m + = 10;

public s t a t ic void m a in (Striiig[] args)


I ¿ I"; f í o 'j '•

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.

Según lo estudiado hasta ahora, cuando se pasa un argum ento que es un o b ­


jeto , Jav a no hace una copia del objeto sobre el parám etro correspondiente del
m étodo, sino que inform a al m étodo acerca del lugar de la m em oria donde está
ese o b jeto para que puede acceder al m ism o, lo que se denom ina pasar un argu­
m ento p o r referencia. E sto es, lo qué se copia en el parám etro del m étodo es una
referencia al objeto;
220 JA V A: C U R SO D E PROGRAM A CIÓN

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:

public static void Increm entarl0(Integer param)


I
i n t v a l o r = p a r a m . i n t V a l u e ( );
v a l o r + - 10:
pa r a m = new I n t e g e r ( v a l o r ) :
I

public static void m a in (S trin g [] args)


I
I n t e g e r a r g - new I n t e g e r ( 1 2 3 4 ) : _______________
Increm entarlO(arg):
S y ste m .o u t .p rin tln (a rg .in tV a lu e ()):
I

El m étodo m a in del ejem plo anterior crea un objeto In te g e r con el valor


1 2 3 4 y alm acena una referencia a ese objeto en la variable arg. C uando m a in in­
voca a In crem en ta rlo , le pasa una referencia que este m étodo alm acena en su pa­
rám etro param . El m étodo In c rem en ta rlo obtiene el v alo r entero del objeto, lo
increm enta en 10 y crea un nuevo objeto In te g e r con el resultado, alm acenando la
referencia al m ism o en param . Esto sobreescribe la referencia anterior que alm a­
cenaba param , pero lógicam ente no afecta a la variable arg, así que el m étodo
m a in m ostrará el valor 1 2 3 4 original.

¿Q ué ha sucedido? Q ue el m étodo In c re m e n ta rlo no sólo no m odificó la es­


tructura de datos del objeto referenciado por param (ya que valor es una variable
local que no pertenece al objeto), sino que asignó a p a ra m un nuevo objeto. Para
poder m odificar el objeto pasado por referencia, la clase In te g e r debería propor­
cionar, según m uestra el ejem plo siguiente, un m étodo análogo a AsignarValor:

public static void I n c r e m e n t a r l o ! I n t e g e r param)


I
i n t v a l o r = p a r a m . i n t V a l u e ( );
v a l o r + = 10:
param .AsignarValor(valor):
I

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:

public static void Increm entarlO (int[] param)


1
param[0] + = 10:

public static void m a in (S trin g [] args)


1
i n t [ ] a r g = I 1234 I ;
Increm entarlO(arg);
S y s t e m . o u t . p r i n t l n ( a r g [ 0 ] ):

En el ejem plo anterior, el m étodo m a in define un valor de tipo int m ediante


una m atriz de un solo elem ento. D espués invoca al m étodo In c rem en ta rlo pasán­
dole com o argum ento esa m atriz, lo que supone copiar la referencia arg en el pa­
rám etro param . A hora param hace referencia a la m ism a m atriz que arg. Por lo
tanto, todos los cam bios realizados p o r el m étodo afectarán a la m atriz original.
C om o consecuencia, el resultado m ostrado p o r m a in será ahora 1244.

ARGUMENTOS EN LA LÍNEA DE ÓRDENES


M uchas veces, cuando invocam os a un program a desde el sistem a operativo, ne­
cesitam os escrib ir uno o m ás argum entos a continuación del nom bre del progra­
ma, separados p o r un espacio en blanco. P or ejem plo, piense en la orden Is -l del
sistem a operativo UNI X o en la orden clir /p del sistem a operativo M S-DOS.
T anto Is com o d ir son program as; -/ y /p son opciones o argum entos en la línea de
órdenes que pasam os al program a para que tenga un com portam iento diferente al
q ue tiene de form a predeterm inada; es decir, cuando no se pasan argum entos.

De la m ism a form a, nosotros podem os construir aplicaciones Java que adm i­


tan argum entos a través de la línea de órdenes ¿Q ué m étodo recibirá esos argu­
m entos? El m étodo m ain. ya que este m étodo es el punto de entrada a la
aplicación y tam bién el punto de salida. Su definición, una vez más, es com o se
m uestra a continuación:

public static void m a in ( S t r in g [ ] args)


i
// C u e r p o d e l m ét o d o
222 JA VA : C U R SO D E PROGRAM A CIÓN

C om o se puede observar, el m étodo m a in tiene un argum ento args q u e es una


m atriz unidim ensional d e tipo S tr in g . El nom bre args puede ser cualquier otro.
Esta m atriz alm acenará los argum entos pasados en la línea de órdenes cuando se
invoque a la aplicación para su ejecución de la form a que se observa a continua­
ción. O bserve q ue cada argum ento está separado p o r un espacio.

java M iA plicación argumentol argumento2 ...

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

El código de la aplicación propuesta, se m uestra a continuación.

public class Test


I
public static void m a in (S trin g [] args)
I
// C ó d i g o común a t o d o s l o s c a s o s
System .out.println("Argum entos: "):
i f ( a r g s . 1 e n g t h = = 0)
I
// E s c r i b a a q u í e l c ó d i g o q u e s ó l o se debe e j e c u t a r cuando
// no s e p a s a n a r g u m e n t o s
C A PÍTU LO 8: M ÉTO D O S 2 2 3

System .out.printlní" nin g u n o");’ 'ip c jc i^ y -to ss-r-*'r-r«-rz-f


! v
el se
•'! I.l UÚVI..I l: .■ uí tn.il v - i r -u w n , v /•!>- o . u
b o o l e a n a r g u m e n t o _ k ** f a l s e , argumento_l “ false,
¡•¡i a r g u m e n t o _ n false:

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

if ( a r g s [ i ] . compareTóf" - k " ) “ 0) argumento_kl - true:


if ( a r g s [ i ] . c o m p a r e T o ( " - l " ) =■ 0) argumento_l: » true:
if ( a r g s [ i ] . c o m p a r e T o t H- n " ) = 0 ) argumento_n - true:
,, ; i., I •: ••• iiii v!> p v i ó u » -i: ■:! ' <r •

if (argumento_k) // s i se pasó el argumento -k:


I
7 / E s c r i b a aq u í el c ó d i g o que s ó l o se debe e j e c u t a r cuando
// s e p a s a e l a r g u m e n t o - k
System .out.printlní" -k");
I

if ( a r g u m e n t o _ l ) // s i se pasó el argumento -1:


, .. ::.r . I
II E s c r i b a a q u í e l c ó d i g o q u e s ó l o se debe e j e c u t a r cuando
// s e p a s a e l a r g u m e n t o -1
System .out.printlní ” - l n ):

if (argumento_n) // s i se pasó el argumento -n:


I
// E s c r i b a a q u í e l c ó d i g o q u e s ó l o se debe e j e c u t a r cuando
// s e p a s a e l a r g u m e n t o -n
System .out.p rintlnC -n "):
I
I
// C ó d i g o común a t o d o s los casos

Al ejecutar este program a, invocándolo com o se ha indicado anteriorm ente, se


obtendrá el siguiente resultado:

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.

¿C uándo es eficaz escribir un m étodo recursivo? La respuesta es sencilla,


cuando el proceso a program ar sea por definición recursivo. P or ejem plo, el cál­
culo del factorial de un núm ero, n ! = n (n -I)!, es p o r definición un proceso recur­
sivo que se enuncia así: fa cto ria l(n ) = n * fa c to ria l(n -l)

Por lo tanto, la form a idónea de program ar este problem a es im plem entando


un m étodo recursivo. C om o ejem plo, a continuación se m uestra un program a que
visualiza el factorial de un núm ero. Para ello, se ha escrito un m étodo fa cto ria l
que recibe com o parám etro un núm ero entero positivo y devuelve com o resultado
el factorial de dicho núm ero.

public class Test


I
// C á l c u l o d e l f a c t o r i a l de un número
p u b l i c s t a t i c l o n g f a c t o r i a l ( i n t n)
I
if ( n — 0)
r e t u r n 1;
else
return n * f a c t o r i a l ( n - 1 );
1

public static void m a in ( S t r in g [ ] args)


I
i n t numer o :
long fa c:

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

fac = fa c to ria l(n u m e ro );


System .out.printlní"\nEl factorial de " + numero + " e s : " + fac):

En la tabla siguiente se ve el proceso seguido por el m étodo fa cto ria l, durante


su ejecución para n = 4.
C A PITU LO 8: M ÉTO D O S 2 2 5

N ivel de recursión P roceso de ida P roceso de vuelta

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.

Los algoritm os recursivos son particularm ente apropiados cuando el problem a


a resolver o los datos a tratar se definen en form a recursiva. Sin em bargo, el uso
de la recursión debe evitarse cuando haya una solución obvia por iteración.

En aplicaciones prácticas es im perativo dem ostrar que el nivel m áxim o de re­


cursión es, no sólo finito, sino realm ente pequeño. La razón es que, por cada eje­
cución recursiva del m étodo, se necesita cierta cantidad de m em oria para
alm acenar las variables locales y el estado en curso del proceso de cálculo con el
fin de recuperar dichos datos cuando se acabe una ejecución y haya que reanudar
la anterior.

VISUALIZAR DATOS CON FORMATO


Los resultados producidos por las aplicaciones que hem os realizado hasta ahora
han sido m ostrados sin aplicar ningún tipo de form ato. Pero quizás en alguna o ca­
sión necesitem os expresar una cantidad:

• incluyendo la com a de los decim ales y el punto de los m iles,

• con un núm ero determ inado de dígitos enteros com pletando con ceros por la
izquierda si fuera necesario,

• con un núm ero determ inado de decim ales y ajustada a la derecha,

• o bien una serie de cantidades decim ales, una debajo de otra, ajustadas por la
com a.

Si en lugar de cantidades hablam os de fechas, tam bién podríam os requerir


diferentes m odos de presentación.
226 JA VA : C U R SO D E PRO G R A M A CIÓ N

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

La clase N u m b e r F o r m a t es la clase base abstracta para todos los form atos


num éricos. La clase D a te F o rm a t es tam bién una clase abstracta para los form atos
de fechas y horas. Pero las clases que son particularm ente útiles y que estudiam os
a continuación son: D e c im a lF o rm a t. S im p le D a t e F o r m a t y M e s sa g e F o rm a t.

Dar formato a números

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:

Sím bolo Significado_______________________________________________________


0 R epresenta un dígito cualquiera, incluyendo los ceros no significati­
vos.
# R epresenta un dígito cualquiera, excepto los ceros no significativos.
R epresenta el separador decim al.
R epresenta el separador de los miles.
E Form ato científico. E, separa la m antisa y el exponente.
A ctúa com o separador cuando se especifican varios form atos.
S igno negativo de form a predeterm inada.
C A PÍTU LO 8: M ÉTODOS 2 2 7

% M ultiplicar p o r 100 y m ostrar el sím bolo %.


a R epresenta el sím bolo m onetario.
carácter C u alquier carácter puede ser utilizado com o prefijo o com o sufijo.
P o r ejem plo $ o ‘\u 0 0 2 4 \

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

Si el núm ero de dígitos correspondiente a la parte entera excede el núm ero de


posiciones especificado para la m ism a, el form ato se extiende en lo necesario. Si
el núm ero de dígitos decim ales excede el núm ero de posiciones especificado para
los m ism os, la parte decim al se trunca redondeando el resultado.

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

El m étodo g e tC u rre n c y ln sta n c e devuelve el form ato m onetario de la locali­


dad actual cuando no se especifica una, o el de la especificada. Por ejem plo:

L ó c a l e e n _ U S = new L o c a l e ( “e n " . " U 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 l n s t a n c e ( e n _ U S ) ;

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

U na localidad no es un idiom a, ya que un m ism o idiom a se puede hablar en varios


países. C uando escriba un program a internacional tendrá que definir la localidad
228 JA VA: C U R SO D B PROGRAM A CIÓN

actual y el conjunto de localidades que soportará el program a. Las localidades son


definidas en Java por la clase L ó c a le incluida en el paquete java.u til. Un objeto
de la clase L ó c a le es sim plem ente un identificador para una localidad específica.
P or ejem plo, el siguiente código crea dos objetos. p a(s[0] y p a ís[¡], uno para el
español de E spaña y otro para el inglés de Estados Unidos.

Local ef] paí s =


I
new L o c a l e t " e s ” , "ES") ,
new L o c a l e ( " e n ” . "US” ).
I;

U tilizando los m étodos com entados en el apartado anterior, se puede obtener


el form ato predeterm inado para cualquiera de estos países. Por ejem plo:

Deci mal Formal d f =


(Deci mal Fo r ma l ) De c i ma l Fo r ma t . g e t Nu mb e r I n s t a n c e ( p a i s [ i ] ) :

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

Para utilizar el objeto F ie ld P o sitio n definido, el m étodo fo rm a t invocado a


través del objeto form ateador debe ten er tres parám etros: el valor num érico a for­
m atear, un objeto S t r in g B u ff e r donde se alm acenará el núm ero form ateado y el
objeto F ie ldP ositio n . Para realizar la alineación habrá que añadir al principio del
objeto S t r in g B u ff e r un núm ero de espacios en blanco igual al espacio d e im pre­
sión deseado m enos el valor devuelto por g e tE n d ln d e x .

¿Q ué devuelve g e tE n d ln d e x ? Si el objeto F ie ld P o sitio n se creó basado en la


constante IN T E G E R _F IE L D , el m étodo g e tE n d ln d e x devolverá el núm ero de d í­
gitos enteros del dalo a form atear, y si se creó basado en la constante FRAC-
T IO N _F IE L D , el núm ero total de dígitos (enteros y decim ales). Para aclarar lo
expuesto observe el siguiente ejem plo, continuación del anterior:
C A PÍTU LO 8: M ÉTO D O S 2 2 9

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.

Clase para formatos numéricos

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

static public String Form atoPertString patrón, double dato)


I
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 t p a t r ó n ) ;
S trin g salida = form ato.form at(dato):
return sa lid a:
I
2 3 0 JA V A: C U R SO D E PROGRAM A CIÓN

static public StringBuffer A linD er(String patrón, double dato)


I
F i e ld P o s i t i o n fp =
new F i e l d P o s i t i o n ( N u m b e r F o r m a t . F R A C T 1 0 N _ F I E L D ) ;
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 ( p a t r ó n ) ;
S t r i n g B u f f e r s a l i d a = new S t r i p g B u f f e r t );
form ato.form aUdato. sa lid a , fp);
f o r ( i n t i = 0; i < ( p a t r ó n .1e n g t h ( ) - f p . g e t E n d l n d e x í ) ) ; i++)
sal i d a . i n s e r t ( 0 . ’ ");
return sa lid a;
1

static public String Form atoPa1s(String patrón, double dato.


,... L ó c a l e , l u g a r )
i ;.. 'y'
Decim alFormat d f =
... . y . . : ’, , ; /’.'V;Vré
( D e c i m a l F o r m a t ) D e c i m a l F o r m a t . g e t N u m b e r I n s t a n c e ( 1 u g a r );
d f . a p p l y P a t t e r n ( p a t r ó n );
S t r i n g s a l i d a = d f . format-(dato.-)';,
return sal id a ;

P u e d e , si lo desea, a ñ a d ir esta c la se a la carp eta e sp e c ific a d a p o r la va ria b le


d e e n to rn o C L A S S P A T H . A c o n tin u a c ió n e s c r ib im o s u n a a p lic a c ió n q u e n o s per­
m ita p ro b a r ca d a u n o d e lo s m é t o d o s d e la c la s e anterior.

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

C om o ejercicio, ejecute esta aplicación y analice los resultados.

Dar formato a fechas/horas


Para dar form ato a una fecha/hora dada, prim ero hay que crear un objeto forma-
teador de la clase S im p le D a t e F o r m a t basado en un form ato específico, y luego
u tilizar su m étodo fo rm a t para convertir la fecha/hora en una cadena construida a
p artir del form ato elegido. Los sím bolos que se pueden u tilizar para especificar un
determ inado form ato son:

Sím bolo Significado_____________ Presentación____________Ejem plo__________


y año num érica 2002
M m es del año num érica y alfabética 08 y agosto
d día del m es num érica 15
ii hora (1 a 12) num érica 10
H hora (0 a 23) num érica 13
m m inutos num érica 30
s segundos num érica 55
S m ilisegundos num érica 658
E día de la sem ana alfabética jueves
D día del año num érica 227
- día de la sem ana del m es num érica 3 (3o X de agosto)
w sem ana del año num érica 24
W sem ana del m es num érica 2
3 m arca am /pm alfabética PM
Z zona horaria alfabética G M T+02:00

En las presentaciones alfabéticas, 4 o m ás sím bolos dan lugar a la forma


com pleta (por ejem plo, M M M M da lugar al nom bre del m es com pleto: agosto)-, y
232 JAVA: C U R S O DE PROGRAM A CIÓN

m enos de 4 da lugar a la form a abreviada o a la num érica (por ejem plo, M M M da


lugar a una abreviatura, ago, M M a un núm ero de dos dígitos, 08, y M a un núm e­
ro de un dígito, 8).

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

Un objeto Date, construido sin argum entos, encapsula el tiem po en milise-


gundos transcurridos desde el 1 de enero de 1970.

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

o bien con el estilo y la localidad; por ejem plo:


CA PÍTU LO 8: M ÉTODOS 2 3 3

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

donde p a ís es un objeto L ó c a le según se explicó anteriorm ente en el apartado


“L ocalidad” .

Dar formato a mensajes


Para d ar form ato a un m ensaje que se desea construir durante la ejecución, com o
p o r ejem plo "Fueron verificados 1234 ficheros de la unidad C: en 125 segundos” ,
hay que crear un objeto form ateador de la clase M e s sa g e F o rm a t. Esta clase pro­
porciona un m edio para construir m ensajes con partes variables que serán reem ­
plazados durante la ejecución. P or ejem plo:

Object[] argumentos = (new L o n g ( 1 2 3 4 ) , " C : “ . new L o n g ( 1 2 5 ) 1 :

M e s s a g e F o r m a t m e n s a j e = new M e s s a g e F o r m a t ! " F u e r o n v e r i f i c a d o s " +


" 1 0 1 f i c h e r o s d e l a u n i d a d I I I en 12 I s e g u n d o s " ) :
Syste m .o u t.p rin tln (m e n sa je .fo rm a t(a rg u m e n to 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:

int b i n a r y S e a r c h ( tipo[] m, t ipo clave)

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.

El valor devuelto es un entero correspondiente al índice del elem ento que


coincide con el valor buscado. Si el valor buscado no se encuentra, entonces el
valor devuelto es: -(p u n to de inserción) - 1. El valor de punto de inserción es el
234 JA VA : C U R S O DE PROGRAM A CIÓN

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

C om o ejem plo, analice el siguiente código:

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:

b o o l e a n e q u a l s í tipo[] mi. t i p o í ] m2)

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

void fiH (t?p o[] m, t i p o v a l o r )


void fill(típ o [] m, i n t d e s d e l n d . int hastalnd, tipo valor)

donde m es la m atriz y valor es el valor a asignar. C uando sólo queram os asignar


el valor a un rango de elem entos, utilizarem os el segundo form ato de ñ ll donde
d esd eln d y h a stalnd definen ese rango, tipo es cualquier tipo d e datos de los si­
guientes: boolean. byte, char, short, int. long. float, d o u b le y O bject.

Un ejem plo de cóm o utilizar este m étodo es el siguiente:

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)

donde m es la m atriz a ordenar. C uando sólo queram os ordenar un rango de ele­


m entos, utilizarem os el segundo form ato de so rt donde d esd e ln d y hastalnd defi­
nen los lím ites de ese rango, tipo es cualquier tipo de datos de los siguientes:
byte, ch ar, short, int, long, float, d o u b le y O bject.

C om o ejem plo, puede probar los resultados que produce el siguiente código:

doub le[] a = 155.50.45.40.35.30.25.20.15.10):


A r r a y s . s o r t ( a ):

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

g e tC la ss y clone, los hem os utilizado en capítulos anteriores; y otros, com o


equals, to S tr in g y finalize son expuestos a continuación.

boolean equals(Object obj)


El m étodo e q u a ls de la clase O b je c t retorna tru e si y sólo si las dos referencias
com paradas señalan al m ism o objeto; esto es, proporciona el m ism o resultado que
el operador Esto es así porque la intención es proporcionar un m étodo que
pueda ser sobreescrito en cada una de las subclases de O b je c t que requieran una
funcionalidad m ás específica. Por ejem plo, considerem os el siguiente código que
define dos referencias a otros dos objetos de la clase S trin g :

public class Test


I
public static void m a in (S trin g [] args)

S t r i n g s t r l - new S t r i n g ( " a b e " );


S t r i n g s t r ¡ ? = new S t r i n g t " a b e " ) :
/ / Comparar r e f e r e n c i a s
if (strl — str2)
S y s t e m . o u t . p r i n t l n í " L a s r e f e r e n c i a s s o n al mismo o b j e t o " ) ;
el se
S y s t e m . o u t . p r i n t l n t " L a s r e f e r e n c i a s s on a o b j e t o s d i f e r e n t e s " ) ;
/ / Comparar c o n t e n id o s
i f ( s t r l . e q u a 1s ( s t r 2 ))
S y s t e m . o u t . p r i n t í n ( " M i s m o c o n t e n i d o " );
el se
S y s t e m . o u t . p r i n t l n ( " D i f e r e n t e c o n t e n i d o " ):
I
1

La expresión s tr l == str2 será true si la referencia s tr l es igual a la referen­


cia str2\ esto es, si am bas variables contienen idénticos valores, los cuales se c o ­
rresponderán con la posición de m em oria donde se localice un objeto.

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.

C uando se ejecute la aplicación anterior se obtendrá el siguiente resultado:

Las r e f e r e n c i a s son a o b je to s diferentes


Mismo c o n t e n i d o
CA PÍTU LO 8: M ÉTO D O S 2 3 7

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

El ejem plo siguiente perm ite verificar lo expuesto.

public class Test


I
public static void m a in (S trin g [] args)
I
T e s t o b j = new T e s t ( ):
S t r i n g s:
s - 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 í )):
System .out.p rintln(s):
S y s t e m . o u t . p r i n t l n ( o b j . t o S t r i n g ( ) ) ; // mi s mo r e s u l t a d o que s
I
I

C uando se ejecute el ejem plo anterior la línea som breada dará lugar al si­
guiente resultado:

Test@73bf4fel

C on respecto al m étodo t o S tr in g direm os lo m ism o que para equals; esto es,


todas las subclases de O b je c t deberían sobreescribir el m étodo to S tr in g para que
proporcione una inform ación que sea útil. P or ejem plo, la clase S t r in g sobrees-
cribe este m étodo para que retom e el propio objeto S t r in g que recibe el m ensaje
to Strin g.

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.

El m étodo finalize de la clase O b je c t no ejecuta ninguna acción en especial;


sim plem ente retorna norm alm ente. Las subclases de O b je c t deberán sobreescribir
la definición de este m étodo sólo cuando necesiten ejecutar alguna operación de
finalización especial.
238 JA V A : C U R S O DE PRO G R A M A CIÓ N

MÁS SOBRE REFERENCIAS Y OBJETOS String


En el capítulo anterior hicim os una breve exposición acerca d e cóm o crear un
objeto S t r in g a partir de un literal o a partir d e o tro S trin g . Por ejem plo:

Strin g str = "abe";

Este ejem plo crea un objeto S t r in g con el contenido “abe” . D icho proceso
puede realizarse tam bién así:

String s t r = new S t r i n g ( " a b e " ):

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 .

C ada literal de caracteres es representado internam ente por un objeto Strin g .


A sí m ism o. Java m antiene un área de m em oria destinada a alm acenar tales objetos
S trin g . Entonces, cuando Java com pila un literal (en el ejem plo “abe” ) añade el
objeto S t r in g correspondiente, a dicho área de m em oria; posteriorm ente, si apare­
ce el m ism o literal en cualquier otra parte del código de la clase, el com pilador no
añade un nuevo objeto, sino que utiliza el que hay. E sta form a de proceder ahorra
m em oria y no causa problem as porque los S t r in g son objetos no m odificables;
p o r lo tanto, no hay posibilidad de que una parte del código pueda m odificar un
objeto S t r in g com partido por otra parte de código.

A nteriorm ente en este capítulo, vim os cóm o utilizar el m étodo e q u a ls para


verificar los contenidos de dos objetos S trin g . Tam bién vim os que, a diferencia
del m étodo equals, el operador = = no com para los contenidos de los objetos refe-
renciados sino las referencias. E sto nos perm itirá analizar m ediante algunos ejem ­
plos lo expuesto en el párrafo anterior:

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

En cam bio, el resultado de la expresión s tr l = = str2 es true porque am bos


identificadores se refieren al m ism o objeto. A nalicem os p o r qué.

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.

D urante la ejecución, la prim era línea construye un nuevo objeto S t r in g du­


plicando el existente en el área de m em oria citada y alm acena una referencia al
mismo en s tr l. En cam bio, la segunda línea sim plem ente alm acena en str2 una
referencia al objeto que ya existe en el área de m em oria destinada por el com pila­
dor a objetos S tr in g . La figura siguiente m uestra esto gráficam ente:
240 JA V A: C U R SO D E PROGRAM A CIÓN

N o obstante, es posible colocar los objetos S t r in g creados dinám icam ente


(objetos creados durante la ejecución m ediante el operador new ) en el espacio de
m em oria reservado por el com pilador Java para tales objetos utilizando su m étodo
intern. Esto puede redundar en un ahorro de m em oria en program as que utilicen
una gran cantidad de objetos S trin g . P o r ejem plo, las líneas de código siguiente:

S t r i n g s t r l = new S t r i n g t " a b e " ) : // c r e a r un n u e v o o b j e t o


s t r l = s t r l . i n t e r n ( ):
Strin g str2 = "abe” :

son equivalentes a:

String s trl - "abe” ;


S t r i ng s t r 2 - "abe":

El m étodo in te rn coloca el objeto que recibe el m ensaje in te rn en el espacio


de m em oria reservado p o r el com pilador Java para los objetos S t r in g si aún no
estaba, o si estaba lo reutiliza.

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:

strl = s t r l .re p la c e t'a ', ’ x ’ ):

Partiendo de que s tr l era el S t r in g “abe”, después de ejecutarse la línea de


código anterior se genera un nuevo objeto “xbc” referenciado por s tr l. Esto hace
que el objeto referenciado por str2. que antes de la ejecución era el m ism o que el
referenciado por s tr l, perm anezca inalterado. El nuevo objeto generado no se
añadirá al espacio reservado para los objetos S t r in g a no ser que se invoque ex­
plícitam ente al m étodo intern.

EJERCICIOS RESUELTOS

1. Un algoritm o que genere una secuencia aleatoria o aparentem ente aleatoria de


núm eros, se llam a generador de núm eros aleatorios. M uchos program as requieren
C A PÍTU LO 8: M ÉTODOS 2 4 1

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:

rk = (m ultiplicador * rk.¡ + increm ento) % m ódulo

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 r a n d o m de la clase M a t h del paquete ja v a .la n g, o bien los m éto­


dos de la clase R a n d o m del paquete ja v a .u til están basados en este algoritm o.

El siguiente m étodo utiliza el algoritm o d e congruencia lineal para generar un


núm ero aleatorio entre 0 y 1, y no causará sobrepasam iento en un ordenador que
adm ita un rango de enteros de -2 31 a 2 3l- i .

public static double rnd(in t[] raridom)


I
randomíO] = (25173 * randomíO] + 13849) % 65536:
return ( (double)random [0] / 65535):
I

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

public class CRandom


I
7/ Nú me r o s a l e a t o r i o s e n t r e 0 y 1
p u b l i c s t a t i c d o u b l e r n d ( i n t [ ] ran do m)
I
randomíO] = (25173 * randomíO] + 13849) X 65536:
r e t u r n ( ( d o u b l e ) randomíO] / 6 5535):
242 JA V A: C U R SO DE PROGRAM A CIÓN

public static void m a in ( S t r in g [ ] args)


I
i n t i n i c i o = ( i n t ) ( ( n e w D a t e ( ) ) . g e t T i m e ( ) % 6 5 5 3 6 ): // s e m i l l a
i n t [ ] rando m = ( i n i c i o ) : II r a n d o m = núm ero e n t r e 0 y 65535

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

2. U tilizando el m étodo ra n d o m de la clase M a t h del paquete ja v a .la n g, realizar un


program a que m uestre 6 núm eros aleatorios diferentes entre 1 y 4 9 ordenados as­
cendentem ente.

Para producir enteros aleatorios en un intervalo dado puede u tilizar la fórm u­


la: P arte_entera_de((lím iteSup ■lím ite ln f + l ) * random + lím itelnf).

La solución al problem a planteado puede ser de la siguiente forma:

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

• O btenem os el siguiente núm ero aleatorio y verificam os si ya existe en la m a­


triz, en cuyo caso lo desecham os y volvem os a obtener otro. E ste proceso lo
repetirem os hasta haber generado todos los núm eros solicitados.

for (i =0; i < n.length; i++)


I
CA PÍTU LO 8: M ÉTODOS 2 4 3

n[i ] - ( i n t ) ( ( 1 1miteSup - l i m i t e l n f + 1) * M ath. random() +


1i m i t e l n f ) ;
f o r ( k = 0; k < i ; k + + )
i f <n [ k ] = = n [ i ] ) II ya e x i s t e
I
i - -:
break:

L a sentencia fo r externa define cuántos núm eros se van generar. C uando se


genera un núm ero se alm acena en la siguiente posición de la m atriz. Después,
la sentencia fo r interna com para el últim o núm ero generado con todos los
anteriorm ente generados. Si ya existe, se decrem enta el índice i de la matriz
para que cuando sea increm entado de nuevo por el fo r externo apunte al ele­
m ento repetido y sea sobreescrito p o r el siguiente núm ero generado.

• 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] + ” "):

E l program a com pleto se m uestra a continuación.

import j a v a .u t i 1 .*;

public class C Ra ndo mJ a v a


1
// O b t e n e r n ú m e r o s d e n t r o de un r a n g o
pu b lic s t a t ic void m a in (S trin g [] args)
I
in t llm ite Su p = 49, l i m i t e l n f = 1;
i n t n [ ] = new i n t [ 6 ] , i . k:

for (i - 0; i < n.length; 1++)


I
II O b t e n e r un número a l e a t o r i o
n [ i ] - ( i n t ) ( ( 1 1 m i t e S u p - l i m i t e l n f + 1) * M a t h . r a n d o m í ) +
lim itelnf);
II V e r i f i c a r s i ya e x i s t e e l ú l t i m o n ú m ero o b t e n i d o
f o r ( k = 0; k < i ; k++)
i f ( n [ k ] = = n [ i D ) // y a e x i s t e
I
i--; II i s e r á i n c r e m e n t a d a p o r e l for externo
break; II s a l i r de e s t e f o r
I
244 JA V A: C U R SO DE PRO G R A M A CIÓ N

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

3. R ealizar un program a que partiendo de dos m atrices de cadenas de caracteres


clasificadas en orden ascendente, construya y visualice una tercera m atriz también
clasificada en orden ascendente. L a idea que se persigue es construir la tercera
lista clasificada; no construirla y después clasificarla em pleando el m étodo so rt.

Para ello, el m étodo m a in proporcionará las dos m atrices e invocará a un


m étodo cu y o prototipo será el siguiente:

int Fusionar(String[] listal. Strin g[] 1 i s t a 2. Strin gf] lista3);

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.

El proceso de fusión consiste en:

a) Partiendo de que y a están construidas las dos m atrices de partida, tom ar un


elem ento de cada una de las m atrices.

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

d) C uando no queden m ás elem entos en una de las dos m atrices de partida, se


copian directam ente en la m atriz resultado, todos los elem entos que queden en
la otra m atriz.

El program a com pleto se m uestra a continuación.

public class CFusionarListas


I
// F u s i o n a r d o s l i s t a s c l a s i f i c a d a s
public s t a t ic in t F u sio n a r(S trin g [] listaA, StringC ] listaB .
Strin g[] listaC)
C A P ÍT U L O 8: M ÉTODOS 2 4 5

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

fo r (ind = indB; i nd < 1 i s t a B . 1 e n g t h ; in d + + )


1istaC[indC++] - 1i staBC i n d ] :

return 1;

static p u b lic void m a in (S trin g [] args)


I
// I n i c i a m o s l a s l i s t a s a c l a s i f i c a r ( p u e d e s u s t i t u i r e s t e
// p r o c e s o , p o r o t r o de l e c t u r a c o n e l f i n de t o m a r l o s
// d a t o s de l a e n t r a d a e s t á n d a r ) .
S t r i n g [ ] l i s t a l - I "A n a ". "Carmen". " D a v id " .
"Francisco". "Ja v ie r". "Jesús",
"José", "Josefina". "L u is".
" M a r í a ” . " P a t r i c i a ” . " S o n i a " I:

StringC ] lista2 - I "A g u stín ", "Belén". "D aniel".


"Fernando". "Manuel".
" P e d r o " . " R o s a " . " S u s a n a " I;

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

O bserve que el m étodo F usionar copia referencias. C om o se expuso ante­


riorm ente en este m ism o capítulo, esta form a de proceder ahorra m em oria y no
causa problem as porque los S trin g son objetos no m odificables; por lo tanto, no
hay posibilidad de que una parte del código pueda m odificar un objeto S trin g
com partido por o tra parte d e código.

4. E scribir un program a que calcule la serie:

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:

a) E scribir un m étodo que tenga el siguiente prototipo:

static double e x p o n e n c ia l( f l o a t x);

Este m étodo devolverá com o resultado el v alor aproxim ado de ex.

b) E scribir el m étodo m a in para que invoque al m étodo exponencial y com prue­


be q ue para x igual a 1 el resultado es el núm ero e .

El program a com pleto se m uestra a continuación.

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

public static void m a in (S trin g [] args)


I
d o u b l e ex p . x:
S y s t e m . o u t . p r i n t t " V a l o r de x : " ) ; x = L e e r . d a t o F l o a t t ):
exp = e x p o n e n c i a l ( x ) :
S y s t e m . o u t . p r i n t í n ( " e x p t " + x + ") = " + exp):

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

U na vez finalizada la introducción de las listas de palabras pasam os al m odo tra­


ducción, de form a que si tecleam os g ree n , la respuesta ha de ser verde. Si la pala­
bra no se encuentra se em itirá un m ensaje que lo indique.

El program a constará al m enos de dos m étodos:

a) crearD iccionario. Este m étodo creará el diccionario.


b) traducir. E ste m étodo realizará la labor de traducció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

R ealizar un program a que visualice un cuadrado m ágico de orden im par n. El


program a verificará que n es im par y que está com prendido entre 3 y 15.

U na form a de construirlo puede ser: situar el núm ero 1 en el centro de la primera


línea, el núm ero siguiente en la casilla situada encim a y a la derecha, y a sí sucesi­
vam ente. E s preciso tener en cuenta que el cuadrado se cierra sobre sí m ism o, esto
es, la línea encim a de la prim era es la últim a y la colum na a la derecha de la últi­
ma es la prim era. S iguiendo esta regla, cuando el núm ero caiga en una casilla
ocupada, se elige la casilla situada debajo del últim o núm ero situado.

Se deberán realizar al m enos los m étodos siguientes:

a) eslm par. E ste m étodo verificará si n es im par.


b) cuadradoM ágico. Este m étodo construirá el cuadrado m ágico.

3. R ealizar un program a que:

a) Lea dos cadenas de caracteres denom inadas c a d e n a l y cadena2 y un número


entero n.

b) L lam e a un método:

static int c o m p c a d s í c a d e n a l. cadena2, n );

que com pare los n prim eros caracteres de c a d e n a l y de cadena2, y devuelva


com o resultado un valor entero:

0 si ca d e n a l y cadena2 son iguales


1 si ca d e n a l es m ayor q ue c a d e n a l (los n prim eros caracteres)
-1 si ca d e n a l es m enor q ue cadena2 (los n prim eros caracteres)

Si n es m enor que 1 o m ayor que la longitud de la m enor de las cadenas, la


com paración se hará sin tener en cuenta este parám etro.

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:

static void lee rM atriz2D (float[][] m):

El parám etro m del m étodo leerM atr¡z2D es la m atriz cuyos elem entos deseam os
leer.

static float[] sum aColsM atriz2D(float[][] m);

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.

5. Escribir un program a para evaluar la expresión (ax + b y f . Para ello, tenga en


cuenta las siguientes expresiones:

[k) k\ (n —k)\

/?! = /? * ( / ? - ! ) * ( » - 2 )*...*2 * 1

a) E scribir un m étodo cuyo prototipo sea:

static long f a c t o r ia l( int n);

El m étodo fa cto ria l recibe com o parám etro un entero y devuelve el factorial
del m ism o,

b) Escribir un m étodo con el prototipo:

static long com binaciones( int n. int k);

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

c) E scribir un m étodo que tenga el prototipo:

static long potencia(int base, i n t exponente):


250 JA V A: C U R SO D E PROGRAM A CIÓN

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.

d) El m étodo m a in leerá los valores de a, b, n , x e y , y utilizando los m étodos


anteriores escribirá com o resultado el valor de (a x + b y )".
PARTE

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.

DEFINICIÓN DE UNA CLASE

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.

Según hem os aprendido, la definición de una clase consta de dos partes: el


nom bre de la clase precedido por la palabra reservada class. y el cuerpo de la cla­
se encerrado entre llaves. Esto es:

class nom bre je ta s e


{
cuerpo de la clase
)
254 JA V A: C U R SO DE PRO G R A M A CIÓ N

El cuerpo de la clase en general consta de m odificadores de acceso (public,


p ro te c te d y p rív a te ), atributos, m ensajes y m étodos. Un m étodo im plícitam ente
define un m ensaje (el nom bre del m étodo es el m ensaje).

Por ejem plo, un círculo puede ser descrito p o r la posición x , y de su centro y


por su radio. H ay varias cosas que nosotros podem os hacer con un círculo: cal­
cular la longitud de la circunferencia, calcular el área del círculo, etc. C ada cír­
culo es diferente (por ejem plo, tienen el centro o el radio diferente); pero visto
com o una clase de objetos, el círculo tiene propiedades intrínsecas que nosotros
podem os agrupar en una definición. El siguiente ejem plo define la clase Círculo.
O bservar cóm o los atributos y los m étodos form an el cuerpo 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 longCircunferencia()


I
return 2 * Math.PI * radio;

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.

Es posible asignar un valor inicial a un atributo de una clase. P or ejem plo, en


la clase C írculo podem os iniciar el radio con el valor 1, aunque generalm ente esto
no se hace, ya q ue com o expondrem os un poco m ás adelante este tipo de opera­
ciones son típicas del constructor de la clase:

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:

Punto(double ex. double cy) I x = ex; y = cy; I


256 JA V A: C U R SO DE PROGRAM A CIÓN

class Circulo

p r i v a t e Punto cen tro: II 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 : // r a d i o d e l c i r c u l o
11...
I

El orden de las clases es indiferente. E sta form a de proceder ya ha sido utili­


zada en capítulos anteriores. Recuerde, por ejem plo, que en m ás de una ocasión
hem os declarado un atributo de la clase S trin g .

Métodos de una clase


Los m étodos generalm ente form an lo que se denom ina interfaz o m edio de acceso
a la estructura interna de los objetos; ellos definen las operaciones que se pueden
realizar con sus atributos. D esde el punto de vista de la PO O , el conjunto de todos
estos m étodos se corresponde con el conjunto de m ensajes a los que los objetos de
una clase pueden responder.

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

public double IongC ircunferencia()


I
return 2 * Math.PI * radio:
I
11...
C A P ÍT U L O 9: CL A SES Y PAQUETES 2 5 7

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.

Control de acceso a los miembros de la clase

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:

1. Q ue el usuario no tenga acceso directo a la estructura de datos interna de la


clase, para que no pueda generar código basado en esa estructura.

2. Que si en un m om ento determ inado alteram os la definición de la clase, ex­


cepto el prototipo de los m étodos, todo el código escrito por el usuario basado
en estos m étodos no tendrá que ser retocado.

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

En m uchos de los ejem plos realizados en los capítulos anteriores, no se ha espe­


cificado ningún tipo de control de acceso. Esto es, los atributos y los m étodos se
declararon de form a análoga a com o puede observar en el ejem plo siguiente:

class CRacional
I
i n t Numerador:
i n t Denominador:

v o i d A s i g n a r D a t o s f i n t num, int den)


I
258 JA V A: C U R SO D E PROGRAM A CIÓN

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.

C om o se puede observar este tipo de control de acceso no tiene m ucho dom i­


nio sobre el m ism o. Si lo que se pretende es tener un control preciso sobre cóm o
va a ser utilizada nuestra clase por otras, deberem os utilizar los m odificadores
p rív a te , p ro te c te d o p u b lic en vez de aceptar el tipo de control predeterm inado.

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

IMPLEMENTACIÓN DE UNA CLASE


L a program ación orientada a objetos sugiere separar la im plem entación de cada
clase en un fichero .class, fundam entalm ente para posteriorm ente reutilizar y
m antener dicha clase. C om o ejem plo, diseñarem os una clase que alm acene una
fecha, verificando que es correcta; esto es, que el día esté entre ios lím ites 1 y días
del m es, que el m es esté entre los lím ites 1 y 12 y que el año sea m ayor o igual
que 1582.

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

public class C Fech a


I
// A t r i b u t o s
p r i v a t e i n t d 1 a . mes . año:

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

public void a sig n a rF e c h a d n t dd , i n t mm, int aaaa)


I
d i a = dd ; mes = mm: a ñ o = a a a a :
)

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 .

S iguiendo las reglas enunciadas, finalizarem os el diseño de la clase escribien­


do el resto de los m étodos. El resultado que se obtendrá será la clase C Fecha que
se observa a continuación:

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

public void a s i g n a r F e c h a ( i n t dd . i n t mm. int aaaa)


I
dia = dd : mes = mm: a ñ o - aaaa:
I

public void obte nerFecha (int[] fecha)


I
fe c h a [0] = di a :
f e c h a [ l ] = mes ;
f e c h a [ 2 ] = año:

public boolean fe c h a C o rre c ta í )


I
boolean d ia C o r re c t o . mesCorrecto. añoCorrecto:
// ¿ a ñ o c o r r e c t o ?
a ñ o C o rre c to = (año >- 1582):
// ¿m e s c o r r e c t o ?
m e s C o r r e c t o = (m es >= 1) && (mes < = 1 2 ) :
s w i t c h (mes)
// ¿ d i a c o r r e c t o ?
I
c a s e 2:
if (bisiestoO )
diaCorrecto - (dia >= 1 && d í a <= 2 9 ) :
el s e
diaCorrecto = (dia >= 1 && d i a <= 2 8 ) :
break:
c a s e 4 : c a s e 6: c a s e 9: c a s e 11:
diaCorrecto = ( d í a > - 1 && d í a <= 30 ):
break:
default:
diaCorrecto = (día > = 1 && d i a <= 31):
I
return d i a C o r r e c t o && m e s C o r r e c t o && a ñ o C o r r e c t o ;
1
I

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

El m étodo público fech a C o rrecta verifica si la fecha que se desea asignar al


objeto que recibe este m ensaje es correcta. E ste m étodo devuelve true si la fecha
es correcta y false en caso contrario.

El m étodo protegido bisiesto verifica si el año de la fecha que se desea asig­


n ar al objeto que recibe este m ensaje es bisiesto. E ste m étodo retorna true si el
año es bisiesto y false en caso contrario.

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:

public int read()


public int read(byte[] b)
public int read(byte[] b, i n t off. int len)

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:

public void p r i n t ( i n t ;')


public void p r i n t ( d o u b l e d)
public void p r i n t ( c h a r [ ] s)

¿En qué se diferencian los m étodos r e a d ? E n su núm ero d e parám etros. Y,


¿en qué se diferencian los m étodos p rin t expuestos? En el tipo de su parám etro.

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.

La sobrecarga de m étodos elim ina la necesidad de definir m étodos diferentes


que en esencia hacen lo m ism o, com o es el caso del m étodo p rin t, o tam bién hace
posible que un m étodo se com porte de una u otra form a según el núm ero de ar­
gum entos con el que sea invocado, com o es el caso del m étodo read.
CA PÍTU LO 9: CL A SES Y PAQUETES 2 6 3

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.

L a fecha actual del sistem a se puede obtener a partir de un objeto de la clase


G r e g o r ia n C a le n d a r , q ue es una subclase de C a le n d a r , del paquete java.u til. La
clase C a le n d a r es una clase abstracta que proporciona una serie de constantes ta­
les com o D A Y_O F _M O N TH , M O N T H o YEAR que podem os u tilizar com o argu­
m ento en el m étodo get para obtener el dato al que alude.

public void asignarFechaí)


I
// A s i g n a r , p o r o m i s i ó n , l a f e c h a a c t u a l .
G r e g o r i a n C a l e n d a r f e c h a A c t u a l = new G r e g o r i a n C a l e n d a r ( ):
di a = f e c h a A c t u a l , g e t ( C a l e n d a r . D A Y _ O F _ M O N T H ) :
mes = f e c h a A c t u a l . g e t C C a l e n d a r . H 0 N T H 1 + 1 :
año = f e c h a A c t u a l . g e t t C a l e n d a r . Y E A R ) ;
I

public void a s i g n a r F e c h a t i n t dd)


1
a s i g n a r F e c h a ( );
d 1 a = dd:
I

public void a sig n a rF e c h a íin t dd. i n t mm)


I
a s i g n a r F e c h a ( );
d i a = d d : mes = mm;
I

public void asignarFechaíint dd. i n t mm, int aaaa)


I
d 1 a = dd : mes = mm: a ñ o = a a a a ;
I

C om o se puede observar, el que una definición del m étodo invoque a otra es


una técnica de m étodo abreviado que da com o resultado m étodos m ás cortos.

P o r cada llam ada al m étodo asignarF echa que escribam os en un program a, el


com pilador Jav a debe resolver cuál de los m étodos con el nom bre asignarFecha
es invocado. Esto lo hace com parando el núm ero y tipos de los argum entos espe­
cificados en la llam ada, con los parám etros especificados en las distintas defini­
ciones del m étodo. El siguiente ejem plo m uestra las posibles form as de invocar al
m étodo a signarF echa:
264 JA V A: CU R SO DE PROGRAM A CIÓN

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.

IMPLEMENTACIÓN DE UNA APLICACIÓN


Recordando lo expuesto en capítulos anteriores, las aplicaciones son program as
Java que se ejecutan por sí m ism os, a diferencia de los applets que requieren de
un explorador. U na aplicación consiste en una o m ás clases, de las cuales una de
ellas tiene que ser una clase aplicación: clase que incluya el m étodo m a in . C uan­
do com pile el fichero que contiene su aplicación, el com pilador Java generará un
fichero .class por cada una de las clases que la com ponen; cada fichero generado
tendrá el m ism o nom bre que la clase que contiene.

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

S y s t e m . o u t . p r i n t í " a ñ o , IHHHt : "): año = Leer.datolntí):


f e c h a . a s i g n a r F e c h a í d l a . mes . a ñ o ) ;
I
w h i 1e ( ! f e c h a . f e c h a C o r r e c t a ( ) ) :

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:

public static void m a i n i S t r i n g í ] args)


I
CFecha f e c h a = new C F e c h a ( ) :
II...
i n t dd = f e c h a . d í a : // e r r o r : d 1 a e s un m i e m b r o p r i v a d o
f e c h a . m e s = 1: II e r r o r : mes e s un m i e m b r o p r i v a d o
b o o l e a n e s B i s i e s t o = f e c h a . b i s i e s t o í ); // c o r r e c t o
I

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.

CONTROL DE ACCESO A UNA CLASE


El control de acceso a una clase determ ina la relación que tiene esa clase con otras
clases de otros paquetes. D istinguim os dos niveles de acceso: de paquete y p úbli­
co. Una clase con nivel de acceso de paquete sólo puede ser utilizada por las cla­
ses de su paquete (no está disponible para otros paquetes, ni siquiera para los
subpaquetes). En cam bio, una clase pública puede ser utilizada p o r cualquier otra
clase de otro paquete.

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

R ecuerde, en el capítulo 4 aprendió que cada objeto m antiene su propia copia de


los atributos pero no de los m étodos de su clase, de los cuales sólo existe una co­
pia para todos los objetos de esa clase. Esto es, cada objeto alm acena sus propios
datos, pero para acceder y operar con ellos, todos com parten los m ism os m étodos
definidos en su clase. Por lo tanto, para que un m étodo conozca la identidad del
objeto particular para el que ha sido invocado, Java proporciona una referencia al
objeto denom inada this. Así, por ejem plo, si cream os un objeto fe c h a ] y a conti­
nuación le enviam os el m ensaje asignarF echa,

f e c h a l . a s i g n a r F e c h a t d í a . mes . año):

Jav a define la referencia th is para perm itir referirse al objeto fe c h a 1 en el cuerpo


de el m étodo que se ejecuta com o respuesta al m ensaje. E sa definición es así:

final C F ec h a this = fechal:

Y cuando realizam os la m ism a operación con otro objeto fech a 2 ,

fecha2.asignarFecha(d1a, mes . año):

Jav a define la referencia th is, para referirse al objeto fe c h a l, de la forma:

final CFecha this - fecha2:

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:

public void asignarFechatint dd. i n t mm. int aa)


I
this.día = dd : t h i s . m e s = mm: t h i s . a ñ o - aa:
CA PÍTU LO 9: C L A SES Y PA Q U ETES 2 6 7

¿Q ué representa th is en este m étodo? Según lo explicado, th is es una referen­


cia al objeto q ue recibió el m ensaje asignarF echa; esto es, al objeto sobre el que
se está realizando el proceso llevado a cabo p o r el m étodo asigncirFecha.

O bserve ahora el m étodo m a in de la clase Test presentada anteriorm ente. En


él hem os declarado un objeto fe c h a de la clase C Fecha y posteriorm ente le hem os
enviado un m ensaje fechaC orrecta:

do

System.out pri n t ( 'di a . ## : "): di a Leer d a t o l n t í ):


System.out pri n t ( 'm es, U : "): mes Leer d a t o l n t í ):
S ystem .o ut p r i nt( 'año. # # # : "): año Leer d a t o l n t ( );
fe c h a . a s i gnarFech a (d i a mes . a ñ o ) :

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 ?

p u b lic boolean fechaCorrectaí)


I
II...
if ( b i s i e s t o ! ))
// ...

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

if (th is.b isie sto O )

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.

VARIABLES, MÉTODOS Y CLASES FINALES


En el capítulo 3 ya fueron expuestas las variables finales, generalm ente denom i­
nadas constantes porque nunca cam bian su valor. Tam bién vim os su uso ju n to con
268 JA VA : C U R SO D E PROGRAM A CIÓN

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

D eclarar un m étodo fin al supone que la clase se ejecute con m ás eficiencia,


porque el com pilador puede colocar el código de bytes del m étodo directam ente
en el lugar del program a donde se invoque a dicho m étodo, ya que se garantiza
que el m étodo no va a cam biar. P or ejem plo:

public fin a l void asigriarFechaí in t dd . i n t mm. int aa)


I
d i a = dd : mes = mm: a ñ o = a a :
I

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

public final class CFecha


!
II...
I

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 :

C on objetos, el com pilador proporciona un constructor público por om isión


para cada clase definida. Este constructor será ejecutado después que el operador
new, secuencial y recursivam ente (un m iem bro de una clase puede ser iniciado
con un objeto de otra clase) reserve m em oria para cada uno de los m iem bros y los
inicie. Igualm ente, el com pilador proporciona para cada clase de objetos un des­
tructor protegido por om isión, que será invocado ju sto antes de que se destruya
un objeto con el fin de perm itir realizar tareas de lim pieza y liberar recursos.

No obstante, com o verem os a continuación, cuando el constructor proporcio­


nado por om isión p o r Java no satisfaga las necesidades de nuestra clase de obje­
tos, podem os d efinir uno. Idem para el destructor.

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

• A signa m em oria para el objeto p o r m edio del operador new.


• Inicia los atributos de ese objeto, ya sea a sus valores iniciales (si los atributos
fueron iniciados en su propia declararon) o a los valores predeterm inados por
el sistem a: los atributos num éricos a ceros, los alfanum éricos a nulos, y las
referencias a objetos a nuil.
• L lam a al constructor de la clase que puede ser uno entre varios, según se ex­
pone a continuació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 p o r om isión de una ciase C es un constructor sin parám etros


que no hace nada. Sin em bargo, es necesario porque según lo que acabam os de
exponer, será invocado cada vez que se construya un objeto sin especificar ningún
argum ento, en cuyo caso el objeto será iniciado con los valores especificados
cuando se declararon los atributos en su clase, o en su defecto, con los valores
predeterm inados por el sistem a.

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

C om o ejem plo, vam os a añadir un constructor a la clase C Fecha con el fin de


poder iniciar los atributos de cada nuevo objeto con unos valores determ inados:

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

O bserve q ue el constructor, salvo en casos excepcionales, debe declararse


siem pre público para que pueda ser invocado desde cualquier parte, aunque la cla­
se, que se supone pública, pertenezca a otro paquete.

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:

public class Test

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

public static void m a in (S trin g [] args)


I
// La s i g u i e n t e l i n e a i n v o c a al c o n s t r u c t o r de l a c l a s e C F ech a
CF e c h a f e c h a - new C F e c h a d . 3. 2 0 0 2 ) : // o b j e t o de t i p o C F ec h a
v i s u a l i z a r F e c h a ( f e c h a ):

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 ;

Q uizás se sorprenda cuando el com pilador Java le indique que la clase C F e­


cha no tiene ningún constructor sin parám etros, cuando anteriorm ente habíam os
dicho que Java proporciona para toda clase uno. L o que sucede es que siem pre
que en una clase se define explícitam ente un constructor, el constructor im plícito
(constructor por om isión) es reem plazado p o r éste.

Según lo expuesto, la definición explícita del constructor con parám etros


C Fecha)int dd, int m m, int aaaa), ha sustituido al constructor p o r om isión que Ja­
va añ ad ió a esa clase. Para solucionar este problem a, hay que añadir a la clase un
constructor sin parám etros. Por ejem plo, el siguiente:

public CFechaO { /* Sin código */ )

El constructor anterior, realiza la m ism a función que el constructor p o r om i­


sión. N o obstante, en el caso de la clase CFecha, quizás sea m ás conveniente,
añadir un constructor sin parám etros que inicie cada objeto creado con los valores
correspondientes a la fecha actual:

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

Sobrecarga del constructor


Según lo expuesto, es evidente que podem os d efinir m últiples constructores con el
m ism o nom bre y diferentes parám etros con el fin de poder iniciar un objeto de
una clase de diferentes form as. Esto no es nuevo, sim plem ente es aplicar la técni­
ca de sobrecargar un m étodo, expuesta anteriorm ente, al constructor de una clase.

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:

public CFechaO // c o n s t r u c t o r sin parámetros


(
a s i g n a r F e c h a O : // a s i g n a r f e c h a actual
1
CA PÍTU LO 9: CL A SES Y PA Q U ETES 2 7 3

public CFecha(int dd) // c o n s t r u c t o r c o n un p a r á m e t r o


I
a s i g n a r F e c h a ! ) : // a s i g n a r fecha actual
dia = d d :
i f ( ! f e c h a C o r r e c t a ( ))
I
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 ! ):

public C F e c h a í i n t dd . i n t mm) // c o n s t r u c t o r con d o s p a r á m e t r o s


I
a s i g n a r F e c h a ! ) ; // a s i g n a r fecha actual
d i a = dd : mes = mm:
i f ( ! f e c h a C o r r e c t a ! ))
I
System .out.printín("Fecha incorrecta. Se a s i g n a la actual."):
a s i g n a r F e c h a ! );

public CFechatint dd . i n t mm. int aaaa) // c o n s t r u c . con t r e s pars.


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

A hora, podem os invocar al constructor C Fecha con O, 1, 2 ó 3 argum entos,


según se puede observar en las líneas de código siguientes:

CF e c h a fechal = new CFecha!):


CF e c h a fecha2 = new CFechaO):
CF e c h a fecha3 = new CFecha(15, 3):
C F ec h a fecha4 = new C F e c h a ü . 3. 2 0 0 2 ) :

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:

public void CFechaíint a. int b, int c)


I
// ...
274 JA V A : C U R SO D E PROGRAM A CIÓN

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:

t h i s ( [ [ [ [ a r g j ] , arg?], arg3]. ...]);

La llam ada a un constructor sólo puede realizarse desde dentro de otro co n s­


tructor de su m ism a clase y debe ser siem pre la prim era sentencia. Por ejem plo, el
constructor con un parám etro podría escribirse tam bién así:

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 :

Este ejem plo crea tres objetos: f e c h a l , f e c h a l y fe c h a 3 . D espués declara una


nueva referencia fe c h a d y le asigna fe c h a l, pero tanto fe c h a l com o fe c h a d son
referencias a objetos CFecha, que ahora apuntan al m ism o objeto (al referencian-
do por fe c h a l) . Finalm ente, asigna fe c h a 2 a fech a d , con lo que am bas referencias
apuntarán al objeto referenciado p o r fech a 2 .

Lo anteriorm ente expuesto dem uestra que el operador de asignación no sirve


para co piar un objeto en otro. ¿C uál es la solución para resolver el problem a
planteado? Pues, añadir a la clase C Fecha un m étodo com o el siguiente:
C A P ÍT U L O 9: CL A SES Y PA Q U ETES 2 7 5

public void copiar(CFecha obj)


I
d ía = o b j .d1a :
mes = o b j . m e s :
año = o b j .a ñ o ;
I

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

Si ahora quisiéram os copiar el objeto fe c h a 3 en el objeto fe c h a ! y en el objeto


fe c h a 1, podríam os proceder, por ejem plo, así:

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

T endríam os que m odificar el m étodo copiar com o se observa a continuación:

public CFecha c o p i a r ( C F e c h a obj)


I
di a = o b j . d i a :
mes = o b j . m e s :
año = o b j . a ñ o ;
return th is;

El m étodo co piar devuelve ahora una referencia al objeto resultado de la co­


pia, con lo cual podem os u tilizar esta referencia para copiar este objeto en otro;
esto es, el hecho de que el m étodo copiar retom e una referencia al objeto resul­
tante perm ite realizar copias m últiples encadenadas.

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

nom bre_clase(nom bre_clase referencia jo b je tó )

Se puede observar que un constructor de las características especificadas tiene


un solo parám etro, que es una referencia a un objeto de su m ism a clase. Por tra­
tarse de un constructor no hay un valor retornado. Pues bien, un constructor que
se invoca para iniciar un nuevo objeto creado a partir de otro existente es denom i­
nado constructor copia.

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.

C uando un objeto es destruido ocurren varias cosas: se llam a al m étodo fin a ­


lize y después, el recolector de basura se encarga de elim inar el objeto, lo que
conlleva liberar los recursos que dicho objeto tenga adjudicados, com o por ejem ­
plo, la m em oria que ocupa.
C A PÍTU LO 9: CLA SES Y PAQUETES 2 7 7

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:

protected void fin alize O th rows Throwable I /* s in código */ I

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.

C om o ejem plo vam os a añadir a la clase C Fecha del program a anterior, un


destructor para q ue sim plem ente nos m uestre un m ensaje cada vez q ue se destruya
un objeto de esa clase. Esto es:

public c la ss CFecha
I
// ...

protected void fina lizeO t h r o w s T h r o w a b l e // d e s t r u c t o r


1
S y s t e m . o u t . p r i n t l n í " O b j e t o d e s t r u i d o " );
I
II...
I

Ejecute ahora la aplicación Test cuyo código se m uestra a continuación y ob­


serve los resultados.

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

public static void v i s u a l i z a r F e c h a ( CFecha fecha)


I
i n t [ ] f = new i n t [ 3 ] :
fecha.obtenerFecha(f);
System .out.p rintín(f[0] + "/" + f [ 1 ] + "/" + f [ 2 ] ) :

public static void m a in (S trin g [] args)


I
C F e c h a f e c h a l = new C F e c h a C l , 3. 2002):
// E mp i e z a un b l o q u e de c ó d i g o
I
C F e c h a f e c h a 2 = new C F e c h a < f e c h a l ):
v is u a liz a r F e c h a ífe c h a 2);
I // f i n d e l b l o q u e
v i s u a l i z a r F e c h a C f e c h a l );

A nalizando este ejem plo, observam os que en el m étodo m a in se crean dos


objetos: uno al nivel del bloque de m a in , y otro local a un bloque interno a m ain.
Por lo tanto, cada objeto quedará desreferenciado cuando el flujo de ejecución
salga fuera del bloque en el que está definido, instante a partir del cual el reco­
lector de basura puede recolectar esos objetos.

O bservará que cuando finalice la ejecución del m étodo m a in no se visualiza


el m ensaje “O bjeto destruido” tantas veces com o objetos hay. Esto significa que
el recolector de basura, ju sto en este instante, no está en ejecución (sólo p o r una
pequeña cantidad de tiem po); un poco m ás larde, posiblem ente cuando el sistem a
esté libre, el recolector de basura identificará los objetos que no tienen referencias
y los elim inará liberando la m em oria q ue ocupan.

N o obstante, la aplicación Java puede finalizar sin que el recolector haya


identificado todos los objetos que d eb ía e n v iar a la basura y p o r lo tanto, los d es­
tructores correspondientes no se ejecutarán. En este caso, será el sistem a operati­
vo el que se encargue de liberar los recursos que fueron ocupados.

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

Ejecutar el recolector de basura


El recolector d e basura se ejecuta en un subproceso paralelam ente a su aplicación
lim piando la basura (objetos desreferenciados) en form a silenciosa y en segundo
plano y nunca se detiene por m ás de una pequeña cantidad de tiem po.

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:

public class Test


I
// Vi s u a l i z a r una f e c h a
p u b l i c s t a t i c v o i d v i s u a l i z a r F e c h a ( CFecha fecha)
I
i n t [ ] f = new i n t [ 3 ] ;

fecha.obtenerFecha(f);
S y stem .o ut.p r i n t í n < f [0] + "/" + f[l] + "/" + f[2]);
1

public static void m a in ( S t r in g [ ] args)


I
C F e c h a f e c h a l = new C F e c h a d , 3. 2002):
// E mp i e z a un b l o q u e de c ó d i g o
I
C F e c h a f e c h a 2 = new C F e c h a ( f e c h a 1 ) ;
vi su a li zarFecha( fecha2):
fecha2 = n u i l :
Runtime runtim e = R u n t i m e . g e t R u n t i m e ( );
r u n t i m e . g c ( ):
r u n t i m e . r u n F i n a l i z a t i o n ( ):
I // f i n d e l b l o q u e
v i s u a l i z a r F e c h a ( f e c h a l ):

El ejem plo anterior fuerza la recolección de basura en el bloque de código


interno a m a in . Si em bargo, esto rara vez será necesario; a lo m ejor, si acaba de
liberar m uchos objetos ya inservibles y quiere que se lleven pronto la basura.

REFERENCIAS COMO MIEMBROS DE UNA CLASE


Un m iem bro de una clase que sea una referencia requiere, generalm ente, de una
asignación de m em oria, proceso que norm alm ente realizará el constructor. Sucede
entonces que el espacio de m em oria asignado es referenciado desde el objeto pe-
280 JA V A : C U R SO DF. PROGRAM A CIÓN

ro, lógicam ente, no pertenece al objeto, lo que puede d ar lugar a problem as si no


se im plem entan adecuadam ente los m étodos que generan un objeto copia de otro
de su m ism a clase. Un ejem plo de este tipo de clases es la clase C írculo expuesta
al principio de este capítulo; recuerde que tenía un m iem bro centro de la clase
Punto.

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

Según lo expuesto, la funcionalidad de la clase C V ector estará soportada por


los atributos:

• vector: una referencia a una m atriz de valores de tipo do u b le.


• nE lem entos: núm ero de elem entos de dicha m atriz.

public class CVector


I
private double[] vector:
prívate i n t nElementos:
II ...

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

public C VectorO nt ne) // ne e l e m e n t o s


I
if ( ne < 1 )
I
System .out.p rin tln ("N ° de e l e m e n t o s no v á l i d o : " + ne):
System .out.println("Se a s i g n a n 10 e l e m e n t o s " ) :
ne = 10;
I
n E l e m e n t o s — ne:
v e c t o r = new d o u b l e [ n E l e m e n t o s ] :
I

public CVector(double[] 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


I
nEle mentos = m .le n g th ;
v e c t o r = new d o u b l e [ n E 1e m e n t o s ]:
II 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 ];

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 u b lic CVector co piar(C V ector v) // c o p i a un C V e c t o r en o t r o


I
nEle mentos = v . n E le m e n t o s :
v e c t o r = new d o u b l e f 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
282 JA V A: C U R SO D E PROGRAM A CIÓN

f o r ( i n t i = 0; i < nE lem entos; i++ )


vectorfi] = v.vector[1];
return this;
)

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

public double v a lo r E n í int i ) I return vectord]; I

• longitud-, m étodo que devuelve el núm ero de elem entos de un objeto CVector.

public int longitud!) I return nElementos; I

El resultado de encapsular los m étodos anteriorm ente expuestos es la clase


C V ector que se m uestra a continuación:

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

p u b l i c C V e c t o r í ) // n ú m ero de e l e m e n t o s por om isión


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

public CVectoríint ne) // ne e l e m e n t o s


I
if ( ne < 1 )
I
S y s t e m . o u t . p r i n t l n í " N s de e l e m e n t o s no v á l i d o : ” + ne);
S y s t e m . o u t . p r i n t l n i " S e a s i g n a n 10 e l e m e n t o s ” );
ne = 10;
I
n E l e m e n t o s = ne;
v e c t o r = new d o u b l e [ n E 1e m e n t o s ] :
CA PÍTU LO 9: C L A SES Y PAQUETES 2 8 3

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

public CVectoríCVector v) // c o n s t r u c t o r copia


I
nEle mentos = v . n E le m e n t o s :
v e c t o r = new d o u b l e [ n E 1 e m e n t o s ] ;
// C o p i a r el o b j e t o v
f o r ( i n t i = 0: i < nElem entos; i + + )
vector[i] = v.vector[1]:

p u b lic CVector c o p ia ríC V e c to r v) // c o p i a un C V e c t o r en o t r o


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 1e m e n t o s ] ;
II C o p i a r e l o b j e t o v
f o r ( i n t i = 0: i < n E l e m e n t o s ; i + + )
vector[i] = v.vector[i]¡

return this;

public void ponerValorEní int i, double v a l o r )


I
if ( i >= 0 && i < n E l e m e n t o s )
vector[i ] = valor;
else
System .out.p r i n t l n ( Ӓn d ic e fuera de l i m i t e s " ) :

p u b lic double va lo rE n í int i )


I
if ( i > = 0 && i < n E l e m e n t o s )
return v e c to rti];
else
I
S y ste m .o u t.p rin tín (" índice fuera de l i m i t e s " ) :
r e t u r n Double.NaN;

public int longitudO I return nElementos: I


284 JA VA: C U R S O DE PROGRAM A CIÓN

El resultado es que cada objeto C V ector consta de dos bloques de m em oria,


uno de tam año fijo que alm acena su estructura interna (vector y nE lem entos) y
o tro de longitud variable que alm acena los datos (la m atriz de tipo do u b le).

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

public static v o i d ma i n ( S t r i rig[ ] a r g s )


I
C V e c t o r v e c t o r l = new C V e c t o r ( 5 ) :
vi s u a 1 i z a r V e c t o r ( v e c t o r l );

CVect or v e c t o r 2 = new CVe c t o r ) );


f o r ( i n t 1 = 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 En( i . ( i + l ) * 1 0 ) :
vi s u a l i zar Vect or ( vector2) ;

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

System .out.p rintlní"Fin de la a p l i c a c i ó n " ) :

A nalizando a grandes rasgos el código presentado anteriorm ente, podem os


v er que la línea:
CVector v e c to rl = new C V e c t o r ( 5 ) :

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

for (in t i = 0 : i < ve cto r2 .lo n g itu d (): i+ + )


v e c t o r ? .ponerValo rE n ( i . (1+1) * 1 0 ) :

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

la prim era define la m atriz x y la últim a llam a al constructor C V ectorfdouble[ / m )


y crea un objeto vector4 iniciado con los datos d e la m atriz 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;

Suponga tam bién que en la aplicación anterior el m étodo m a in fuera com o


sigue:

public static void m a in (S trin g [] args)

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

// El siguiente bloque d e f in e vector2


I
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 ):
286 JA V A: C U R SO DE PROGRAM A CIÓN

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

System .out.p rintlnC Fin de l a aplicación"):

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

será dos objetos, ve cto rl y v e c to r l, referenciando la m ism a m atriz. La figura si­


guiente m uestra esto con claridad:

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

E sto significa que cualquier m odificación en uno de los objetos afectará a


am bos, ju sto lo que sucede cuando se ejecuta el código siguiente. Las m odifica­
ciones realizadas en el objeto ve c to rl afectan de la m ism a form a a v e c to r l:

// El siguiente bloque d e f in e vector2


1
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 );
f o r ( i n t i - 0: i < v e c t o r 2 . 1 o n g i t u d ( ) ; i + + J
v e c t o r 2 . p o n e r V a lo r E n ( i. v e c t o r 2 . v a l o r E n ( i )*10>:
vi s u a li z a r V e c t o r ( v e c t o r 2 ) ;
1

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

ciado p o r el atributo vector de v e c to r il N o, porque dicho objeto m atriz tiene aún


una referencia: vector de vector].

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

C om o ejem plo, añada la definición del m étodo e q u a ls a la clase CVector. Para


poder escrib ir este m étodo, prim ero debe responder a la siguiente pregunta:
¿cuándo dos objetos C Vector son iguales? La respuesta es, cuando contengan los
m ism os valores; esto es, cuando las m atrices que representan sean idénticas. Basta
entonces con el m étodo e q u a ls de la clase C V ector com pare las m atrices de los
dos objetos a com parar:

p u b lic boolean eq u a ls(C V e cto r v)


I
r e t u r n A r r a y s .e q u a l s ( v e c t o r . v . v e c t o r ) ;
I

El código anterior im plica im portar la clase A r r a y s del paquete java.util. Pa­


ra probar los resultados que podem os obtener a p artir de este m étodo a diferencia
de los obtenidos por el operador “= = ” escriba la siguiente aplicación:

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

public static void m a in (S trin g [] args)


1
d o u b l e x [ ] = 1 1 . 2 . 3. 4. S . 6. 7 1: // 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

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

Si ejecuta la aplicación Test anterior, obtendrá los siguientes resultados:

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

MIEMBROS STATIC DE UNA CLASE


Este tem a y a fue introducido en el capítulo 4. Por lo tanto, el propósito ahora que
ya tiene un m ayor conocim iento de la PO O es abundar en detalles con el fin de
dejar suficientem ente claro cuál es la utilidad de estos m iem bros.
CA PITU LO 9: CL A SES Y PA Q U ETES 2 8 9

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.

Un atributo static no es un atributo específico de un objeto (el radio si es un


atributo específico de un círculo; cada círculo tiene su radio), sino m ás bien es un
atributo de la clase; esto es, un atributo del que sólo hay una copia que com parten
todos los objetos d e la clase. Por esta razón, un atributo static existe y puede ser
utilizado aunque no exista ningún objeto de la clase.

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;

P u n t o í d o u b le ex. double cy)


I
x = ex: y - cy:

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;

p r i v a t e Punto cen tro: // 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 : // r a d i o d e l c i r c u l o

// Métodos
290 JA V A: C U R SO DE PRO G R A M A CIÓ N

protected void m sgEsN egativo()


I
System .out.printlní"El r a d i o es n e g a t i v o . Se c o n v i e r t e a p o s i t i v o " ) :
I

public C ircu lo !) // c o n s t r u c t o r sin parámetros

t h is ( 100.0, 1 0 0 . C “1 0 0 . 0 ) :
I

p u b l i c C i 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
c e n t r o = new P u n t o í c x . cy);
i f ( r < 0)
1
m s g E s N e g a t i v o ( );
r = -r :
I
r a d i o = r;
numCi r c u l o s + + ;
I

public double 1o n g C i r c u n f e r e n c i a < )


I
return 2 * pi * radio:
I

p u b lic double á r e a C I r c u l o ( )
I
return pi * radio * radio:
i
i

Un atributo static puede ser calificado com o p riv a te (privado), protected


(protegido), p u b lic (público), o no calificado (acceso predeterm inado). Asimismo,
podem os calificarlo fin a l para que sea una constante en lugar de una variable.

Acceder a los atributos static


En el apartado anterior podem os ver cóm o los m étodos de la clase C írculo pueden
acceder directam ente a los atributos num C írculos y p i de la m ism a, igual que ac­
ceden al resto de los atributos. Pero, desde otra clase ¿cóm o podem os acceder a
esa inform ación? Puesto que num C írculos es una variable static declarada public
podem os acceder a ella directam ente a través del nom bre de la clase (utilizar el
nom bre de un objeto, aunque es válido, puede dar lugar a m alas interpretaciones
del código). Por ejem plo:
C A P ÍT U L O 9: CL A SES Y PAQUETES 2 9 1

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

O bserve que para acceder a la inform ación proporcionada por el atributo


num C írculos se utiliza el nom bre de su clase y no el de un objeto de la misma. En
cam bio, desde la clase Test no se puede acceder al atributo p i porque es privado.

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.

C om o ejem plo, vam os a añadir a la clase C írculo un m étodo cam biarPreci-


siónP iA que perm ite cam biar la precisión de p i siem pre que su valor se m antenga
entre 3.14 y 3.1416.

public static void ca m b iarP recisiónP iA (d ou b le nuevoValor)


I
if (nuevoValor < 3.14 || n u e v o V a l o r > 3 . 1 4 1 6 ) return:
pi = nuevoValor:
292 JA V A: C U R SO D E PROGRAM A CIÓN

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:

public class Test


I
public static void m a in (S trin g [] args)
I
C i r c u l o o b j l - new C í r c u l o O :
S y s t e m . o u t . p r in t l n t o b j l .1o n g C ir c u n f e r e n c ia ( ));
S y s t e m . o u t . p r i n t l n t o b j l . á r e a C i r c u l o t ) >:

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

Se puede observar que el com portam iento de C írculo.cam biarPrecisiónPiA es


igual que el de cualquier otro m étodo de un lenguaje no orientado a objetos. Esto
hace posible escribir program as Java utilizando solam ente esta clase de métodos,
pero entonces se frustraría el propósito m ás im portante de este lenguaje: la POO.
No piense por ello que utilizar este tipo de m étodos es una tram pa. Hay m uchas y
buenas razones para utilizarlos y sino observe la utilidad de las clases M a th y
S y stem en las que todos sus m étodos son estáticos.

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

En m ás de una ocasión necesitarem os iniciaciones m ás com plejas que esas


que podem os hacer en la propia declaración. Para esto podem os utilizar alguno de
los constructores de la clase. Pero en el caso de que la clase tenga atributos estáti­
cos, los constructores sólo serían adecuados cuando la iniciación de esos atributos
no sea requerida antes de que se cree un prim er objeto. Si no es así, será necesario
añadir a la clase un iniciador estático cuya sintaxis es la siguiente:

stati c
I
// i n i c i a c i ó n ele l o s atributos de l a clase
1

Un iniciador estático es un m étodo anónim o que no tiene parám etros, no re­


tom a ningún valor, y es invocado autom áticam ente por el sistem a cuando se carga
la clase.

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:

CPersona[] listaTeléfonos = new C P e r s o n a f l O O ] :

listaTeléfonos es una m atriz de referencias a objetos de la clase C Persona. Cada


elem ento de esta m atriz será iniciado p o r Java con el valor nuil, indicando así que
la m atriz inicialm enle no referencia a ningún objeto C P ersona; esto es, la matriz
está vacía.

U na vez creada la m atriz, para asignar un objeto al elem ento i de la m ism a se


puede u tilizar una línea de código com o la 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 ] ) :

C om o ejem plo, supongam os que deseam os m antener una lista de teléfonos.


La lista será un objeto que encapsule la m atriz de objetos persona, y m uestre una
interfaz que perm ita añadir, elim inar y buscar una en la lista.

En un prim er análisis sobre el enunciado identificam os dos clases de objetos:


personas y lista de teléfonos.

La clase de objetos persona (que denom inarem os C Persona) encapsulará el


nom bre, la dirección y el teléfono de cada una de las personas de la lista; asimis­
m o proporcionará la funcionalidad necesaria para establecer u obtener los datos
de cada persona individual.

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:

• C onstructores, con y sin argum entos, para iniciar un objeto persona.

• M étodos de acceso (asignar... y obtener...) para cada uno de los atributos.


CA PÍTU LO 9: CL A SES Y PA Q U ETES 2 9 5

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

public void a s i g n a r N o m b r e ( S t r i n g nom)

nombre = nom;

public String obtenerNombre()

r e t u r n n o mbr e ;

public void a s ig n a r D ir e c c ió n ( S t r in g dir)


I
d i r e c c i ón = d i r ;

public S trin g obtenerDirección()


I
return dirección:

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 ;

public long obtenerTeléfono()


I
return teléfono;
296 JA V A : C U R SO DE PROGRAM A CIÓN

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

private void unElementoMás( C P e rso n a [] l i s t a A c t u a l ) I ... 1


private void unElementoMenos( C P e rso n a [] l i s t a A c t u a l ) I ... 1
public C L i s t a T f n o s í ) I . . . 1 // c o n s t r u c t o r
public void p one rValorEnt i n t i. CPersona ob je to ) I ... 1
public CPersona va lo rE n ( in t i ) I . .. 1
public i n t 1o n g i t u d ( ) I . . . I
public void añadír(CPersona obj) I ... I
public v o i d el i m i n a r d o n g tel ) I . . . 1
public in t b u s c a r ( S t r in g s t r . in t pos) i ... )
)

Para crear un objeto lista de teléfonos escribirem os una línea de código como
la siguiente:

CListaTfnos listatfnos = new C L i s t a T f n o s ( ):

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:

public C L ista T fn o s()


I
II C r e a r una l i s t a v a c i a
n E l e m e n t o s = 0:
l i s t a T e i é f o n o s = new C P e r s o n a C n E l e m e n t o s ] :
CA PÍTU LO 9: C L A SES Y PAQUETES 2 9 7

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

El código que escribirem os para añadir un teléfono (objeto C P ersona) a la


lista de teléfonos (objeto C ListaTfnos) será análogo al siguiente:

1i s t a t f n o s .añadir(new CPersona(nombre, dirección, teléfono));

C uando el objeto listatfnos de la clase C ListaTfnos recibe el m ensaje añadir,


responde ejecutando su m étodo añadir que increm entará en uno el tam año del
atributo m atriz listaTeléfonos y asignará a este nuevo elem ento el objeto C P erso­
na pasado com o argum ento. Para realizar estas dos tareas añadirem os a la clase
C ListaTfnos los m étodos unE lem entoM ás y p onerV alorE n.

public void añadir(CPersona obj )


I
un E l e m e n t o M á s ( 1 i s t a T e l é f o n o s ):
p o n e r V a l o r E n t n E l e m e n t o s - 1. o b j );
1

O bservam os q ue cuando se invoca al m étodo unE lem entoM ás se pasa com o


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
listaTeléfonos un nuevo espacio de m em oria que perm ita albergar un elem ento
más de los que tiene actualm ente, copiar uno a uno los elem entos que tenía hasta
ahora la m atriz y que están referenciados por listaActual, e increm entar el atributo
nE lem entos. O bserve que el bloque de m em oria viejo quedará desreferenciado
cuando el flujo de control salga fuera del m étodo unE lem entoM ás, p o r ser el pa­
rám etro listaA ctual local ¿Q uién liberará ese bloque de m em oria y los bloques de
m em oria de los objetos referenciados p o r él? De esta tarea se encarga el recolec­
tor de basura de Java.

private void unElementoMás(CPersona[] listaActual)


I
nEle mentos = 1 i s t a A c t u a l .1e n g t h ;
l i s t a T e l é f o n o s - new C P e r s o n a í n E l e m e n t o s + 1 ] ;
// C o p i a r l a l i s t a a c t u a l
f o r ( i n t i = 0: i < nElem entos: i + + )
1i s t a T e l é f o n o s í i ] = 1 i s t a A c t u a l [ i ]:
n E 1e m e n t o s + + ;
I

El m étodo p o n erV alorE n tiene com o m isión asignar la referencia a un nuevo


objeto C Persona, al elem ento / de la m atriz listaTeléfonos. A m bos datos, objeto y
posición, son pasados com o argum entos.
298 JA V A: C U R S O D E PROGRAM A CIÓN

public void ponerValorEn( int i. CPersona objeto )


I
if ( i > = 0 && i < n E l e m e n t o s )
1i s t a T e l é f o n o s f i ] = objeto:
el se
S y s t e m . o u t . p r i n t l n ( " í n d i ce f u e r a de l i m i t e s " ) :
I

El código que escribirem os p ara elim inar un teléfono (objeto CPersona) de la


lista de teléfonos (objeto C ListaTfnos) será análogo al siguiente:

elim inado = 1i s t a t f n o s . e lim i n a r ( t e lé f o n o ) ;

C uando el objeto listatfnos de la clase C ListaTfnos recibe el m ensaje elimi­


n a r, responde ejecutando su m étodo elim inar que quitará de la lista el elemento
correspondiente al teléfono pasado com o argum ento y decrem entará en uno el ta­
m año de la lista. Para realizar estas dos tareas, prim ero buscará en la m atriz lista-
Teléfonos el elem ento que referencia al objeto C Persona que tiene el núm ero de
teléfono pasado com o argum ento y asignará a este elem ento el valor n u il (de esta
form a, el objeto C Persona será enviado a la basura y recolectado p o r el recolector
de basura); después, invocará al m étodo unElem enloM enos para q u itar ese ele­
m ento de la lista. El m étodo elim inar devuelve tr u e si se encontró y elim inó el
elem ento especificado y false en caso contrario.

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

prívate void unElementoMenos(CPersona[] l i s t a A c t u a l )


I
i f ( 1 i s t a A c t u a l .1ength == 0) return:
i n t k = 0:
nEle mentos - 1 i s t a A c t u a l . 1 e n g t h :
1 i s t a T e l é f o n o s - new C P e r s o n a [ n E l e m e n t o s - 1 ] ;
II C o p i a r l a l i s t a a c t u a l
f o r ( i n t i - 0: i < nE le m e ntos: i + + )
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 1e m e n t o s - -:
I

El código que escribirem os para buscar un teléfono (objeto C Persona) en la


lista de teléfonos (objeto C ListaTfnos) será análogo al siguiente:

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

C uando el objeto listatfnos de la clase C ListaT fnos recibe el m ensaje buscar,


responde ejecutando su m étodo buscar q ue recorrerá la lista de teléfonos en busca
de un elem ento (objeto C Persona referenciado) que contenga en su cam po n om ­
bre la subeadena pasada com o argum ento. La búsqueda se iniciará en la posición
pasada com o argum ento. El m étodo buscar devolverá la posición del elem ento
buscado, si se encuentra, o -1 en caso contrario.

public int buscar(String str, int pos)


I
S t r i n g nom;
i f ( s t r = - n u i l ) re tu rn -1;
i f ( p o s < 0 ) p o s - 0:
for ( i n t i - pos: i < nElementos: i++ )
I
nom = 1 i s t a T e l é f o n o s C i ] . o b t e n e r N o m b r e ( ):
i f (nom “ n u il) continué:
// ¿ s t r e s t á c o n t e n i d a en nom?
i f ( n o m . in d e x O f( s tr) > -1)
return i ;
I
return -1:
I

O tros m étodos de interés son valorEn y longitud. El m étodo valorEn devuel­


ve el objeto C Persona referenciado por el elem ento i de la m atriz UstaTeléfonos.

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.

public int 1o n g i t u d ( ) 1 return nElementos; 1

H asta aquí, el diseño de la clase C Persona y C U staTfnos. El siguiente paso


será escribir una aplicación que se ejecute así:

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:

public class Test


I
public static in t menúO I ... I

public static void m a in ( S t r in g [ ] args)


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 y o t r o de s a l i d a
C A P ÍT U L O 9: CL A SES Y PAQUETES 3 0 1

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

El listado com pleto d e la aplicación Test se m uestra a continuación:

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

Syste m .o u t.p ri n t l n ( "2. Buscar 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 i r " );
S y s t e m . o u t . p r i n t í n ( " 4 . El i mi 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.printt" Opción: " ) ;
i n t op:
do
o p - L e e r . d a t o l n t í );
w h i l e ( o p < 1 || op > 5 ) :
r e t u r n op:

public static void m a in (Strin g [] args)


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 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 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 l u j o S - System.out:

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

catch (IOException ignorada) II


I
w h i 1 e ( o p c i ón != 5);

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

2. C rear una estructura jerá rq u ica d e carpetas en el disco duro. H em os dicho


que la biblioteca de clases de Java está organizada en paquetes dispuestos j e ­
rárquicam ente. Pues bien, esta estructura jerárquica se hace corresponder en
el disco duro con una estructura jerárq u ica de carpetas, de form a que los
nom bres de las carpetas coincidan con los de los elem entos del paquete. La
ruta de la carpeta raíz de esta estructura jerárq u ica tiene que estar especificada
por la variable C LASSPATH .

P ara el ejem plo propuesto en el punto 1, la variable C LA SSP A TH debe indi­


car, entre otras, la ruta de la carpeta m isClases:

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

3. E specificar el paquete a l que pertenece la clase. C uando defina una clase


puede especificar a qué paquete pertenece utilizando la sentencia:
p a c k a g e nom bre_j)aquete\
Esta sentencia debe ser la prim era línea de código del fichero fuente.

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

A continuación coloque las clases C Persona y C ListaTfnos im plem entadas


anteriorm ente, en la carpeta utilidades. Edítelas y añada a cada una de ellas la
sentencia p a c k a g e para especificar el paquete al que pertenecen:

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

UN EJEMPLO DE DISEÑO DE UNA CLASE


El siguiente ejem plo m uestra cóm o construir una clase para o p erar con núm eros
racionales. Un núm ero racional es un núm ero representado por el cociente de dos
núm eros enteros (lo que norm alm ente llam am os quebrado), com o 5/7. El núm ero
de la izquierda se denom ina num erador y el de la derecha denom inador.

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:

Pensem os ahora en el conjunto de operaciones que deseam os realizar con los


núm eros racionales (a m odo de ejem plo, sólo expondrem os algunas de las varias
posibles):

• C onstruir un núm ero racional. El constructor im plícito (sin argum entos) no es


adecuado puesto que 0/0 es una indeterm inación. P or ello definirem os explí­
citam ente varios constructores.

• O peraciones aritm éticas. Sum a, resta, m ultiplicación y división.

• C om paración de dos núm eros racionales. Igual, m enor y m ayor.

• O peraciones para facilitar la entrada y salida.

• C opiar un racional en otro y verificar si un racional es cero.

• Increm ento, decrem ento y cam bio de signo.

E m pecem os con el constructor. La construcción de un núm ero racional cuan­


do se om iten los argum entos parece lógico que resulte ser el racional 0 /1 . Partien­
do de este supuesto, el constructor C R acional puede ser:

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

public CRacionalí l o n g num, l o n g den ) // c o n s t r u c t o r


I
n u m e r a d o r = num;
d e n o m in ad o r = den:

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.

protected CRacional Sim p lificare)


1
// 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 );
w h i l e ( temp > 0 )
I
r e s t o = mcd % temp:
mcd = temp;
temp = r e s t o :
I
II S i m p l i f i c a r
i f ( mcd > 1 )
1
n u m e r a d o r /= mcd:
d en om in ad or /= mcd :
)
return this;

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

Pensem os ahora en las operaciones aritm éticas; p o r ejem plo, en la operación


de sum ar. S upongam os las siguientes declaraciones:

CRacional r l = new CRacional(1):


CRacional r2 - new C R a c i o n a ld . 4):
CRacional r3;

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:

p u b lic CRacional sumar( CRacional r )


I
C R a c i o n a l temp;
temp - 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.d e n o m in a d o r ):
r e t u r n temp:
I

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.

Supongam os ahora que uno de los operandos que intervienen en la sum a es un


entero. En este caso podríam os proceder así:

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 Ra cio nal r3:


r 3 = new C R a c i o n a l ( n ) . s u m a r ( r 2 );

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.

La im plem entación de los m étodos restar, m ultiplicar y dividir, sabiendo có­


m o se obtiene el num erador y el denom inador del resultado, siguen un desarrollo
análogo al explicado para sumar.

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

L a expresión r3.equals(r2) sugiere redefinir el m étodo e q u a ls con un pará­


m etro que haga referencia a un objeto CRacional-, el otro objeto im plicado en la
com paración, el de la izquierda, es aquel que recibe el m ensaje eq u a ls. El método
debe devolver un valor tr u e o false. Según lo expuesto, la definición del método
puede ser así:

public boolean e q u a ls( CRacional r )


I
return ( nu merador * r . d e n o m i n a d o r ==
d e n o m i n a d o r * r . n u m e r a d o r ):
I

El resto de las operaciones de relación se desarrollan de form a sim ilar a la ex­


puesta.

A continuación pasam os a resolver la entrada salida de núm eros racionales.


L os m étodos que im plem entem os com o parte de la interfaz que los usuarios de
esta clase utilizarán, deben tratar el núm ero racional com o un objeto indivisible;
CA PÍTU LO 9: C L A SES Y PAQUETES 3 1 1

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

La expresión r l.to S trin g )) debe devolver un objeto S trin g con el contenido


correspondiente al objeto C R acional que recibe el m ensaje toString expresado de
la form a “num erador/denom inador” :

public String toStringO


I
return new S t r i n g ( n u m e r a d o r + ” / " + d e n o m i n a d o r ) :
I

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:

public static CRacional lee rO


I
l o n g num. d e n :
int i. barras;
boolean c a r á c t e r V á lid o :
S t r i n g r a c i o n a 1:
312 JA V A : C U R SO DE PRO G R A M A CIÓ N

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

Pensem os ahora en copiar un objeto C R acional en otro. S upongam os las si­


guientes declaraciones:

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

Para poder copiar un objeto C R acional en otro, p o r ejem plo r2 en r l , pode­


m os proceder así:

rl.copiar(r2);

La expresión rl.c o p ia r(r2 ) requiere redefinir el m étodo copiar con un pará­


m etro que haga referencia al objeto C R acional a copiar; el objeto sobre el que se
realiza la copia, el de la izquierda, es aquel que recibe el m ensaje copiar. El m é­
todo deberá devolver el objeto copiado con el fin de poder encadenar esta opera­
ción cuando se solicite. Según lo expuesto, este m étodo puede ser así:

public CRacional copiart CRacional r )


I
numerador = r.num erador;
denominador = r . d e n o m i n a d o r ;
return th is;
I

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

Y , cóm o realizar una operación de la form a a = -b\ tenga en cuenta que b no


cam bia. E sta operación podríam os requerirla así:

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

w hile ( temp > 0 )


I
r e s t o - mcd % temp:
mcd = temp:
temp = r e s t o :
I
// S i m p l i f i c a r
i f ( mcd > 1 )
I
numerador /= mcd:
denomi n a d o r / = m c d :
I
return this;

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:

p u b lic C R a cio n a l( long num ) // c o n s t r u c t o r


(
n u m e r a d o r = num;
denomi n a d o r = 1;

public CRacional( l o n g num. l o n g den ) // 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 = d e n ;
i f ( denominador = 0 )
1
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.");
denomi n a d o r = 1;
I
if ( denominador < 0 )
I
numerador = -numerador:
denominador = -denominador;
I
S i m p l i f i c a r ( );

public CRacionaK CRacional r ) // c o n s t r u c t o r copia


I
numerador = r . numerador:
denominador = r . denominador:
I
316 JA V A: C U R SO D E PROGRAM A CIÓN

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

public class CMatriz

private d oub le[] 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 [] d im sM a triz ; II v a l o r de c a d a d i m e n s i ó n
// . . .

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.

Un ejem plo de m anipulación de un objeto C M atriz es el siguiente:

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.

La representación gráfica de la estructura de datos construida es la siguiente:

M a triz m u ltid im en sio nal

_______________________________________ M a triz d e u n a d im ensió n

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 )

y por los m étodos públicos,

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 )

Suponiendo que querem os m anipular m atrices de 1, 2 ó 3 dim ensiones, res­


ponda a las siguientes preguntas:
CA PÍTU LO 9: CL A SES Y PA Q U ETES 3 2 1

1. E scriba el esqueleto de la definición de la clase CM atriz.

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.

3. Escriba el m étodo construir. Este m étodo es invocado p o r los constructores


de la clase y com prueba si todas las dim ensiones son positivas. D espués esta­
blece los atributos de CM atriz. T enga presente que m atriz referencia a una
m atriz unidim ensional que representa a la m atriz de 1, 2 ó 3 dim ensiones.

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.

int total Elem entos*)

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.

int desplazamiento* int[] subind )

El m étodo desplazam iento retorna la posición en la m atriz unidim ensional del


elem ento especificado p o r subind o -1 si algún subíndice es inválido.

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.

void asignarDato* int dato, int il )


void asignarDato* int dato, int i l . int i2 )
322 JA V A: C U R S O DE PRO G R A M A CIÓ N

void asignarDato( int dato, int il, int i 2, int i3 )

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.

double obtenerDato( int il )


double obtenerDato( int i l . int i2 )
double obtenerDatoí int il, int i2, in t i3 )

El m étodo obtenerD ato retom a el valor alm acenado en el elem ento especifi­
cado de la m atriz.

9. U tilizando la clase C M atriz que acaba de construir, escriba un program a que


utilice com o cuerpo del m étodo m a in , el expuesto en el enunciado. El resul­
tado que tiene que obtener con este m étodo es:

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?

11. ¿Es necesario un destructor para esta clase? ¿P or qué?

12. ¿Q ué m étodos se invocan y en qué orden, cuando se ejecuta la sentencia?

C M a t r i z m( A . B ):

13. ¿Q u é m étodos se invocan y en qué orden, cuando se ejecuta la sentencia?

m.obtenerDato! i . j );

La solución a las preguntas 1 a 8 puede obtenerlas del código presentado a


continuación. D icho código corresponde a la definición de la clase CM atriz.

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

private void c o n s t r u ir í int[] dim )


I
int i ;
for ( i = 0: i < dim .length: i++ )
i f ( d i m[ i ] < 1 )
I
System .out.pri ntln("D im ensión nula o negativa"):
S y ste m .e x ití-1):
1
// E s t a b l e c e r l o s a t r i b u t o s
d i m s M a t r i z = new i n t [ d i m . 1 e n g t h ] ;
f o r ( i = 0: i < d i m . l e n g t h : i + + ) d i m s M a t r i z [ i ] = d i m [ i ] :
nDims = d i m . l e n g t h :
m a t r i z = new d o u b l e [ t o t a l E l e m e n t o s ( ) ] ;

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

public CMatrizí int di ) // c o n s t r u c t o r


I
i n t di mC] = I d i ); // una d i m e n s i ó n
c o n s t r u i r { d i m );

public CMatrizí int di, i n t d2 ) // c o n s t r u c t o r


I
in t dim[] = I d i, d2 I; // d o s d i m e n s i o n e s
c o n s t r u i r ( d i m );

public CMatriz( int di, i n t d2, int d3 ) // c o n s t r u c t o r


I
in t dim[] = I d i. d2, d3 I: // t r e s dimensiones
c o n s t r u i r í di m ):

public int total Elem entosí)


I
int i ;
i n t n T E l e m e n t o s = 1;
// C a l c u l a r e l nú m e r o t o t a l de e l e m e n t o s de l a matriz
f o r ( i = 0: i < nDims: i+ + )
nTElementos * = d i m s M a t r i z [ i ]:
r e t u r n nTElementos;
324 JA V A: C U R SO DE PROGRAM A CIÓN

public int desplazamiento! in t [] subind )


I
int i :
i n t d e s p l a z a m i e n t o = 0;

for ( i - 0: i < nDims; i++ )


I
// V e r i f i c a r s i l o s s u b í n d i c e s e s t á n d e n t r o d e l r a n g o
i f ( s u b i n d f i ] < 0 || s u b i n d f i ] > d i m s M a t r i z f i ] )
I
System .out.p rintlnt"Subíndice fuera de r a n g o " ) :
r e t u r n -1;
1
// D e s p l a z a m i e n t o e q u i v a l e n t e en l a m a t r i z unidim ensional
desp lazam ien to += s u b i n d f i ] :
i f ( i+ 1 < nDims )
desplazamiento *= d i m s M a t r i z [ i + l ] ;
I
return desplazamiento:

public void asignarDatot int dato, int il )


f
asignarDatotdato. il. 0. 0):

p u b lic void asignarD atot int dato, int il. int i2 )


I
a s i g n a r D a t o t d a t o . i l . i 2. 0 ) ;
I

public void asignarDatot int dato, int il. int i 2. int i3 )


I
// A s i g n a r un v a l o r a l e l e m e n t o e s p e c i f i c a d o de l a matriz
i n t s u b i n d f ] = I i l . i 2. i 3 I :
i n t i = d e s p l a z a m i e n t o t s u b i n d ):
i f ( i — - 1 ) S y s t e m . e x i t ( - 1 ) : // s u b í n d i c e fuera de r a n g o
m a t r i z f i ] = dato:

p u b lic double obtenerOatot int il )


I
return obtenerDatot il. 0. 0);

p u b lic double obtenerDatot int il. int i2 )


I
return obtenerDatot il. i2. 0);
CA PÍTU LO 9: CLASES Y PAQUETES 3 2 5

public double obtenerDatoí int 11. int i2, int i3 )


{
II O b t e n e r e l v a l o r al e l e m e n t o e s p e c i f i c a d o d e l a m a t r i z
i n t s u b i n d [ ] = I i 1. i 2 . i 3 I :
i n t i - d e s p l a z a m i e n t o í s u b i n d ):
i f ( i — - 1 ) S y s t e m . e x i t ( - 1 ) ; // s u b í n d i c e f u e r a d e r a n g o
r e t u r n m a t r i z [ i ];

//////////////////////////////////////////////////////////////////

A continuación se m uestra la respuesta a la pregunta 9.

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

R espuesta a la pregunta 11. N o es necesario escribir el m étodo fin alize por­


que de liberar la m em oria asignada dinám icam ente (operador new ) se encarga el
recolector de basura.
326 JA V A : C U R SO DE PROGRAM A CIÓN

R espuesta a la pregunta 12. Los m étodos invocados cuando se ejecuta la sen­


tencia C M a t r i z m( A, B ) son:

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

R espuesta a la pregunta 13. Los m étodos invocados cuando se ejecuta la sen­


tencia m . o b t e n e r D a t o ! i . j ) son:

p u b lic double obtenerDato( in t i l , int i2 )


p u b lic double obtenerDato( i n t i l . i n t i 2. i n t i3 )
p u b lic in t desplazamiento( i n t [] subind )

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

A su vez, la m atriz, q u e irá creciendo a m edida que se vayan añadiendo palabras,


será un atributo d e la clase C F recuenciasP alabras. L a interfaz de esta clase in­
cluirá al m enos los m étodos: BuscarPcilabra, InsertarP alabra y ObtenerObjPala-
bra.

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

de expresiones se definió la unidad im aginaria V -T , que se representa por De


este m odo podem os escribir que:

2 + V -9 = 2 + 3 -7 -1 = 2 + 3 /, que se representa com o (2, 3).

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.

O peraciones aritm éticas:

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.

public class Complejo


I
p r i vate double real; // p a r t e real
p r i v a t e double i ma g: // p a r t e im aginaria
II...
I

La interfaz de esta clase proporcionará varios conjuntos de m étodos que se pue­


den clasificar de la form a siguiente:

• U no o m ás constructores. El com plejo construido por om isión será el (0, 0).


• P aso de form a p o lar a binóm ica.
• O peraciones aritm éticas sum ar, restar, m ultiplicar y dividir.
328 JA VA : C U R SO DE PROGRAM A CIÓN

• C om paración de com plejos. La igualdad y la desigualdad la realizarem os en


m ódulo y argum ento. El resto de las com paraciones tienen sentido cuando
sólo se com paran los m ódulos.
• O peraciones trigonom étricas.
• O peraciones logaritm o natural, exponencial, potencia y raíz cuadrada.
• O peraciones de entrada/salida.
• C om plejo conjugado, negativo y opuesto.
• O peraciones de asignación.

3. Se quiere escribir un program a para m anipular ecuaciones algebraicas o poli-


nóm icas dependientes de una variable. Por ejem plo:

2x3 - x + 8.25 m ás 5x5 - 2 x3 + 7x2 - 3 igual a 5x5 + 7x2 - x + 5.25

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:

• C onstruir un térm ino, iniciado a cero por om isión.


• A cceder al coeficiente de un térm ino para obtener su valor.
• A cceder al exponente de un térm ino para obtener su valor.
• O btener la cadena de caracteres equivalente a un térm ino con el form ato si­
guiente: {+1-} 7x*4.

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:

• C onstruir un polinom io, inicialm ente con 0 térm inos.


• O btener el núm ero de térm inos que tiene actualm ente el polinom io.
• A signar un térm ino a un polinom io colocándolo en orden ascendente del ex­
ponente. Si el coeficiente es nulo, no se realizará ninguna operación. Cada
vez que se inserte un nuevo térm ino, se increm entará autom áticam ente el ta­
m año del polinom io en uno. El m étodo encargado de esta operación tendrá un
parám etro de la clase CTermino.
• S um ar dos polinom ios. El polinom io resultante quedará tam bién ordenado en
orden ascendente del exponente.
• O btener la cadena de caracteres correspondiente a la representación de un po­
linom io con el form ato siguiente: + 5xA5 - 1x^1 + 5.25.
C A PÍTU LO 10
©F.J.Cebalox/RA-MA

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 on la herencia todas las clases están clasificadas en una jerarq u ía estricta.


C ada clase tiene su superclase (la clase superior en la jerarquía), y cada clase pue­
de tener una o m ás subclases (las clases inferiores en la jerarquía). Las clases que
están en la parte inferior en la jerarq u ía se dice que heredan de las clases que es­
tán en la parte superior en la jerarquía. P or ejem plo, la figura siguiente indica que
las clases C C uentaC orriente y C C uentaAhorro heredan de la clase CCuenta.

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

CLASES Y MÉTODOS ABSTRACTOS


En una jerarquía de clases, una clase es tanto m ás especializada cuanto m ás aleja­
da esté de la raíz, entendiendo por clase raíz aquella de la cual heredan directa o
indirectam ente el resto de las clases de la jerarquía; y al contrario, es tanto más
genérica cuanto m ás cerca esté de la raíz. S irva de ejem plo la clase O b je c t, clase
raíz de la jerarq u ía de clases de Java; es una clase que sólo define los atributos y
el com portam iento com unes a todas las clases. L o m ism o sucederá con la clase
C Cuenta de la figura anterior, será m ás bien una clase genérica, correspondiendo
los atributos y com portam iento más específicos a las clases C C uentaAhorro y
C C uentaC orriente.

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:

public abstract class CCuenta


I
// C u e r p o de l a clase
)

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.

¿Q ué es un m étodo abstracto? Es un m étodo calificado a b s tr a c t con la parti­


cularidad de que no tiene cuerpo. Por ejem plo, el siguiente código declara comi­
siones com o un m étodo abstracto:

public abstract c la ss CCuenta


I
// . ..
public abstract void com isiones!):
/ / ...
I

¿P or qué no tiene cuerpo? Porque la idea es proporcionar m étodos que deban


ser redefinidos en las subclases de la clase abstracta, con la intención de adaptar­
los a las necesidades particulares de éstas.

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:

1. C uando en un principio se abordó el diseño de una aplicación para adm inis­


trar las cuentas de una entidad bancaria, fue suficiente con las capacidades
proporcionadas por la clase C Cuenta. Posteriorm ente, la evolución de los
m ercados bancarios, sugirió nuevas m odalidades de cuentas. L a m ejor solu­
ción para adaptar la aplicación a esas nuevas exigencias fue definir una sub­
clase para cada nueva m odalidad, puesto que el m ecanism o de herencia ponía
a disposición de las subclases todo el código de su superclase, al que sólo era
necesario añ ad ir las nuevas especificaciones. E videntem ente, la herencia es
una form a sencilla de reutilizar e l código proporcionado p o r otras clases.

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.

En los dos casos planteados, la herencia es la solución para reutilizar código


perteneciente a otras clases. Para ilustrar el m ecanism o de herencia vam os a im-
plem entar la jerarq u ía de clases de la figura anterior. La idea es diseñar una apli­
cación para adm inistrar las cuentas corrientes y de ahorro de los clientes de una
entidad bancaria. C om o am bas cuentas tienen bastantes cosas en com ún, hemos
decidido ag rupar éstas en una clase C C uenta de la cual posteriorm ente derivare­
m os las cuentas específicas que vayan surgiendo. Según este planteam iento, no
parece que tengam os intención de crear objetos de C C uenta; m ás bien la intención
es que agrupe el código com ún que heredarán sus subclases, razón por la cual la
declararem os abstracta.

Pensem os entonces inicialm ente en el diseño de la clase C Cuenta. Después de


un análisis acerca de los factores que intervienen en una cuenta en general, llega­
mos a la conclusión de que los atributos y m étodos com unes a cualquier tipo de
cuenta son los siguientes:

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.

El código correspondiente a esta clase se expone a continuación:

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

public void a s i g n a r N o m b r e í S t r i n g nom)


I
if ( n o m . 1e n g t h í ) = 0)
I
S y s t e m . o u t . p r i n t l n ( “E r r o r : cadena v a c i a " ) :
return;
I
no mb r e = nom:
I

public String obtenerNombre!)


I
return n o mbr e :
I

public void a sig n a rC u e n ta íS trin g cue)


(
if ( c u e . l e n g t h ( ) = = 0)
I
S y s t e m . o u t . p r i n t l n ( " E r r o r : c u e n t a no v á l i d a " ) :
return:
I
c u e n t a = cue:
1

public String o b te nerC u enta ()


I
return cuenta:
)

p u b lic double e s t a d o ! )
I
return saldo:
)

public abstract void com isiones!):

public a b s t r a c t double intereses!):

public void ingreso(double cantidad)


I
if (cantidad < 0)
I
System .out.p r i n t l n ( " E r r o r : cantidad negativa"):
return:
I
sa ldo += cantidad:
334 JA V A: C U R SO D E PROGRAM A CIÓN

public void rein tegro(dou ble cantidad)


I
if (saldo - cantidad < 0)
I
System .out.p rintln !"Error: no d i s p o n e de s a l d o ” );
return:
I
saldo -= cantidad:
I

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

public void asignarTipoDeInterés(double tipo)


I
if ( t i po < 0)
I
S y ste m .o u t.p rin tln ("E rro r: tipo no v á l i d o ” ):
return;
I
tipoDelnterés = tipo;
I
I
//////////////////////////////////////////////////////////////////

Si ahora, utilizando la definición de la clase anterior intenta ejecutar una línea


de código com o la siguiente, obtendrá un error indicándole que la clase es abs­
tracta.

CCuenta clienteOl = new C C u e n t a O :

DEFINIR UNA SUBCLASE


Pensem os ahora en un tipo de cuenta específico, com o es una cuenta de ahorro.
U na cuenta de ahorro tiene las características aportadas p o r un objeto CCuenta, y
adem ás algunas otras; por ejem plo, un atributo que especifique el im porte que hay
que pagar m ensualm ente p o r el m antenim iento de la m ism a. Esto significa que
necesitam os diseñar una nueva clase, C C uentaA horro, que tenga las m ism as ca­
pacidades de C Cuenta, pero a las que hay que añadir otras que den solución a las
nuevas necesidades.

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

public class CCuentaAhorro


I
II A t r i b u t o s y m é t o d o s d e C C u e n t a
// N u e v o s a t r i b u t o s y m é t o d o s de C C u e n t a A h o r r o
I

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.

U na subclase es un nuevo tipo de objetos definido por el usuario que tiene la


propiedad de heredar los atributos y m étodos de o tra clase definida previam ente,
denom inada superclase. La sintaxis para definir una subclase es la siguiente:

class nom bre_subclase e x te n d s nom bre_superclase


{
II C uerpo de la subclase
}

L a palabra clave e x te n d s significa que se está definiendo una clase denom i­


nada nom bre_subclase que es una extensión de otra denom inada nom bre_super-
clase\ tam bién se puede decir que n o m b rejsu b c la se es una clase derivada de
nom bre_supe reíase.

El ejem plo m ostrado a continuación define la clase C C uentaA horro com o una
extensión de CCuenta.

public class CCuentaAhorro extends CCuenta


I
II C C u e n t a A h o r r o ha h e r e d a d o l o s m i e m b r o s de C C u e n t a
II E s c r i b a a q u í l o s n u e v o s a t r i b u t o s y m é t o d o s de C C u e n t a A h o r r o
1

Si no se especifica la cláusula e x te n d s con el nom bre d e la superclase, se en­


tiende que la superclase es la clase O b je c t. Por lo tanto, la clase C C uenta está d e ­
rivada de la clase O b jec t.

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.

Control de acceso a los miembros de las clases


En el capítulo dedicado a clases se expuso que para controlar el acceso a los
m iem bros de una clase, Java provee las palabras clave p riv a te (privado), protec-
ted (protegido) y p u b lic (público), o bien pueden om itirse (acceso predeterm ina­
do). Lo allí estudiado se am plía ahora para las subclases. Para evitar confusiones,
la tabla siguiente resum e de una form a clara qué clases, o subclases, pueden acce­
d er a los m iem bros de otra clase, dependiendo del control de acceso especificado:

Un m iem bro declarado en una clase com o


Puede ser accedido desde: p rivado predeterm inado pro teg id o público
Su m ism a c l a s e ................... sí sí sí sí
C u alquier clase o subclase
del m ism o p a q u e te ............. no sí sí sí
C u alquier clase de otro
p a q u e te ................................... no no no sí
C ualquier subclase de otro
p a q u e te ................................... no no sí sí

Qué miembros hereda una subclase


Los siguientes puntos resum en las reglas a tener en cuenta cuando se define una
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 no tiene acceso directo a los m iem bros privados (p rivate) de su


superclase.
C A PÍTU LO 10: SUBC LA SES E IN TERFACES 3 3 7

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.

2. U na subclase puede añadir sus propios atributos y m étodos. Si el nom bre de


alguno de estos m iem bros coincide con el de un m iem bro heredado, este últi­
m o queda oculto para la subclase, que se traduce en que la subclase ya no
puede acceder directam ente a ese m iem bro. L ógicam ente, lo expuesto tiene
sentido siem pre que nos refiram os a los m iem bros de la superclase a los que
la subclase podía acceder, según el control de acceso aplicado.

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.

L a definición correspondiente a esta clase se expone a continuación:

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

public void asignarCuotaMantentdouble cantidad)


I
if (cantidad < 0)
I
S y s t e m . o u t .p r in t ln ( "E r r o r : cantidad negativa"):
return;
I
cuotaMantenimiento = cantidad:

public double obtenerCuotaM antent)


I
return cuotaMantenimiento;

public void com isiones!)


(
// 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 de l a c u e n t a
G r e g o r i a n C a l e n d a r f e c h a A c t u a l = new G r e g o r i a n C a l e n d a r t );
in t dia = fechaActual.get(Calendar.DA Y_0F_M0NTH):

if (dia = = 1) reintegro(cuotaM antenim iento):

p u b lic double intereses))


I
G r e g o r i a n C a l e n d a r f e c h a A c t u a l = new G r e g o r i a n C a 1e n d a r ( );
in t día = fechaActual.get(Calendar.DAY_OF_MONTH):

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

// D e v o l v e r e l i n t e r é s m e n s u a l por si fuera necesario


return in te re se sP ro d u cid o s;
I
//////////////////////////////////////////////////////////////////

CCuentciAhorro es una subclase de la superclase C Cuenta. O bserve que para


definir una subclase se añade a continuación del nom bre de la m ism a la palabra
reservada e x ten d s y el nom bre de la superclase. En la definición de la subclase se
describen las características adicionales que la distinguen de la superclase.

La capacidad de la clase C Cuenta está soportada por:


CA PÍTU LO 10: SUBCLASES E INTERFACES 3 3 9

A tributos M étodos

nom bre constructores C C uenta


cuenta asignarN om bre
saldo obtenerN om bre
tipoD elnterés asignarC uenta
obtenerC uenta
estado
com isiones
intereses
ingreso
reintegro
asignarT ipoD elnterés
obtenerT ipoD elnterés

La capacidad de la clase C C uentaA horro, derivada de C Cuenta, está soporta­


da p o r los m iem bros heredados de C Cuenta (en cursiva y no tachados) m ás los
suyos:

A tributos M étodos

nom bre constructores C Cuenta


cuenta asignarN om bre
saldo obtenerN om bre
tipoD elnterés asignarC uenta
obtenerC uenta
estado

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

O bserve que los constructores de la clase CCuenta no se heredan, puesto que


cada clase define el suyo por om isión, y que los m étodos com isiones e intereses
quedan ocultos por los m étodos del m ism o nom bre de la clase C Cuenta Ahorro.
Un poco m ás adelante verem os que es posible referirse a un m iem bro oculto utili­
zando la palabra reservada s u p e r de Java: s u p e r .m iem bro_oculto.
340 JA V A: C U R S O D E PROGRAM A CIÓN

Según el análisis anterior, m ientras un posible objeto C Cuenta contendría los


datos nom bre, cuenta, saldo y tipoD elnterés, un objeto C C uentaAhorro contiene
los datos nom bre, cuenta, saldo, tipoD elnterés y cuotaM antenim iento.

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 .

Partim os del hecho de que las clases C Cuenta y C C uentaAhorro pertenecen al


m ism o paquete que la clase Test. Entonces, un “objeto” , com o clienteO l, de la
clase C C uentaAhorro puede invocar a cualquiera de los m étodos públicos, prote­
gidos y predeterm inados de C C uentaAhorro y de C Cuenta, pero no tiene acceso a
sus m iem bros privados. Si las clases C C uentaA horro y C C uenta pertenecieran a
o tro paquete, la clase Test sólo tendría acceso a los m iem bros públicos.

L os “ m étodos” de una subclase no tienen acceso a los m iem bros privados de


su superclase, pero s í lo tienen a sus m iem bros protegidos y públicos; y si la sub­
clase pertenece al m ism o paquete que la superclase, tam bién tiene acceso a sus
m iem bros predeterm inados. Por ejem plo, el m étodo com isiones de la clase
C C uentaA horro no puede acceder al atributo saldo de la clase C C uenta porque es
privado, pero s í puede acceder a su m étodo público reintegro.

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 de l a c u e n t a
G r e g o r i a n C a l e n d a r f e c h a A c t u a l = new G r e g o r i a n C a l e n d a r ( ):
int día = fechaActual,get(Calendar.DAY_0F_MONTH);

if ( d i a == 1) reintegro(cuotaM antenim iento);


CA PÍTU LO 10: SUBC LA SES E IN TERFACES 3 4 1

E sta restricción puede sorprender, pero es así para im poner la encapsulación.


Si una subclase tuviera acceso a los m iem bros privados de su superclase, entonces
cualquiera podría acceder a los m iem bros privados de una clase, sim plem ente d e ­
rivando una clase de ella. C onsecuentem ente, si una subclase quiere acceder a los
m iem bros privados de su superclase, debe hacerlo a través de la interfaz pública,
protegida, o predeterm inada en su caso, de dicha superclase.

ATRIBUTOS CON EL MISMO NOMBRE


C om o sabem os, una subclase puede acceder directam ente a un atributo público,
protegido, o predeterm inado en su caso, de su superclase. Q ué sucede si defini­
m os en la subclase uno de estos atributos, con el m ism o nom bre que tiene en la
superclase. P o r ejem plo, supongam os que una clase C laseA define un atributo
identificado por atributo_x, que después redefinim os en una subclase ClaseB:

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 :

class ClaseB extends ClaseA


I
protected int a t r i b u t o _ x = 2:

public int método_x()


I
return atributo_x * -10:

A hora, la definición del atributo atributo_x en la subclase oculta la definición


del atributo con el m ism o nom bre en la superclase. Por lo tanto, la últim a línea de
código del ejem plo siguiente devolverá el valor de atributo_x de ClaseB. Si este
atributo no hubiera sido definido en la subclase, entonces el valor devuelto sería el
valor de atributo_x de la superclase. Se puede observar que el tipo de control de
acceso puede m odificarse en cualquier sentido.
342 JA V A : C U R SO DE PROGRAM A CIÓN

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

A h o ra bien ¿cóm o procederíam os si el m étodo referenciado por el m étodo j e


de la clase C laseB tuviera que acceder obligatoriam ente al dato atributo_jc de la
superclase? La solución es sencilla: utilizar para ese atributo nom bres diferentes
en la superclase y en la subclase. N o obstante, aun habiendo utilizado el mismo
nom bre, tenem os una alternativa de acceso: utilizar la palabra reservada super.
Por ejem plo:

public in t método_x()
I
return su p e r.atribu to_x * -10;
I

C om o se puede ver, podem os referirnos al dato atributo_x de la superclase


con la expresión:

super.atributo_x

A sim ism o, podríam os referirnos al d ato atributo_x de la subclase con la ex­


presión:

th is.atribu to_x

En cam bio, la expresión siguiente hace referencia al dato atributo_x de la


C lase A:

( ( C1a s e A ) t h i s ) . a t r i buto_x

La técnica de realizar una conversión explícita u obligada es la que tendremos


que utilizar si necesitam os referim os a un m iem bro oculto perteneciente a una
clase p o r encim a de la superclase (una clase base indirecta).
C A P ÍT U L O 10: SUBC LA SES E IN TERFA CES 3 4 3

REDEFINIR MÉTODOS DE LA SUPERCLASE

C uando se invoca a un m étodo en respuesta a un m ensaje recibido por un objeto.


Java busca su definición en la clase del objeto. La definición del m étodo que allí
se encuentra puede pertenecer a la propia clase o puede haber sido heredada de
alguna de sus superclases (esto últim o equivale a decir que si no la encuentra. Ja­
va sigue buscando hacia arriba en la jerarquía de clases hasta que la localice).

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.

R edefinir un m étodo heredado significa volverlo a escribir en la subclase con


el m ism o nom bre, la m ism a lista de parám etros y el m ism o tipo del valor retom a­
do que tenía en la superclase; su cuerpo será adaptado a las necesidades de la sub­
clase. Esto es lo que se ha hecho con el m éto d o _ x del ejem plo expuesto en el
apartado anterior.

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 m étodo m a in de la clase Test del ejem plo anterior, se creó un objeto


objC laseB y se invocó a su m étodo_y. C om o la clase del objeto, C laseB , no define
este m étodo. Java ejecuta el heredado. A sim ism o, se invocó a su m étodo_x\ en
este caso, existe una definición para este m étodo, que es la que se ejecuta.

C uando en una subclase se redefine un m étodo de una superclase, se oculta el


m étodo de la superclase, pero no las sobrecargas que existan del m ism o en dicha
superclase. Si el m étodo se redefine en la subclase con distinto tipo o núm ero de
parám etros, el m étodo de la superclase no se oculta, sino que se com porta com o
una sobrecarga de ese m étodo. P or ejem plo, el m étodo_x tal cual lo hem os redefi­
nido en la subclase oculta al m étodo del m ism o nom bre de la superclase. Pero si
lo hubiéram os definido con distinto núm ero de parám etros, por ejem plo con uno,
según se m uestra a continuación, sería una sobrecarga.

public int m étodo_x(int a) // m é t od o de C l a s e B


I
return atributo_x * - a;
I

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

A diferencia de lo que ocurría con los atributos redefinidos, el control de ac­


ceso de un m étodo que se redefine no puede m odificarse en cualquier sentido.
C om o regla, no se puede redefinir un m étodo en una subclase y hacer que su con­
trol de acceso sea m ás restrictivo que el original. El orden de los tipos de control
de acceso de m ás a m enos restrictivo es así: private, predeterm inado, protected y
public. A plicando esta regla, un m étodo que en la superclase sea protected, en la
subclase podrá ser redefínido com o protected o pub lic: si es p u b lic sólo podrá
ser redefínido com o public. Si es p riv a te no tiene sentido hablar de redefinición,
porque no puede ser accedido nada m ás que desde su propia clase.

Para acceder a un m étodo de la superclase que ha sido redefínido en la sub­


clase, igual que se expuso para los atributos, tendrem os que utilizar la palabra re­
servada super. Por ejem plo, suponga que añadim os el siguiente m étodo a la
C laseB :

public int método_z()


I
a t r i b u t o _ x - s u p e r . a t r i b u t o _ x + 3;
return super.método_x() + atributo_x;

C om o se puede observar, podem os referim os al m étodo_x de la superclase


con la expresión:

super,método_x()

Es im portante resaltar que su p e r puede ser utilizado sólo desde dentro de la


clase que proporciona los m iem bros redefinidos.

A sim ism o, com o ya vim os cuando se expuso this. podríam os referim os al


m étodo_x de la subclase así:

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

En el ejem plo anterior, th is lógicam ente es una referencia a un objeto de la


C laseB ; esta referencia es convertida explícitam ente a un objeto de la C lase A ; pe­
ro, independientem ente de esto, en Java, el m étodo invocado, cuando se trate de
un m étodo redefinido, siem pre pertenece a la clase del objeto no de la referencia;
por lo tanto, el m étodo_x invocado será el de la ClaseB.

CONSTRUCTORES DE LAS SUBCLASES


Sabem os q ue cuando se crea un objeto de una clase se invoca a su constructor.
T am bién sabem os que los constructores de la superclase no son heredados por sus
subclases. En cam bio, cuando se crea un objeto de una subclase, se invoca a su
constructor, que a su vez invoca al constructor sin parám etros de la superclase,
que a su vez invoca al constructor de su superclase, y a sí sucesivam ente.

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:

nom bre_subclase( lista d e parám etros )


{
s u p e r( lista d e p a rá m e tro s);
// cuerpo del constructor de la subclase
}

En la definición genérica anterior correspondiente a un constructor con pará­


m etros de una subclase, se observa, por una parte, la utilización de la palabra re ­
servada s u p e r para invocar al constructor de la superclase, y p o r otra, el cuerpo
del constructor de la subclase. C uando desde un constructor de una subclase se
invoque al constructor de su superclase, esta línea tiene que ser la prim era.

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

Si la superclase no tiene un constructor de form a explícita o tiene uno que no


requiere parám etros, no se necesita invocarlo explícitam ente, ya que Java lo invo­
cará autom áticam ente m ediante su p e r() sin argum entos. Por el contrario, sí es ne­
cesario invocarlo cuando se trate de un constructor con parám etros, para poder así
pasar los argum entos necesarios en la llam ada.

P o r ejem plo, aplicando la teoría expuesta, vam os a añadir a la clase CCuen-


taA horro un constructor con parám etros. ¿C uántos parám etros debe tener este
constructor para iniciar todos los atributos del un objeto CCuentaAhorro^. Pues
tantos com o atributos heredados y propios tenga la clase; en nuestro caso un ob­
je to C C uentaAhorro contiene los atributos nom bre, cuenta, sa ld o , tipoD elnterés y
cuotaM antenim iento. Según esto el constructor podría ser así:

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

La prim era línea del m étodo constructor anterior llam a al constructor de


C Cuenta. superclase de C CuentaAhorro. Lógicam ente, la clase C C uenta debe te­
ner un constructor con cuatro parám etros del tipo de los argum entos especifica­
dos. L a segunda línea invoca al m étodo asignarC uotaM anten para iniciar el
atributo cuotaM antenim iento de CCuentaAhorro.

E videntem ente, si sólo se desea iniciar algunos de los atributos de un objeto,


hay que escribir los constructores adecuados tanto en la subclase com o en la su­
perclase.

De acuerdo con los m étodos constructores definidos en la clase CCuentaAho­


rro, son declaraciones válidas las siguientes:

public class Test


I
public static void m a in (S trin g [] args)
I
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 C u e n t a A h o r r o c l i e n t e 0 2 - new C C u e n t a A h o r r o ! " U n n o m b r e " ,
"Una c u e n t a " . 1 0 00 000 , 3 . 5 , 300);
// . . .

En este ejem plo, la sentencia prim era requiere en C C uentaAhorro un cons­


tructor sin parám etros y en C C uenta otro. E n cam bio, la segunda sentencia re-
CA PÍTU LO 10: SUBC LA SES E IN TERFA CES 3 4 7

quiere en C C uentaAhorro un constructor con parám etros y en C C uenta otro que


se pueda invocar com o se indica a continuación, con el fin de iniciar los atributos
definidos en la superclase, con los valores pasados com o argum entos.

superlnombre, cuenta, saldo, tipoln terés);

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.

DESTRUCTORES DE LAS SUBCLASES


A cabam os de ver que en Java, el constructor de una subclase invoca autom ática­
m ente al constructor sin parám etros de su superclase. En cam bio, con los des­
tructores (m étodos finalize) no ocurre los mismo.

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

protected void fin alize O throw s Throwable // d e s t r u c t o r


1
System .out.println!"Recursos de C l a s e A liberados"):

class ClaseB extends ClaseA


I
protected int a t r i b u t o _ x = 2;

publ i c int método_x()


I
return atributo_x * -10:
1

public int método_z()


1
a t r i b u t o _ x = s u p e r . a t r i b u t o _ x + 3:
return super.método_x() + atribu to_x:
I

protected void finalize!) t h r o w s T h r o w a b l e // d e s t r u c t o r


t
S y s t e m , o u t . p r i n t í ri( " R e c u r s o s de C l a s e B liberados");
s u p e r . f i n a l i z e ( );
I
I

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

Para ver cóm o son invocados los destructores, el m étodo m a in de la clase


T est crea un objeto de la C laseB referenciado p o r objC laseB y cuando finaliza el
trabajo con el m ism o asigna a la variable objC laseB el valor nuil con la intención
de enviar el objeto referenciado p o r ella a la basura. Finalm ente fuerza al reco­
lector de basura a que recoja la basura, lo que provocará la ejecución de los des­
tructores: prim ero el de la C laseB y después el de la ClaseA.

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.

protected void f i n a l i z e ( ) throws T h r o w a b l e // d e s t r u c t o r


I
// N i n g u n a o p e r a c i ó n
s u p e r . f i n a l i z e ( ) ; // i n v o c a r a l m ét o d o f i n a l i z e de l a superclase
I

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

_________ I ~ _______________ I____________


^ la s e ^ C C u e n t a C o r r ie ^ ^ ^ ^ ^ la s ^ C u e n ta A h o r r 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

El conjunto de clases así definido da lugar a una jera rq u ía de clases. Cuando


cada subclase lo es de una sola superclase, com o ocurre en Java, la estructura je ­
rárquica recibe el nom bre de árbol de clases.

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.

L as reglas que podem os aplicar para m anipular la subclase CCuentaCorriente


de la superclase CCuenta o la subclase C C uentaC orrienteC onln de la superclase
C C uentaC orriente son las m ism as que hem os aplicado anteriorm ente para la sub­
clase C C uentaAhorro de la superclase C C uenta, y lo m ism o direm os para cual­
quier otra subclase que deseem os añadir. Esto quiere decir que para im plem entar
una subclase com o C C uentaC orrienteC onln, nos es suficiente con conocer a fon­
do su superclase C C uentaC orriente sin im portarnos CCuenta.

O bserve que la clase C C uenta actúa com o superclase de m ás de una clase,


concretam ente de las clases C C uentaA horro y C C uentaC orriente.

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

reintegro R esta la cantidad especificada del saldo actual de la


cuenta e increm enta el núm ero de transacciones.
com isiones Se ejecuta los días uno de cada m es para cobrar el
im porte de las transacciones efectuadas que no estén
exentas y pone el núm ero de transacciones a cero.
intereses Se ejecuta los días uno de cada m es para calcular el
im porte correspondiente a los intereses/m es produci­
dos y añadirlo al saldo. H asta 3000 euros al 0.5% . El
resto al interés establecido.

A plicando la teoría expuesta hasta ahora y procediendo de form a sim ilar a


com o lo hicim os para construir la subclase C C uentaA horro, la definición de la
clase C C uentaC orriente es la siguiente:

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

public void asignarlm portePorTransídouble imptrans)


I
if (im ptrans < 0)
I
S y s t e m . o u t .p r in t ín ( "E r r o r : cantidad negativa");
352 JA V A: C U R SO DE PROGRAM A CIÓN

return:
1
importePorTrans = imptrans;

public double o b te n e r Im p o r t e P o r T r a n s()


(
return importePorTrans:

public void a sig n a rT ra n sE x e n ta síin t transex)


I
if (transex < 0)
(
System .out.p rintln("Error: cantidad negativa"):
return;
)
transExentas = transex;

public int obtenerTransExentas()


I
return tra n sE xe n ta s:

public void in gre soíd o u b le cantidad)


I
supe r.ingreso(cantid ad):
transacciones++;

public void reintegrotdouble cantidad)


I
super.reintegro(cantidad);
tr a n s a c c i ones++;

public void c o m isio n e sO


I
// Se 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 de l a c u e n t a
G r e g o r i a n C a l e n d a r f e c h a A c t u a l = new G r e g o r i a n C a l e n d a r f );
i n t d í a = f e c h a A c t u a l . g e t ( C a l e n d a r . D A Y _ 0 F _ M 0 N T H );
i f (día — 1 )
1
int n = transacciones - transExentas:
i f (n > 0) r e i n t e g r o t n * im p o r t e P o r T r a n s ) ;
t r a n s a c c i o n e s = 0:
C A PÍTU LO 10: SUBC LA SES E IN TERFACES 3 5 3

p u b lic double intereses!)


I
G r e g o r i a n C a l e n d a r f e c h a A c t u a l - new G r e g o r i a n C a l e n d a r ! );
i n t d i a - f e c h a A c t u a l . g e t ( C a l e n d a r . D A Y _ 0 F _M 0 N T H ):

if (día !- 1) return 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 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.

La definición correspondiente a esta clase se expone a continuación:

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

p u b l i c C C u e n t a C o r r i e n t e C o n I n ( S t r i n g nom. S t r i n g c u e , double sal,


double t ip o , double im ptrans, i n t transex)
I
II I n v o c a r al c o n s t r u c t o r d e l a s u p e r c l a s e
superínom , cue. s a l . t i p o , im p tra n s. t r a n s e x ) ;

p u b lic double intereses!)


1
G r e g o r i a n C a l e n d a r f e c h a A c t u a l = new G r e g o r i a n C a l e n d a r ! );
in t día = fechaActual.get(Calendar.DAY_OF_MONTH):

if (día ! = 1 || e s t a d o ! ) < 3000) return 0.0:

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

// D e v o l v e r e l i n t e r é s m e n s u a l por s i fuera necesario


return interesesProducidos:

//////////////////////////////////////////////////////////////////

La clase C C uenta es la “ superclase directa” (o sim plem ente superclase) de


C C uentaA horro y de C C uentaC orriente, y es una “superclase indirecta” para
C C uentaC orrienteC onln. M ientras que una subclase tiene una única superclase
C A PÍTU LO 10: SU B C LA SE S E INTERFACES 3 5 5

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.

U na subclase que redefina un m étodo heredado sólo tiene acceso a su propia


versión y a la publicada por su superclase directa. Por ejem plo, las clases C C uen­
ta y C C uentaC orriente incluyen cada una su versión del m étodo ingreso', y la
subclase C C uentaC orrienteC onln hereda el m étodo ingreso de C C uentaC orrien­
te. Entonces, C C uentaC orrienteC onln, adem ás de a su propia versión, sólo puede
acceder a la versión de su superclase directa por m edio de la palabra s u p e r (en
este caso am bas versiones son la m ism a), pero no puede acceder a la versión de su
superclase indirecta C C uenta (s u p e r.s u p e r no es una expresión adm itida por el
com pilador Java).

Según lo expuesto las líneas de código:

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

del m étodo intereses de la clase C C uentaC orriente podrían ser sustituidas p o r la


indicada a continuación, puesto que el m étodo ingreso de C Cuenta no actúa sobre
las transacciones:

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

En cam bio, en el m étodo intereses de la clase C C uentaC orrienteC onln no


podem os proceder de la m ism a form a porque desde esta clase no se puede acceder
a la versión de ingreso de CCuenta.

A continuación se presenta una aplicación con algunos ejem plos de operacio­


nes con objetos de las clases pertenecientes a la jerarquía construida:

public class Test


I
public static void main( S t r i n g [] args)
I
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 !
" Un n o m b r e " , " U n a c u e n t a " , 1 0 0 0 0 , 3.5, 30);

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

En la aplicación anterior se puede observar cóm o el m étodo m a in construye


dos objetos: clienteO l de la clase C C uentaAhorro y clienle02 de la clase C C uen­
taC orrienteC onln.

Para co n struir clienteO l se ha utilizado el constructor C C uentaAhorro con ar­


gum entos. U na vez construido, observe que responde a una serie de m ensajes eje­
cutando los m étodos del m ism o nom bre, unos heredados de su superclase, com o
obtenerN om bre, y otros propios, com o intereses.

En cam bio, para construir cliente02 se ha utilizado el constructor C C uentaC o­


rrienteC onln sin argum entos. U na vez construido, puede tam bién observar que
responde a una serie de m ensajes ejecutando los m étodos del m ism o nom bre, unos
heredados de su superclase directa, com o reintegro, otros heredados de su super­
clase indirecta, com o asignarN om bre, y otros propios, com o intereses.

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.

REFERENCIAS A OBJETOS DE UNA SUBCLASE


Las referencias a objetos de una subclase pueden ser declaradas y m anipuladas de
la m ism a form a que las referencias a objetos de una clase cualquiera, tal y como
C A PÍTU LO 10: SUBC LA SES E IN TERFA CES 3 5 7

ya expusim os en el capítulo 4. V eam os algunos ejem plos basados en la jerarquía


de clases que acabam os de construir:

public class Test


1
public static void m a in (S trin g [] args)
I
CCuentaCorriente c lie n te O I:
c l i e n t e O I = 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 0 ” .
10000. 3 .5 , 30);
/ / ...

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:

public class Test


(
public static v o i d ma i n í S t r i n g [ ] a r g s )
1
CCuentaCorriente c lie n te O I:
c l i e n t e O I = new C C u e n t a C o r r i e n t e C o n I n ( " c l i e n t e O l " . " 1 2 3 4 5 6 7 8 9 0 ” ,
10000, 3 .5 . 1.0. 6);
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 í ):
II...

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.

R esum iendo: cuando accedem os a un objeto de una subclase p o r m edio de


una referencia a su superclase, ese objeto sólo puede ser m anipulado p o r ios mé­
todos de su superclase. P or ejem plo:

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.

En cam bio, cuando se invoca a un m étodo que está definido en la superclase y


redefinido en sus subclases, la versión que se ejecuta depende de la clase del ob­
jeto referenciado, no del tipo de la variable que lo referencia. P or ejem plo:

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

cliente - clie n te 02;


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 C o n l n . 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.

R ecuerde que cuando se invoca a un m étodo que está definido en la supercla­


se y redefinido en sus subclases, la versión que se ejecuta depende de la clase del
objeto referenciado, no del tipo de la variable que lo referencia.

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.

S egún lo expuesto, y en un intento de buscar una codificación m ás genérica,


pensem os en una m atriz de referencias en la que cada elem ento señale a un objeto
de alguna de las subclases d e la jerarq u ía construida anteriorm ente. ¿D e qué tipo
deben ser los elem entos de la m atriz? Según el párrafo anterior deben de ser de la
clase C C uenta; de esta form a ellos podrán alm acenar indistintam ente referencias a
objetos de cualquiera de las subclases. Por ejem plo:

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.

La definición correspondiente a esta clase se expone a continuación:

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

private void unElementoMás(CCuenta[] clientesActuales)


I
nElementos - e l i e n t e s A c t u a l e s . l e n g t h ;
// C r e a r una m a t r i z c o n un e l e m e n t o más
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 + 1 ] ;
// C o p i a r l o s c l i e n t e s q u e ha y a c t u a l m e n t e
f o r ( i n t i = 0; i < nE le m e ntos: i + + )
c l i e n t e s f i ] = e l i e n t e s A c t u a l e s [ i ];
nEle mentos++:

private void unElementoMenos(CCuenta[] clientesActuales)


I
i f ( el i e n t e s A c t u a l e s .1ength == 0) r e t u r n :
i n t k = 0:
nElem entos = el i e n t e s A c t u a l e s .1e n g t h :
// C r e a r una m a t r i z c o n un e l e m e n t o menos
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 - 1 ] ;
// C o p i a r l o s c l i e n t e s no n u l o s que h a y a c t u a l m e n t e
f o r ( i n t i = 0: i < nE le m e ntos: i + + )
i f ( e l i e n t e s A c t u a l e s C i ] != n u i l )
el i e n t e s [ k + + ] = e l i e n t e s A c t u a l e s [ i ];
n E l e m e n t o s - -:

public void insertarC lientet int i, CCuenta o b j e t o )


l
// A s i g n a r al e l e m e n t o i de l a m a t r i z , un n u e v o o b j e t o
i f ( i > = 0 && i < n E l e m e n t o s )
e l i e n t e s í i ] = objeto:
else
S y s t e m . o u t . p r i n t l n í " í n d i c e f u e r a de l i m i t e s " ) ;
I

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

public int longitud*) I re tu r n nElementos: )

public void añadirtCCuenta obj)


I
// A ñ a d i r un o b j e t o a l a m a t r i z
unElementoMásícl i e n t e s ) ;
i n s e r t a r C l i e n t e * n E l e m e n t o s - 1. obj ):

p u b lic boolean e l i m i n a r ( S t r i n g cuenta)


I
II B u s c a r l a c u e n t a y e l i m i n a r e l o b j e t o
f o r ( i n t i = 0: i < n E l e m e n t o s : i + + )
i f ( c u e nta .co m p a re T o ícliente s[i] . obtenerCuenta()) = = 0)
I
c l i e n t e s f i ] = n u i l ; // e n v i a r el objeto a la basura
unElem entoMenostclientes):
return true;
I
return false:

public int buscar*String str. int pos)


I
// B u s c a r un o b j e t o y d e v o l v e r s u p o s i c i ó n
S t r i n g nom. c u e n ;
i f ( s t r = = n u i l ) r e t u r n -1;
i f ( p o s < 0 ) p o s = 0:
f o r ( in t i = pos: i < nElementos: i++ )
I
// B u s c a r p o r e l nombre
nom = e l i e n t e s [ i ] . o b t e n e r N o m b r e * ):
i f (nom = n u il ) continué;
// ¿ s t r e s t á c o n t e n i d a en nom?
i f (n o m . in d e x O f( s tr) > -1)
return i :
// B u s c a r p o r l a c u e n t a
cuen = el i e n t e s t i ] . o b t e n e r C u e n t a * );
i f (cuen == n u i l ) c o n t in u é :
// ¿ s t r e s t á c o n t e n i d a en c u e n ?
i f ( c u e n . i n d e x O f ( s t r ) > -1)
return i :
I
return - 1;

//////////////////////////////////////////////////////////////////
CA PÍTU LO 10: SUBC LA SES E IN TERFACES 3 6 5

A nalizando la clase C B anco, observam os que su constructor inicia la m atriz


clientes con 0 elem entos; que añadir un objeto (un cliente) a la m atriz se hace en
dos pasos: uno, increm entar la m atriz en un elem ento, y dos, asignar la referencia
al objeto al nuevo elem ento de la m atriz; que elim inar un objeto de la m atriz tam ­
bién se hace en dos pasos: uno, poner a nuil el elem ento de la m atriz que referen­
cia al objeto q ue se desea elim inar (el objeto se envía a la basura para que sea
recogido por el recolector de basura), y dos, quitar ese elem ento de la m atriz de-
crem entando su tam año en 1.

N o tar que la operación de increm entar o decrem entar en un elem ento la m a­


triz de referencias clientes, asigna un nuevo espacio de m em oria; el necesario para
el nuevo núm ero de elem entos. E ntonces ¿q u é sucede con el espacio que antes
estaba referenciado p o r clientes? Pues que queda sin referenciar, condición sufi­
ciente para que sea enviado a la basura y recogido p o r el recolector de basura.

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:

La operación elegida será identificada p o r una sentencia sw itch y procesada


de acuerdo al esquem a presentado a continuació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

public static i n t menúO I ... 1

public static void m a in (S trin g [] args)


I
// C r e a r un o b j e t o b a n c o v a c i o (con c e r o ele m entos)
C B a n c o b a n c o = new C B a n c o í ) :

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

El listado com pleto de la aplicación Test se m uestra a continuación. Se puede


observar que la clase aplicación utiliza tres m étodos estáticos: leerD atos, m enú y
el m étodo m ain .

El m étodo leerD atos recibe com o parám etro un valor 1, 2 ó 3 dependiendo


del tipo de objeto que se desee crear: C C uentaA horro, C C uentaC orriente, o
C C uentaC orrienteC onln. Lee los atributos correspondientes al tipo de cuenta ele­
gido e invoca al constructor adecuado. El m étodo devuelve una referencia al nue­
vo objeto construido. Este m étodo será invocado cada vez que se elija la opción
a ñ adir una nueva cuenta.

El m étodo m enú visualiza el m enú anteriorm ente m ostrado, y devuelve el en­


tero correspondiente a la opción elegida.

El m étodo m a in crea el objeto banco e invoca repetidam ente al m étodo m enú


para perm itir elegir la operación program ada que se desee realizar en ese instante
sobre el cliente correspondiente a la cuenta o nom bre especificado.

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 ;

public static i n t menú!)


I
System .out.print("\n\n");
Sy ste m .o u t.p rin tln ("l. Saldo” ) :
S y s t e m . o u t .p r in t ln ! "2. Buscar s ig u i e n t e " )
S y s t e m . o u t . p r i n t l n ! " 3 . I n g r e s o " ):
S y s t e m . o u t . p r i n t l n ! ”4. R e i n t e g r o " ) :
S y s t e m . o u t . p r i n t l n ( " 5 . A ñ a d i r " );
S y s t e m . o u t . p r i n t í n ( " 6 . El i m i n a r " );
S y s t e m . o u t . p r i n t l n ( “7 . M a n t e n i m i e n t o " ) :
S y s t e m . o u t . p r i n t l n ( " 8 . S a l i r ” ):
S y s t e m . o u t . p r i n t l n ! ):
System .out.print!" Opción: "):
i n t op:
do
op = L e e r . d a t o l n t ! );
w h i l e ( o p < 1 || op > 8 ) ;
r e t u r n op:

public static void m a in (S trin g [] args)


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

// C r e a r un o b j e t o b a n c o v a c i o (con cero elementos)


C Ba n c o b a n c o = new C B a n c o ! ) :

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.

¿C uándo un m étodo no podrá pasar a ser un m étodo en línea? C uando el


com pilador no sepa con exactitud a qué versión del m étodo tiene que invocar.
V eam os; si com o en el ejem plo anterior, tenem os una m atriz de referencias a ob­
jeto s de las subclases C C uentaAhorro, C C uentaC orriente o C C uentaC orriente­
C onln, ¿cóm o sabe el com pilador a qué m étodo interés, p o r ejem plo, tiene que
llam ar? El com pilador no puede saber esto. C uando esto sucede, el com pilador
produce código que perm itirá al intérprete Java consultar durante la ejecución qué
m étodo tiene que invocar. C om o el intérprete Java sí sabe a qué objeto, C C uenta­
A horro, C C uentaC orriente o C C uentaC orrienteC onln, se refiere cada uno de los
elem entos de la m atriz, el código añadido por el com pilador será suficiente para
determ inar qué m étodo invocar para cada uno de los objetos.

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.

Definir una interfaz


U na interfaz consta de dos partes: el nom bre de la interfaz precedido por la pala­
bra reservada in te rfa c e , y el cuerpo de la interfaz encerrado entre llaves. Esto es:
372 JA V A: C U R SO DE PROGRAM A CIÓN

[p u blic] in te rfa c e nom bre J n te r fa z e x te n d s superinterfaces


{
cuerpo de la interfaz
}

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 cuerpo de la interfaz puede incluir declaraciones de constantes y declara­


ciones de m étodos (no sus definiciones).

La palabra clave e x te n d s significa que se está definiendo una interfaz que es


una extensión de otras; tam bién se puede decir que es una interfaz derivada de
otras; estas otras se especifican a continuación de e x ten d s separadas p o r com as.
C om o habrá observado, a diferencia de las clases, una interfaz puede derivarse de
m ás de u na superinterfaz. U na interfaz así definida hereda todas las constantes y
m étodos de sus superinterfaces, excepto las constantes y m étodos que queden
ocultos porque se redefinan.

El nom bre de una interfaz se puede utilizar en cualquier lugar donde se pueda
utilizar el nom bre de una clase.

Un ejemplo: la interfaz IFecha


En la jerarq u ía de clases im plem entada anteriorm ente en este m ism o capítulo, no­
sotros declaram os las clases C C uentaA horro, C C uentaC orriente y C C uentaC o­
rrienteC onln com o parte de un conjunto de clases para adm inistrar distintos tipos
de cuentas bancarias. T odas estas clases tienen varios m étodos en com ún; así que
para facilitar, no sólo el diseño, sino el trabajo con m atrices de objetos de dichas
clases, nosotros im plem entam os una superclase genérica, C C uenta, que encapsula
los atributos y los m étodos com unes a todas esas clases; incluso, alguno de esos
m étodos, com o com isiones e intereses, no tenía sentido definirlos en la superclase
porque debían ser después particularizados para cada una de las subclases. Esto
nos condujo a definir esos m étodos com o abstractos, lo que im plicó definir la su­
perclase tam bién abstracta.

L as interfaces, al igual que las clases y m étodos abstractos, proporcionan


plantillas de com portam iento que se espera sean im plem entadas p o r otras clases.
Esto es, una interfaz Java declara un conjunto de m étodos, pero no los define
(sólo aporta los prototipos de los m étodos). Tam bién puede incluir definiciones de
constantes.
C A PÍTU LO 10: SUBC LA SES E INTERFACES 3 7 3

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

La clase G r e g o r ia n C a le n d a r es un proveedor de servicios; en nuestro ejem ­


plo. notificará el día a los objetos derivados de C C uenta cuando intenten ejecutar
sus m étodos com isiones o intereses. Para ello, com o se m uestra a continuación,
proporciona el m étodo get que devuelve el tipo de dato (día, m es, etc.) solicitado:

public class GregorianCalendar extends Calendar


I
// . . .
public final int get(int tipo_de_dato) I ... 1
II...
I

Según hem os planteando el problem a, cualquier objeto derivado de CCuenta


que quiera utilizar un objeto G r e g o r ia n C a le n d a r debe im plem entar el m étodo
día proporcionado por la interfaz ¡Fecha. Este m étodo es el m edio utilizado por el
objeto G r e g o r ia n C a le n d a r para notificar al objeto derivado de CCuenta el día
actual. Según lo expuesto la interfaz / Fecha puede tener el aspecto siguiente:

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 ;

public abstract int dfa():


public abstract i n t mes!);
public abstract i n t a ñ o ( );
)
//////////////////////////////////////////////////////////////////

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.

Utilizar una interfaz


Para utilizar una interfaz hay que añadir el nom bre de la m ism a precedido p o r la
palabra clave im p le m e n ts a la definición de la clase. La palabra clave im ple-
m ents sigue a la palabra clave extends, si existe.

S iguiendo con el ejem plo iniciado en el apartado anterior, una subclase de


C Cuenta com o C C uentaA horro que utilice la interfaz ¡Fecha debe definirse así:

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

p u b lic double intereses!)


I
i f ( d i 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 ca 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 í i p o D 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 );
// D e v o l v e r e l i n t e r é s m e n s u a l p o r s i f u e r a n e c e s a r i o
return interesesProducidos:
C A PÍTU LO 10: SUBC LA SES E INTERFACES 3 7 5

/ / I m p l e m e n t a c i ó n de l o s m ét odo s de l a interfaz IFecha


public int d ía()
I
G r e g o r i a n C a l e n d a r f e c h a A c t u a l ■= new G r e g o r i a n C a l e n d a r ( );
return fechaActual.get(DIA_DEL_M ES);
I
public i n t mes() I r e t u r n 0: í II no s e n e c e s i t a
public in t afloO I r e t u r n 0; I // no s e n e c e s i t a
I
//////////////////////////////////////////////////////////////////

C om o una interfaz sólo aporta declaraciones de m étodos abstractos, es nuestra


obligación definir todos los m étodos en cada una de las clases que utilice la in­
terfaz. N o podem os elegir y definir sólo aquellos m étodos que necesitem os. De no
hacerlo, Java obligaría a que la clase fuera abstracta. Se puede observar tam bién
cóm o el acceso a las constantes definidas en la interfaz es directo.

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

p u b lic double inte resesO


I
if ( d 1a ( ) != 1) return 0.0;

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 om o la clase C C uentaC orriente im plem enta la interfaz ¡Fecha, su subclase


C C uentaC orrienteC onln heredará los nuevos m étodos y constantes. P or lo tanto,
no es necesario agregar a la definición de esta clase la palabra clave im p lem en ts
m ás el nom bre de la interfaz.

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

///////////////////////// 1 I I I I I I 1 1 ///////////////////// I I I I ///////

U na vez realizadas en nuestra jerarq u ía de clases las m odificaciones pro­


puestas com o consecuencia d e hab er añadido la interfaz ¡Fecha, el resultado ob­
tenido desde un punto de vista gráfico es el siguiente:

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.

Clase abstracta frente a interfaz


Llegado a este punto, se preguntará ¿en qué difiere una interfaz de una clase abs­
tracta? Puesto que una interfaz es sim plem ente una lista de constantes y m étodos
abstractos, ¿sería equivalente la clase ¡Fecha siguiente, a la interfaz ¡F echal

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

La respuesta a la pregunta anterior es no. Si ¡Fecha es una clase abstracta,


entonces todas las subclases de C Cuenta, com o C C uentaA horro, que quisieran
u tilizar su funcionalidad para interactuar con G r e g o ria n C a le n d a r tendrían que
derivarse de ella. Pero sucede que las subclases a las que nos referim os ya tienen
una superclase y no pueden tener otra, ya que Java no perm ite la herencia m últiple
de clases: sí perm ite que una interfaz se derive de m últiples interfaces. Por lo
tanto, en casos com o el presentado hay que utilizar una interfaz.

L o anterior es una explicación práctica. U na explicación conceptual puede ser


que G re g o ria n C a le n d a r no debe forzar a sus usuarios a establecer una relación
entre clases. Esto es, no im porta la clase; lo único que im porta es im plem entar
uno o m ás m étodos específicos. Al fin y al cabo, una interfaz no es m ás que un
protocolo que una clase im plem enta cuando necesita utilizarlo.

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

Utilizar una interfaz como un tipo


U na interfaz es un nuevo tipo de datos; un tipo referenciado. P or lo tanto, el nom­
bre de u na interfaz se puede utilizar en cualquier lugar donde pueda aparecer el
nom bre de cualquier otro tipo de datos.

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.

El siguiente ejem plo m uestra cóm o tres clases no relacionadas, C la se l, Cía-


se2 y C lase3, p o r el hecho de im plem entar la m ism a interfaz Ixxx, perm ite definir
una m atriz de objetos de esas clases y aplicar la definición de polim orfism o.

In te rfa z Ixxx

c C la s e l
J C la s e 3
J C la s e 2

public interface Ixxx


1
public abstract v o i d m ( ); // m é t od o m
public abstract void p (); // m é t od o p
CA PÍTU LO 10: SUBC LA SES E IN TERFA CES 3 7 9

public class Clasel implements Ixxx


(
public void m í)
I
S y s t e m . o u t . p r i n t l n í ' m é t o d o m de C l a s e l " ) ;
I
public void p() II

public class Clase? implements Ixxx


(
public void m ()
I
S y s t e m . o u t . p r i n t l n í ’ m é t od o m d e C 1 a s e 2 " ) ;
1
public void p() II

public c la ss Clase3 implements Ixxx


I
public void m()
I
S y s t e m . o u t . p r i n t l n í " m é t o d o m de C í a s e 3 " );
)
public void p() II

public class Test


I
public static v o i d main (Stringf] args)
I ..................... . . . . . .
I x x x [ ] o b j s = n e w I x x x [ 3 ] ¡ / / m a t r i z d e r e f e r e n c i a s a o b j e t o s

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

Interfaces frente a herencia múltiple


A m enudo se piensa en las interfaces com o en una alternativa a la herencia m últi­
ple. Pero la realidad es que am bos conceptos, interfaz y herencia m últiple, son
bastantes diferentes, a pesar de que las interfaces pueden resolver problem as si­
m ilares. En particular:
380 JA V A: C U R SO DE PROGRAM A CIÓN

• D esde una interfaz, una clase sólo hereda constantes.


• D esde una interfaz, una clase no puede heredar definiciones de m étodos.
• L a jerarq u ía de interfaces es independiente de la jerarq u ía de clases. De he­
cho. varias clases pueden im plem entar la m ism a interfaz y no pertenecer a la
m ism a jerarq u ía de clases. En cam bio, cuando se habla de herencia m últiple,
todas las clases pertenecen a la m ism a jerarquía.

Para qué sirve una interfaz


D espués de todo lo expuesto es posible que aún no esté claro cuál es el sentido de
utilizar interfaces. Si analizam os el ejercicio realizado anteriorm ente basado en la
jerarq u ía de clases C C uenta y en la interfaz ¡Fecha, seguro que llegarem os a al­
guna conclusión sim ilar a la siguiente: puesto que los m étodos día, m es y año
pertenecen a la subclase que los im plem enta y las constantes no son ningún obstá­
culo, ¿para qué querem os la interfaz? Pensando así, para nada.

U na interfaz se utiliza para definir un protocolo de conducta que puede ser


im plem entado por cualquier clase en una jerarq u ía de clases. La utilidad que esto
pueda tener puede resum irse en los puntos siguientes:

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

En este caso, otros desarrolladores recibirían la clase com pilada y la interfaz


correspondiente.

Implementar múltiples interfaces


U na clase puede im plem entar una o m ás interfaces. Por ejem plo:
CA PÍTU LO 10: SU B C LA SE S E IN TERFA CES 3 8 1

public class miClase implements inte rfazl, interfaz2. interfaz3


I
II...

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:

public class CPersona


I
// M i e m b r o s d e C P e r s o n a __
p r i v a t e c l a s s CFecha
1
// M i e m b r o s de C F e c h a
I
// O t r o s miembros de C P e r s o n a
I

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.

U na clase anidada es un m iem bro m ás de la clase que la contiene. En el ejem ­


plo anterior la clase C Fecha es un m iem bro m ás de C Persona y com o tal se le
aplican las m ism as reglas que para el resto de los m iem bros. Según esto, C Fecha
tendrá acceso al resto de los m iem bros de C Persona independientem ente de su
m odificador de acceso (decir CFecha. im plica a los m iem bros de C F echa); C F e­
cha puede ser pública, privada o protegida; puede ser estática; etc.
382 JA V A: C U R S O DE PROGRAM A CIÓN

R ecuerde: un m iem bro estático (static) es un m iem bro de la clase y uno no


estático es un m iem bro del objeto; y com o ocurría con los m étodos estáticos, una
clase anidada estática no puede referirse directam ente a un m iem bro del objeto,
sólo puede hacerlo a través de un objeto de su clase.

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.

U n objeto de una clase interna puede existir sólo dentro de un objeto de su


clase externa. A sim ism o, puesto que se trata de un m iem bro de su clase externa,
tiene acceso directo al resto de los m iem bros de esa clase. Por ejem plo, una fecha
puede ser un m iem bro de los datos relativos a la identificación de una persona;
entonces, la persona puede ser representada por una clase C Persona y la fecha por
una clase CFecha, com o puede observar en el código m ostrado a continuación:

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

public C P e r s o n a ( S t r i n g nom. int dd . int mm. int aa)


I
nombre =■ nom;
f e c h a N a c i m i e n t o = new C F e c h a í d d . mm. a a ) :
I

public String obtenerNombret) ( r e t u r n n o mb r e : I

public String obtenerFechaNa()


I
return f e c h a N a c i m i e n t o . d 1a + " / " +
fechaNacimiento.mes + " / " +
fechaNacim iento.año:

El siguiente ejem plo crea un objeto C Persona invocando al constructor de


esta clase, el cual, a su vez, invocará al constructor de C Fecha para crear el objeto
fech a N a cim ien to con la fecha pasada com o argum ento.

public class Test


I
public static void m a in ( S trin g [] args)
I
C P e r s o n a u n a P e r s o n a = new C P e r s o n a ( " S u n o m b r e " . 22. 2. 2002):
System .out. p rin tln (u n a Persona.obtenerNombre());
System .out.println(unaPersona.obtenerFechaNa());

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.

Clases definidas dentro de un método


Java perm ite definir una clase dentro de un m étodo. Por ejem plo, el m étodo met-
C lase2 de la C lase2 m ostrada a continuación incluye la definición de una C lase3:

public class Clasel


I
public static v o i d main (Stringf] args)
I
384 JA V A : C U R SO D E PROGRAM A CIÓN

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

U na clase definida dentro de un m étodo tiene unas reglas de acceso bastante


restrictivas. U n m étodo de una clase definida dentro de otro m étodo sólo tiene ac­
ceso a sus variables locales o parám etros form ales declarados fin al. En el ejem ­
plo, m e tC la se l tiene dos parám etros form ales, x e y, y dos variables locales, i y c.
Se puede observar que el m étodo m etC lase3 de la clase C lase3 definida dentro de
m etC lase2 sólo tiene acceso a las variables locales y parám etros de este que han
sido declarados final.

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

class Clase3 implements Interfaz


I
public void p() I S y s t e m . o u t . p r i n t l n( " m é t o d o p. " ) ; 1
public void m() I S y s t e m . o u t . p r i n t l n ! " m é t o d o m " ); )
I

public class Clasel


I
public static v o i d main (String[] args)
I
C l a s e 2 o b j = new C l a s e 2 ( ) :
I n t e r f a z i o b j = o b j . m e t C l a s e 2 ( ): // d e v u e l v e un o b j e t o C l a s e 3
i o b j , m ( ):
I
I

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.

U na alternativa al ejem plo planteado es d efinir anónim a la clase utilizada


dentro del m étodo (en el ejem plo Clase3). Por ser anónim a, su definición debería
ocupar el lugar donde se utiliza su nom bre para crear un objeto de la misma. Esto
es, la definición y la creación del objeto se harían bajo la siguiente sintaxis:

new Ixxx () { ... }

donde Ixxx es el nom bre de la interfaz que im plem enta la clase. Por ejem plo:

public In te rfa z m etClase2()


I
r e t u r n new I n t e r f a z C )

public void p ( ) 1 System .out.p rintln ("m é todo p"): )


public void m () 1 S y s t e m . o u t . p r i n t l n t " m é t o d o m " ): I
386 JA V A: CU R SO DH PRO G R A M A CIÓ N

Se puede observar que la llam ada a n ew va seguida de la definición de la cla­


se y que no se utiliza el nom bre de la clase, sino el nom bre de la interfaz que la
clase im plem enta; notar que en este caso no interviene la palabra clave im ple-
m e n ts y que la sentencia r e tu r n finaliza con punto y com a.

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:

2xJy - xy3 + 8.25 m ás 5x?y - 2 x 3y + 7xT - 3 igual a 5 x sy + 7x2 - xy3 + 5.25

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 puede escribirse así:

import java.m a th .*:


//////////////////////////////////////////////////////////////////
// C l a s e C T e r m i n o : e x p r e s i ó n de l a f o r m a a . x An . y Am
// a es el c o e f i c i e n t e de t i p o d o u b l e .
// n y m s o n l o s e x p o n e n t e s e n t e r o s de x e y .
//
public class CTermino
I
private double c o e f i c i e n t e = 0.0: // c o e f i c i e n t e
private i n t exponenteDeX = 1 ; II e x p o n e n t e d e x
private i n t e x p o n e n t e D e Y = 1; // e x p o n e n t e d e y

p u b lic CTerm ino() II


p u b l i c CTerminot double co e f, int expx, int expy ) // c o n s t r u c t o r
I
c o e f ic ie n t e = coef;
exponenteDeX = expx;
ex pon en teD eY = expy;
CA PÍTU LO 10: SU B C LA SE S E IN TE R FA C E S 3 8 7

public CTerm ino(CTermino t) // c o n s t r u c t o r c o p i a


I
coeficiente = t.coeficiente:
exponenteDeX = t.exponenteD eX:
exponenteOeY = t.exponenteDeY :
I
p u b l i c CTermino c o p ia r< C T e rm in o t) // a s i g n a c i ó n
I
coeficiente = t.coeficiente:
exponenteDeX = t.exponenteD eX:
exponenteDeY = t . e x p o n e n t e D e Y ;
return th is;
1
public void a sig n a rC o e ficie n te (d o u b le coef) (c o e fic ie n te - coef;l
public double o b t e n e r C o e f ic i e n t e ( ) (re t u rn c o e f i c i e n t e : !
public vo id a s i g n a r E x p o n e n t e D e X ( i n t expx) (exponenteDeX - expx;|
public i n t o b te nerExp on en teD e X í) ( re t u rn exponenteDeX:!
public v o i d asig n a rE x p o n e n t e D e Y ( in t expy) (exponenteDeY - e x p y : l
public i n t o b te n e r E x p o n e n t e D e Y ( ) ( r e t u r n exponenteDeY:!
public void m ostrarTerm ino()
(
i f ( c o e f i c i e n t e = = 0) r e t u r n ;
// S i g n o
S t r i n g sterm = ( c o e f i c i e n t e < 0)
// C o e f i c i e n t e
i f ( M a t h . a b s ( c o e f i c i e n t e ) ! - 1)
s t e r m = s t e r m + M a t h . a b s ( c o e f i c i e n t e ):
// P o t e n c i a de x
i f ( e x p o n e n t e D e X > 1 || e x p o n e n t e D e X < 0)
sterm - sterm + " x A " + exponenteDeX;
e l s e i f ( e x p o n e n t e D e X - - 1)
sterm = sterm + " x " ;
II P o t e n c i a de y
i f ( e x p o n e n t e D e Y > 1 || e x p o n e n t e D e Y < 0 )
sterm = sterm + " y A" + exponenteDeY;
e l s e i f ( e x p o n e n t e D e Y — 1)
sterm - sterm + " y " :
// M o s t r a r t é r m i n o
System .out.printísterm ):

//////////////////////////////////////////////////////////////////

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.

S iguiendo con el desarrollo, el esqueleto de la clase C P olinom io puede escri­


birse así:

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

public CPolinom iot)


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:
t é r m i n o s = new C T e r m i n o í n E l e m e n t o s ] :

private vo id unElementoMás(CTermino[] té rm in osA ct) 1 ... )


private void unElementoMenos(CTermino[] té rm in osA ct) I ... 1
public void insertarTerm inoíCTe rm ino obj) I ... I
public boolean e lim in a rT e rm in o t in t i ) 1 ... I
public C T e r m i n o t éri ni n o E n í i n t i ) I . . . I
public i n t l o n g i t u d O 1 re tu rn nElementos: I
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) ( . . . 1
public C P o l i n o m i o s u m a r ( C P o l i n o m i o pB ) I . . . 1
public void m o st ra rP o lin o m io () ! ... )
public do u b le v a l o r P o l o n o m i o í d o u b l e x. do u b le y ) I . . . I
)
//////////////////////////////////////////////////////////////////

C om o se puede observar, la clase C P olinom io tiene dos atributos: térm inos


que es una m atriz de referencias a objetos C Term ino y nE lem entos que es un ente­
ro que especifica el núm ero de térm inos del polinom io.
C A P ÍT U L O 10: SU B C LA SE S E IN TERFA CES 3 8 9

Para crear un polinom io escribirem os una sentencia análoga a la siguiente:

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

Esta sentencia, invoca al constructor C P olinom io e inicia un polinom io con 0


elem entos, según se puede observar en la clase CPolinom io.

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:

private void unElementoMás(CTermino[] térm inosAct)


I
nEle mentos = t é r m i n o s A c t .1e n g t h ;
// C r e a r una m a t r i z c o n un e l e m e n t o más
t é r m i n o s = new C T e r m i n o [ n E l e m e n t o s + 1 ] ;
// C o p i a r l o s t é r m i n o s q u e h a y a c t u a l m e n t e
f o r ( i n t i = 0: i < nE le m e ntos: i+ + )
t é r m i n o s t i j = t é r m i n o s A c t [ i ]:
n E 1e m e n t o s + + :

private void unElementoMenos(CTermino[] térm inosAct)


I
i f ( té rm in o sA c t.1ength == 0) return:
i n t k = 0;
n E l e m e n t o s = t é r m i n o s A c t . 1e n g t h ;
II C r e a r una m a t r i z c o n un e l e m e n t o s menos
t é r m i n o s = new C T e r m i n o [ n E l e m e n t o s - 1 ] :
// C o p i a r l o s t é r m i n o s n o n u l o s que h a y a c t u a l m e n t e
f o r ( i n t i = 0: i < n E l e m e n t o s : i + + )
i f ( t é r m in o s A c t f i] != n u il )
t é r m i n o s [ k + + ] = t é r m i n o s A c t t i ]:
n E 1ementos - - ;
I

Para añadir un nuevo térm ino en el polinom io escribirem os el m étodo inser-


tarTerm ino, que perm ite insertar el térm ino pasado com o argum ento, en orden as­
cendente del exponente de y a exponentes iguales de x , en orden ascendente de
y. Este m étodo prim eram ente verifica si el coeficiente del térm ino a insertar es 0,
en cuya caso finaliza sin realizar ninguna inserción. Si el coeficiente es distinto de
0, verifica si el térm ino en x y a insertar ya existe, en cuyo caso sim plem ente suma
al coeficiente existente el del térm ino pasado com o argum ento; si el resultado de
esta sum a es cero, invoca adem ás al m étodo elim inarT erm ino para quitar ese tér­
mino. Si el térm ino no existe, entonces lo inserta en el lugar adecuado. Para reali­
zar esta operación, prim ero llam a al m étodo unElem entoM ás (añade un elem ento
390 JA V A: C U R SO D E PROGRAM A CIÓ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.

public void insertarTerminotCTermino obj)


I
// I n s e r t a r un n u e v o t é r m i n o en o r d e n a s c e n d e n t e del
// e x p o n e n t e de x ; y a i g u a l e x p o n e n t e d e x , en o r d e n
// a s c e n d e n t e d e l e x p o n e n t e de y .
if ( o b j.o b t e n e rC o e fic ie n t e !) == 0 ) return:
int k = 10. i :
int ex p X = o b j . o b t e n e r E x p o n e n t e D e X t );
int e xp Y = o b j . o b t e n e r E x p o n e n t e D e Y t );
// S i e l t é r m i n o en x y e x i s t e , s u m a r l o s c o e f i c i e n t e s
for ( i = n E l e m e n t o s - 1: i > = 0 : i - - )
(
if ( ex p X = t é r m i n o s t i ] . o b t e n e r E x p o n e n t e D e X ! ) &&
exp Y = = t é r m i n o s [ i ] . o b t e n e r E x p o n e n t e D e Y ( ) )
I
double coef = t é r m i n o s t i ] .o b t e n e r C o e f ic ie n t e ! ) +
o b j . o b t e n e r C o e f i c i e n t e ! );
i f ( co ef != 0 )
té rm in osfi] . asignarCoeficiente(coef);
el se
e l i m i n a r T e r m i n o ( i );
return:
1
I
// S i e l t é r m i n o en x y no e x i s t e , i n s e r t a r l o .
w h i l e ( M a t h . a b s ( e x p X ) > k || M a t h . a b s ( e xp Y ) > k ) k = k * 10:
// S e a ñ a d e un e l e m e n t o v a c i o
unElem entoMás(térm inos);
i = n E l e m e n t o s - 2 : // i - n E l e m e n t o s - 1 v a l e n u i l
w h i l e ( i > = 0 && ( e x p X * k + e xp Y <
térm inosti].ObtenerExponenteDeXt) * k +
térmi n o s [ i ] . o b te n e rE x p o n e n te D e Y ( ) ) )
I
té rm in o s[i+l] = térm inosti];
i - -:
I
térm inos[i+l] = o b j;
C A PÍTU LO 10: SU B C LA SE S E IN TERFA CES 3 9 1

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.

p u b lic boolean e lim in a rT e r m in o (in t i)


I
// E l i m i n a r e l o b j e t o q u e e s t á en l a posición i
i f ( i > = 0 && i < n E l e m e n t o s )
I
t é r m i n o s E i ] = n u i l : // e n v i a r el objeto a la basura
unElem entoMenos(térm inos);
re tu rn true;
I
return false:
I

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.

Para poder copiar un polinom io en otro escribirem os el m étodo copiar espe­


cificado a continuación:

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:

té rm in o s[i] = p .térm inos[ i ]

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

El proceso de sum ar consiste en:

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.

p u b lic CPolinomio s u m a r ( C P o l i n o m i o pB)


I
/ / pR = p A . s u m a r ( p B ) . pA e s t h i s y pR e l r e s u l t a d o .
int i p a = 0 , i p b = 0 , k = 0:
int na = n E l e m e n t o s . nb = p B . n E l e m e n t o s ;
CA PÍTU LO 10: SUBC LA SES E IN TERFACES 3 9 3

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.

public void m o st ra rP o lin o m io {)


I
int i = nElementos:

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.

p u b lic double valorPo lo no m io(do u ble x, double y)


I
double v = 0:

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

public static CTermino leerTerm ino()


I
CTermino ptx = n u i l ;
double coef;
i n t expx, expy;
System .out.p rin t("C o e fi c ie n te : ");
c o e f = L e e r . d a t o D o u b l e t ):
S y s t e m . o u t . p r i n t í " E x p o n e n t e en X: " ) ;
expx = 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 ( " E x p o n e n t e en Y: " ) ;
e x p y = L e e r . d a t o l n t í );
S y ste m .o u t .p ri n t l n () :
i f ( c o e f = = 0 && e x p x = 0 && e x p y = = 0 ) return nuil:
p t x = new C T e r m i n o t c o e f . e x p x . e x p y ):
re turn ptx;

public static void m a in ( S t r in g [ ] args)


I
// D e f i n i r l o s p o l i n o m i o s a suma r
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 ( ):
C P o l i n o m i o p o l i n o m i o B = new C P o l i nomi o ( ) :
// D e c l a r a r una r e f e r e n c i a a l p o l i n o m i o r e s u l t a n t e
CPolinom io polinomioR;
// D e c l a r a r una r e f e r e n c i a a un t é r m i n o c u a l q u i e r a
C T e r m i n o p t x = n u i l ; // p u n t e r o a un t é r m i n o
// L e e r l o s t é r m i n o s d e l p r i m e r s umando
S y s t e m . o u t . p r i n t t " T é r m i n o s del p o li n o m i o A "
+ " ( p a r a f i n a l i z a r in t r o d u z c a 0 para e l \ n "
+ " c o e f i c i e n t e y p a r a l o s e x p o n e n t e s ) . \ n \ n ” );
p t x = 1e e r T e r m i n o t );
whi 1 e ( p t x ! = n u i l )
I
pol i n o m i o A . i n s e r t a r T e r m i n o t ptx );
p t x = 1e e r T e r m i n o t );
I
// L e e r l o s t é r m i n o s d e l s e g u n d o s umando
S y s t e m . o u t . p r i n t l n ( "T érm ino s del p o li n o m i o B "
+ " ( p a r a f i n a l i z a r i n t r o d u z c a 0 para e l \ n "
+ " c o e f i c i e n t e y p a r a l o s e x p o n e n t e s ) . \ n \ n " );
p t x = 1e e r T e r m i n o t ) :
w h i 1e ( p t x ! = n u l 1 )
I
polinom ioB.insertarTerm inot ptx );
p t x = 1e e r T e r m i n o t ) :
I
// S u m a r l o s d o s p o l i n o m i o s l e í d o s
p o lin o m io R = p o l i n o m i o A . sum ar( p o l i n o m i o B );

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

Se quiere escribir un program a para m anipular ecuaciones algebraicas o polinó-


m icas dependientes de las variables ,v. y, z. Por ejem plo:

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 ^

De acuerdo con el enunciado y apoyándose en el ejercicio anteriorm ente re ­


suelto, construya las clases a las que hem os hecho referencia para que soporten al
m enos la m ism a funcionalidad que vio allí y realice un program a sim ilar al ante­
rior, para probar las clases construidas.
CA PÍTU LO 11
© F.J.Cebilos/RA-MA

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

Básicam ente, el esquem a anterior dice que si el código de la. aplicación no


puede realizar alguna operación, se espera lance una excepción que será tratada
por el código de tratam iento especificado para esa clase de excepción, o en su
defecto por Java.
CA PÍTU LO 11: EXCEPCION ES 3 9 9

A lo largo de este capítulo com probará que: el m anejo de excepciones reduce


la com plejidad de la program ación; los m étodos que invocan a otros no necesitan
com probar valores de retorno; si el m étodo invocado finaliza de form a norm al, el
que llam ó está seguro de que no ocurrió ninguna situación anóm ala; etc.

EXCEPCIONES DE JAVA
D urante el estudio de los capítulos anteriores, seguro que se habrá encontrado con
excepciones com o las siguientes:

C lase de excepción__________________ Significado


A rith m e tic E x c e p tio n U na condición aritm ética excepcional ha
ocurrido. P or ejem plo, una división p o r 0.
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 U na m atriz fue accedida con un índice
ilegal (fuera de los lím ites perm itidos).
N u llP o in te rE x c e p tio n Se intentó utilizar n u il donde se requería
un objeto.
N u m b e rF o rm a tE x c e p tio n Se intento convertir una cadena con un
form ato inapropiado en un núm ero.

¿Q ué es lo que ocurrió entonces cuando durante la ejecución d e su program a


se lanzó una excepción? S eguram ente el program a d ejó de funcionar y Java vi­
sualizó algún m ensaje acerca de lo ocurrido. Si no es esto lo que deseam os, ten­
drem os que aprender a m anipular las excepciones.

Las excepciones en Java son objetos de clases derivadas de la clase T h ro w a -


ble definida en el paquete ja v a .la n g. P or ejem plo, cuando se lanza una excepción
A rith m e tic E x c e p tio n , autom áticam ente Java crea un objeto de esta clase. La fi­
gura siguiente m uestra algunas de las clases de la jerarq u ía de excepciones:

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

Un objeto E r r o r se crea cuando ha ocurrido un problem a serio. N orm alm ente


se lanza u na excepción de este tipo, cuando durante la ejecución ocurre un error
que involucra a la m áquina virtual de Java, por lo que una aplicación norm al no
suele m anipular este tipo de excepció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 .

La clase R u n tim e E x c e p tio n cubre las excepciones ocurridas al ejecutar ope­


raciones sobre los datos que m anipula la aplicación y que residen en m em oria; se
trata de excepciones que se lanzan en tiem po de ejecución, en contraposición a las
que se lanzarían por causas no dependientes de la m áquina virtual de Java, com o
sucedería cuando no se pudiera leer de un fichero del disco. Son ejem plos de ex­
cepciones de este tipo: A rith m e tic E x c e p tio n o N u llP o in te rE x c e p tio n . Este gru­
po de excepciones pertenece al paquete ja v a.lan g.

L a clase IO E x c e p t io n cubre las excepciones ocurridas al ejecutar una opera­


ción de entrada o salida. Este grupo de excepciones pertenece al paquete java.io.

Las excepciones d e tiem po de ejecución son excepciones im plícitas y se c o ­


rresponden con las subclases de R u n tim e E x c e p tio n y E r r o r . Se dice que son
im plícitas porque son lanzadas por la m áquina virtual de Java y por lo tanto, los
m étodos im plem entados en las aplicaciones no tienen que declarar que las lanzan,
y aunque lo hicieran, cualquier otro m étodo que los invoque no está obligado a
m anejarlas. El resto de las excepciones, com o las que se corresponden con las
subclases de IO E x c e p tio n , son excepciones explícitas; esto significa que, si se
quieren m anipular, los m étodos im plem entados en las aplicaciones tienen que de­
clarar que las lanzan y en este caso, cualquier otro m étodo que los invoque está
obligado a m anejarlas.

C om o ejem plo, recuerde que en las aplicaciones desarrolladas hasta ahora el


com pilador Jav a nunca nos obligó a m anejar una excepción de la clase A r ith m e ­
ticE xception. pero sí una excepción de la clase IO E x c e p tio n . Un ejem plo lo te­
nem os cada vez que en alguna parte del código de nuestra aplicación invocam os
al m étodo re a d L in e de la clase B u ffe re d R e a d e r; esta obligación surge de que
r e a d L in e declara q ue puede lanzar una excepción de la clase IO E x c e p tio n :

public String readLineO th rows IOException


I
II...
CAPÍTULO 11: EXCEPCIONES 4 0 1

N o se preocupe si no le quedó todo claro; a continuación aprenderá con deta­


lle cóm o atrapar, crear y lanzar excepciones, adem ás de otras cosas.

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.

P or ejem plo, ¿recuerda la clase L eer desarrollada en el capítulo 5? Según


puede observar a continuación, el m étodo dato de esta clase invoca a re a d L in e
con el propósito de devolver un objeto S t r in g correspondiente a la cadena leída.
Según se ha explicado anteriorm ente, re a d L in e puede lanzar una excepción de la
clase IO E x c e p tio n . Para m anejarla hay que atraparla, para lo cual se utiliza un
bloque catch, y para poder atraparla hay que encerrar el código que puede lan­
zarla en un bloque try.

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

// L e e r . La e n t r a d a f i n a l i z a al pulsar la tecla Entrar


s d a t o = f 1 u j o E . r e a d L i n e ( );
I
catch(IOException e)
I
System .err.printlní"Error: " + e.ge tM e ssag e í));

return sdato: // d e v o l v e r e l dato tecleado

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

Lanzar una excepción


L anzar una excepción equivale a crear un objeto de la clase de la excepción para
m anipularlo fuera del flujo norm al de ejecución del program a. Para lanzar una ex­
cepción se utiliza la palabra reservada th ro w y para crear un objeto, new. Por
ejem plo, volviendo al m étodo datos de la clase L eer expuesta anteriorm ente, si
ocurre un erro r cuando se ejecute el m étodo r e a d L in e se supone que éste ejecuta­
rá una sentencia sim ilar a la siguiente:

if (error) t h r o w new I O E x c e p t i o n ( ):

E sta sentencia lanza una excepción de la clase IO E x c e p t io n lo que im plica


crear un objeto de esta clase. U n objeto de éstos contiene inform ación acerca de la
excepción, incluyendo su tipo y el estado del sistem a cuando el error ocurrió.

Atrapar una excepción


U na vez lanzada la excepción, el sistem a es responsable de encontrar a alguien
que la atrape con el objetivo de m anipularla. El conjunto de esos “alguien” es el
conjunto de m étodos especificados en la pila de llam adas hasta que ocurrió el
error. Por ejem plo, considerem os la siguiente aplicación, que invoca al m étodo
d ato de la clase L eer con la intención de leer un dato:

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

C uando se ejecute esta aplicación y se invoque al m étodo dato , la pila de lla­


m adas crecerá com o se observa en la figura siguiente:
C A PÍTU LO 11: EX CEPCION ES 4 0 3

B u ffe re d R e a d e r.re a d U n e
J

Si al ejecutarse el m étodo re a d L in e ocurriera un error, según hem os visto


anteriorm ente, éste lanzaría una excepción de la clase IO E x c e p t io n que inte­
rrum pirá el flujo norm al de ejecución. D espués, el sistem a buscaría en la pila de
llam adas hacia abajo y com enzando p o r el propio m étodo que produjo el error,
uno que im plem ente un m anejador que pueda atrapar esta excepción. Si el sistem a
descendiendo por la pila de llam adas n o encontrara este m anejador, el program a
term inaría.

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:

1. Encerrar el código que puede lanzar la excepción en un bloque try. En la cla­


se L eer presentada anteriorm ente, el m étodo dato tiene un bloque tr y que en­
cierra la llam ada al m étodo re a d L in e , adem ás de a otras sentencias:

try
I
// ...
s d a t o = f l u j o E . r e a d L i n e í ):
I

2. E scribir un bloque catch capaz de atrapar la excepción lanzada. En la clase


Leer presentada anteriorm ente, el m étodo dato tiene un bloque catch capaz de
atrapar excepciones de la clase IO E x c e p t io n y de sus subclases:

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

En este m anejador se observa un parám etro e que referencia al objeto que se


creó cuando se lanzó la excepción atrapada. Para m anipularla, adem ás de escribir
el código que considerem os adecuado, disponem os de la funcionalidad proporcio­
nada por la clase IO E x c e p tio n , y a la que podrem os acceder m ediante el objeto e.
P or ejem plo, el m étodo g e tM e ssa g e devuelve una cadena con inform ación acerca
de la excepción ocurrida.
404 JA V A : C U R SO DE PR O G R A M A C IÓ N

C uando se trata de m anejar excepciones, un bloque try puede estar seguido de


uno o m ás bloques catch, tantos com o excepciones diferentes tengam os que m a­
nejar. C ada catch tiene un parám etro de la clase T h ro w a b le o de alguna subclase
de ésta. C uando se lance una excepción, el bloque catch que la atrape será aquel
cuyo parám etro sea de la clase o de una superclase de la excepción. D ebido a esto,
el orden en el que se coloquen los bloques catch tiene que ser tal, que cualquiera
de ellos debe perm itir alcanzar el siguiente, de lo contrario el com pilador produci­
ría un error.

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.

En cam bio, en el ejem plo siguiente, una excepción de la clase E O F E x c e p tio n


será atrapada por el prim er bloque catch; una excepción d e la clase IO E x c e p t io n
será atrapada por el bloque segundo; una excepción de la clase F ile N o tF o u n d E x -
ception, subclase de IO E x c e p tio n , será atrapada tam bién p o r el bloque segundo;
y una excepción de la clase C la ssN o tF o u n d E x c e p tio n , subclase de Excep tio n,
será atrapada p o r el bloque tercero.

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

Un m ancjador de excepción, catch, sólo se puede u tilizar justam ente a conti­


nuación de un bloque try o de otro m anejador de excepción (bloque catch). Las
palabras clave try y catch. por definición, van seguidas de un bloque que encierra
el código relativo a cada una de ellas, razón por la cual es obligatorio utilizar lla­
ves: {}.
C A P ÍT U L O 11: EX CEPCION ES 4 0 5

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.

Para aclarar lo expuesto analicem os el siguiente ejem plo. Se trata de un m éto­


do para escribir en un fichero datos procedentes de una m atriz. ¿R ecuerda la apli­
cación Test que escribim os en el capítulo anterior que operaba sobre un objeto
C Banco? Pues, suponga ahora que querem os añadir al m enú que presentaba esta
aplicación, una opción m ás que perm ita escribir en un fichero en disco una lista
con los nom bres de los clientes del banco (en el capítulo siguiente aprenderem os a
trabajar con ficheros). Para ello, añadirem os a la clase Test el m étodo que se ex­
pone a continuación. D icho m étodo se ejecutará cuando se seleccione esa opción:

public static void e scrib írD a to síC B a n co banco. String fich)


1
P r i n t W r i t e r f c l i = n u l 1;
CCuenta c l i e n t e :
C L i s t a C l i e n t e s l i s t a = new C L i s t a C l i e n t e s ( b a n c o . 1 o n g i t u d ( ) ) :
try
I
for (in t i =0: i < b a n c o . 1o n g i t u d ( ) : i++)
I
c l i e n t e = b a n c o . e l i e n t e E n ( i );
1 ista.añadi r(cli e n t e . o b t e n e r N o m b r e (). i );
I
// A b r i r e l f i c h e r o p a r a e s c r i b i r . S e c r e a e l flujo f c 1i :
f e l i = new P r i n t W r i t e r ( n e w F i 1e W r i t e r ( f i c h ) ) ;
l i s t a . e s c r i b i r ( f c l i );
I
catch ( lOException e)
I
S y s t ern. out . p r i n t l n ( e . g e t M e s s a g e ( ) ) :

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:

1. El bloque tr y finalice de ejecutarse satisfactoriam ente.


2. Se lance una excepción IO E x c e p tio n .
3. Se lance una excepción 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 .

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.

Si hubiéram os incluido un m anejador para el caso 3, podríam os cerrar el fi­


chero en los bloques try y catch y prescindir del bloque finally, pero a costa de
duplicar código y hacer m enos legible el program a.

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

public F i 1e W r i t e r ( S t r i n g nombre_fichero) throws IO E xce p tio n


I
// C u e r p o d e l mét od o
I

La palabra reservada th ro w s perm ite a un m étodo declarar las lista de excep­


ciones (nom bres de las clases de excepción separados por com as) que puede lan­
zar. E sto tiene dos lecturas:

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.

2. E scribir un m étodo que lance una o m ás excepciones que no sean atrapadas


por el propio m étodo, sino por los m étodos que lo llam en; al fin y al cabo, lo
único q ue estam os haciendo cuando procedem os de esta form a es no antici­
pam os a las necesidades que pueda tener el usuario en cuanto al tratam iento
de la excepción se refiere.

Siguiendo con lo expuesto, si no deseáram os que el m étodo escribirD atos


atrapara las excepciones debidas a las anom alías que pudieran ocurrir dentro de
él, tendríam os que escribirlo así:

p u b l i c s t a t i c vo i d e s c r i b i r D a t o s í C Ba n c o banco, S t r i n g f i c h ) throws I OException


I
P r i ntWri t e r f c l i = n u i l ;
CCuenta c l i e n t e :
C L i s t a C l i e n t e s l i s t a = new C L i s t a C l i e n t e s ( b a n c o . 1o n g i t u d ( ) ) ;
try
I
for (in t i = 0: i < b a n co .1o n g i t u d ( ); i++)
1
c l i e n t e = b a n c o . e l i e n t e E n í i );
1i s t a . a ñ a d i r ( c l i e n te . o b t e n e r N o m b r e ! ). i ):
I
// A b r i r e l f i c h e r o p a r a e s c r i b i r . Se c r e a el flujo fc li;
f e 1 i = new P r i n t W r i t e r t n e w F i 1e W r i t e r ( f i c h ) ) ;
1 i s t a . e s c r i b i r ( f c l i );
1
f i nal ly
I
// C e r r a r e l f i c h e r o
i f ( f e l i ! = n u i l ) f c l i . e l o s e ( );
408 JA V A: C U R S O DE PROGRAM A CIÓN

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 static void m a in{String[] args)


I
// ...
c a s e 8: // e s c r i b i r
try
I
f l u j o S . p r i n t t " F i c h e r o : " ) ; nombr e = Leer.datoO;
e s c r i b i r D a t o s t b a n c o . nombre):
I
catch ( I O E x c e p t i o n e)
I
System.out.pri ntln(e.getMessaget));
I
break;
II...
I

En el caso de tener que redefinir el m étodo en una subclase, la declaración de


cuántas excepciones que puede lanzar puede ser inferior, nunca superior; incluso
puede no lanzar ninguna.

CREAR Y LANZAR EXCEPCIONES


En alguna ocasión puede que necesitem os crear nuestras propias excepciones, a
pesar de que en la biblioteca de clases de Java hay una gran cantidad de ellas que
podem os utilizar sin m ás. En cualquier caso, todos los tipos de excepción se co­
rresponden con una clase derivada de T h ro w a b ie , clase raíz d e la jerarq u ía de
clases de excepciones de Java. M ás aún, el paquete ja v a .Ia n g proporciona dos
subclases de T h ro w a b ie que agrupan las excepciones que se pueden lanzar, com o
consecuencia de los errores que pueden ocurrir en un program a, en dos clases:
E r r o r y Exceptio n. L os errores que ocurren en la m ayoría de los program as se
corresponden con excepciones de alguna de las subclases de E xcep tio n, razón por
la que esta clase será la superclase directa o indirecta de las nuevas clases de ex­
cepción que creem os, quedando la clase E r r o r reservada para el tratam iento de
los errores que se puedan producir en la m áquina virtual de Java.

En general, crearem os un nuevo tipo de excepción cuando queram os m anejar


un determ inado tipo de error no contem plado por las excepciones proporcionadas
por la biblioteca de Java. Por ejem plo, para crear un tipo de excepción EValor-
N oV ulido. con la intención de m anejar un error “valor no válido” , podem os dise­
ñar una clase com o la siguiente:
CA PÍTU LO I I : EX CEPCION ES 4 0 9

p u b lic c l a s s EV alorN oV alido extends Exception


I
p u b l i c E V a l o r N o V a l i do <) I I
p u b l i c E V a l o r N o V a l i d o ( S t r i n g mensaje)
I
super(mensaje);
I
// ...
1

Según se observa en este ejem plo, la superclase de la nueva clase de excep­


ción E V alorN oV alido es Exception. e im plem enta dos constructores: uno sin pa­
rám etros y otro con un parám etro de tipo S tr in g ; esto es lo m ás habitual. El
parám etro de tipo S t r in g es el m ensaje que devolverá el m étodo g e tM e ssa g e he­
redado de la clase T h ro w a b le a través de Exception. Para ello el constructor
E V alorN oV alido debe invocar al constructor de la superclase y pasar com o argu­
m ento dicha cadena, la cual será alm acenada com o un m iem bro de datos de la cla­
se T h ro w a b le .

La clase de excepción E V alorN oV alido relacionada con el error “ valor no vá­


lido” ya está creada. Lógicam ente, siem pre que se im plem ente una clase de ex­
cepción es porque durante el desarrollo de una clase, p o r ejem plo C M iC lase, se ha
observado que su código para determ inados valores durante la ejecución, puede
presentar una anom alía de la que los usuarios de esa clase deben ser inform ados
para que la puedan tratar.

¿Q ué aspecto tiene CM iC lase? Según lo expuesto, el código que im plem enta


esta clase ante determ inados valores produce un error. H abrá entonces que añadir
el código que chequee si se producen esos valores y en caso afirm ativo lanzar la
excepción program ada para este caso. Por 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...

Lanzar, una excepción equivale a crear un objeto de ese tipo de excepción. En


el ejem plo anterior se observa que la circunstancia que provoca el error es que el
410 JA V A : C U R SO D E PROGRAM A CIÓN

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.

public class CMiClase


I
// ...
public void m(int a) throws EValorNoValido
I
11...
I
II...
I

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.

public class CMiClase


l
/ / ...
public void m(int a)
i
11...
try
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 cero"):
I
catch ( E V a l o r N o V a l i d o e)
I
S y s t e m . o u t . p r i n t l n ( e . g e t M e s s a g e ( )):
1
11...
I
II

C om binar am bas form as (declarar la excepción y adem ás atraparla) no sirve


de nada, porque si un m étodo lanza una excepción y la atrapa, en el supuesto de
CA PÍTU LO 11: EX CEPCION ES 4 1 1

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:

public class Test


I
public static void m a in (S trin g [] args)
I
i n t x = 0:
C M i C l a s e o b j = new C M i C l a s e O :
try
I
obj .m(x);
)
catch ( EValorNoValido e)
I
S y ste m .o ut.p r i n t l n ( e .g e tM e ssa g e ()):
1
System .out.println("C ontinúa la ejecución");
)
I

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.

C uando un m étodo utilizando th ro w lanza una excepción, crea un objeto de la


clase de excepción especificada, que interrum pe el flujo de ejecución del progra­
m a y vuelve por la pila de llam adas hasta encontrar uno que sepa atrapar la e x ­
cepción (que contenga un bloque catch con un argum ento de la clase de la
excepción o de alguna de sus superclases). La ejecución del program a se transfie­
re entonces, directam ente al m étodo que atrapó la excepción para que ejecute el
m anejador. Si el m anejador, una vez ejecutado, perm ite que la aplicación conti­
núe, la ejecución se transfiere a la prim era línea ejecutable que haya a continua­
ción del últim o m anejador del bloque try. Según esto, cuando se ejecute el
m étodo m a in del ejem plo anterior se obtendrá el siguiente resultado:

Error: valor cero


Continúa la ejecución
412 JA VA : C U R SO DE PRO G R A M A CIÓ N

Si un m étodo lanza una excepción y en la vuelta p o r la pila de llam adas no se


encuentra uno que la atrape, el program a finalizará. En cam bio, si se encuentra un
m anejador para esa excepción, se ejecuta. E n el supuesto de que en la pila de lla­
m adas quedaran otros m étodos que pudieran atraparla, n o serán tenidos en cuenta;
esto es, sólo se tiene en cuenta el m anejador del m étodo por el que haya pasado el
flujo de control m ás recientem ente.

A su vez, si el m étodo contiene una lista de m anejadores sólo se ejecutará el


correspondiente a la excepción lanzada; esto es, el com portam iento es el m ismo
que el de una sentencia sw itch, pero con la diferencia de que los case necesitan
sentencias b re a k y los catch no.

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.

U na excepción se considera m anejada desde el m om ento en que se entra en su


m anejador, así que cualquier otra excepción lanzada desde el cuerpo de éste, de­
berá ser atrapada por algún otro m étodo cuya llam ada se encuentre en la pila de
llam adas; si la excepción no es atrapada, el program a finaliza. Esto explica por
qué el siguiente código no provoca un bucle infinito:

public void otroMétodot) th rows EValorNoValido


I
// ...
try
I
// ...
I
c a t c h ( E V a l o r N o V a l i d o e)

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

CUÁNDO UTILIZAR EXCEPCIONES Y CUÁNDO NO


N o todas los program as necesitan responder lanzando una excepción a cualquier
situación anóm ala que se produzca. P or ejem plo, si partiendo de unos datos de
entrada estam os haciendo una serie de cálculos m ás o m enos com plejos con la
única finalidad de observar unos resultados, quizás la respuesta m ás adecuada a
un erro r sea interrum pir sin m ás el program a, no antes de haber lanzado un m en­
saje apropiado y haber liberado los recursos adquiridos que aún no hayan sido li­
berados. O tro ejem plo, podem os utilizar la clase de excepción A r r a y ln d e x O u t o f-
B o u n d s para m anejar el error que se produce cuando se rebasan los lím ites de una
m atriz, pero es m ás fácil utilizar el m iem bro le n gth de la m atriz para prevenir que
esto no suceda.

En cam bio, si estam os construyendo una biblioteca estam os obligados a evitar


todos los errores que se puedan producir cuando su código sea ejecutado por
cualquier program a que la utilice.

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.

La clase de excepción O u t O f M e m o r y E r r o r pertenece a la jerarq u ía cuya


clase raíz es E r r o r . A nteriorm ente se expuso que la jerarq u ía derivada de la clase
E r r o r estaba reservada p ara el tratam iento de los errores que se puedan producir
en la m áquina virtual de Java. El error de falta de m em oria para asignación es un
erro r típico q ue puede surgir en un program a que necesite reservar repetidas veces
bloques de m em oria de un tam año considerable.

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.

public class CListaTfnos


(
p r i v a t e C P e r s o n a [ ] 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 de e l e m e n t o s de l a m a t r i z

private CPersonaü a s ig n a rM e m o ria íin t nElementos)


I
try
I
return new C P e r s o n a f n E l e m e n t o s ] ;
1
catch ( O u t O f M e m o r y E r r o r e)
I
S y s t e m . o u t .p r in t ln ( e . g e t M e s s a g e ( ));
re tu rn 1i s t a T e l é f o n o s ;
I
I

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

private void unElementoMás(CPersona[] listaActual)


I
nEle mentos = 1i s t a A c t u a l .1e n g t h ;
1 is t a T e lé f o n o s = a signarM em oria(nElem en tos + 1):
// C o p i a r l a l i s t a a c t u a l
f o r ( i n t i = 0: i < n E l e m e n t o s : i + + )
1 i s t a T e l é f o n o s [ i ] = 1 i s t a A c t u a l [ i 3:
nElementos++:

private void unElementoMenos(CPersona[] listaActual)


I
i f (1 i s t a A c t u a l . 1 e n g t h = = 0 ) r e t u r n ;
i n t k = 0;
nEle mentos = 1 i s t a A c t u a l .1ength ;
1ist a T e lé f o n o s = as1gnarMemoria(nElementos - 1);
// C o p i a r l a l i s t a a c t u a l
f o r ( i n t i = 0; i < n E l e m e n t o s : i + + )
C A PÍTU LO I I: EX CEPCION ES 4 1 5

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.

Al principio de este capítulo explicam os el m anejador de excepciones que in­


cluye el m étodo datos. C om o ejercicio se trata ahora de explicar y m odificar los
m anejadores de excepciones incluidos en el resto de los m étodos.

El m étodo d atoShort fue im plem entado de la form a siguiente:

public static short datoShortO


I
try
I
return S h o r t . p a r s e S h o r t ( d a t o ( ));
I
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 S h o r t . M I N _ V A L U E ; // v a l o r más p e q u e ñ o
I
416 JA VA : C U R S O DE PROGRAM A CIÓN

Este m étodo devuelve el valor retornado a su vez por el m étodo p a rse S h o rt


de la clase Sh o rt, resultado de co n v eitir la cadena de caracteres devuelta por dato.
Pero ¿qué ocurre si la cadena de caracteres devuelta por dato no se corresponde
con un sh o r t? Pues que al ejecutarse el m étodo p a rse S h o rt, Java lanza una ex­
cepción N u m b e rF o rm a tE x c e p tio n que es atrapada por el m anejador, que de­
vuelve el valor S h o r t . M IN _ V A L U E .

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

public static short datoShortO


!
try
I
S t r in g sdato = d a t o O ;
i f (sdato = nu il)
I
S y s t e m . o u t . p r i n t l n t );
r e t u r n S h o r t .MIN_VA L U E ;
1
el se
return Short.parseShort(sdato):
I
c a t c h ( N u m b e r F o r m a t E x c e p t i on e )
(
System .out.p rintí"Ese dato no es v á l i d o . Teclee otro: "):
r e t u r n d a t o S h o r t í );

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:

i n t eof = Inte ger.M IN _ V A LU E , i = 0;


i n t [ ] a = new i n t [ 1 0 0 ]:

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

System .out.p rin t("D a to int: "):

A nálogam ente podríam os escribir un m anejador para el m étodo dato,Float:

public static float datoFloatí)


I
try
I
S t r i n g s d a t o = d a t o ( ):
i f ( sdato = n u il)
I
S y s t e m . o u t . p r i n t l n í );
r e t u r n F l o a t . N a N : // No e s un Número: valor float.
I
el se
I
F l o a t f = new F l o a t ( s d a t o ) :
r e t u r n f . f 1o a t V a l u e ( ):

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

Si tenem os en cuenta que F loat(dato()) lanza la excepción N u llP o in terE x -


c e p tio n cuando el m étodo d ato devuelve nuil, el m étodo anterior podría escribir­
se tam bién así:

public static float datoFloatí)


I
try
I
F l o a t f = new F l o a t í d a t o í ) ) :
r e t u r n f . f 1o a t V a l u e í );
I
catchíNumberFormatException e)
I
System .out.p rintí"Ese dato no es v á l i d o . Teclee otro: ");
r e t u r n d a t o F l o a t í ):
I
c a t c h í N u l l P o i n t e r E x c e p t i o n e)
I
return Float.NaN; // No e s un Número: valor float.
418 JA VA : C U R SO DE PROGRAM A CIÓN

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:

boolean eof = true;


f l o a t l ] a = new f 1o a t [ 1 0 0 ] :
i n t i = 0:

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

L a variable e o f se ha utilizado sim plem ente por m otivos didácticos. Q uiere


esto decir que la sentencia w hile podría escribirse tam bién así:

while (i < 100 && ¡ F l o a t . i s N a N ( a [ i ] = Leer.datoFloat()))


(
i++:
System .out.print("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 clase E Saldolnsuficiente tendrá dos atributos, uno de la clase C Cuenta para


hacer referencia a la cuenta que causó el problem a, y otro de tipo d o u b le para al­
m acenar la cantidad solicitada. A sim ism o tendrá un constructor y el m étodo m en­
saje. El constructor E Saldolnsuficiente tendrá dos parám etros que harán referen­
cia a la cuenta causante del problem a y a la cantidad solicitada. El m étodo m en­
saje no tiene argum entos, generará un m ensaje de error basado en la inform ación
alm acenada en los atributos y devolverá un objeto S tr in g con ese m ensaje.

C uando haya finalizado pruebe la jerarq u ía de la clase C C uenta ju n to con la clase


C B anco que tam bién im plem entam os en ese capítulo.
CA PÍTU LO 12
© FJ.CebalIos/RA-MA

TRABAJAR CON FICHEROS


T odos los program as realizados hasta ahora obtenían los datos necesarios para su
ejecución de la entrada estándar y visualizaban los resultados en la salida están­
dar. Por otra parte, una aplicación podrá retener los datos que m anipula en su es­
pacio de m em oria, sólo m ientras esté en ejecución; es decir, cualquier dato
introducido se perderá cuando la aplicación finalice.

Por ejem plo, si hem os realizado un program a con la intención de construir


una agenda, lo ejecutam os y alm acenam os los datos nom bre, apellidos y teléfono
de cada uno de los com ponentes de la agenda en una m atriz, los datos estarán dis­
ponibles m ientras el program a esté en ejecución. Si finalizam os la ejecución del
program a y lo ejecutam os de nuevo, tendrem os que volver a introducir de nuevo
todos los datos.

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

D esde el punto de vista inform ático, un fichero o archivo es una colección de


inform ación que alm acenam os en un soporte m agnético para poderla m anipular
en cualquier m om ento. E sta inform ación se alm acena com o un conjunto de regis­
tros, conteniendo todos ellos, generalm ente, los m ism os cam pos. C ada cam po al­
m acena un dato de un tipo predefinido o de un tipo definido por el usuario. El
registro m ás sim ple estaría form ado por un carácter.

P o r ejem plo, si quisiéram os alm acenar en un fichero los datos relativos a la


agenda de teléfonos a la que nos hem os referido anteriorm ente, podríam os diseñar
cada registro con los cam pos nom bre, dirección y teléfono. Según esto y desde un
punto de vista gráfico, puede im aginarse la estructura del fichero así:

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

En program ación orientada a objetos, hablarem os de objetos m ás que de re­


gistros, y de sus atributos m ás que de cam pos.

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

VISION GENERAL DE LOS FLUJOS DE E/S


La com unicación entre el program a y el origen o el destino de cierta inform ación,
se realiza m ediante un flu jo de inform ación (en inglés stream ) que no es más que
un objeto que hace de interm ediario entre el program a, y el origen o el destino de
la inform ación. Esto es, el program a leerá o escribirá en el flu jo sin im portarle
desde dónde viene la inform ación o a dónde va y tam poco im porta el tipo de los
datos q ue se leen o escriben. Este nivel de abstracción hace que el program a no
tenga que saber nada ni del dispositivo ni del tipo de inform ación, lo que se tradu­
ce en una facilidad m ás a la hora de escribir program as.

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

El paquete ja v a .io de la biblioteca estándar de Java, contiene una colección


de clases que soportan estos algoritm os para leer y escribir. Estas clases se divi­
den en d o s grupos distintos, 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 (8 bits) y el de
la derecha con datos de tipo c h a r (16 bits). A m bos grupos presentan clases análo­
gas que tienen interfaces casi idénticas, por lo que se utilizan de la m ism a m anera.
422 JA VA : C U R S O DE PRO G R A M A CIÓ N

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 .

Sin em bargo, a m enudo es m ás conveniente agrupar las clases según su fina­


lidad en vez de por el tipo de datos que leen o escriben (caracteres o bytes). Desde
este punto de vista distinguim os flujos que sim plem ente perm iten leer y escribir
datos y flujos que, adem ás, procesan la inform ación leída o escrita.

Flujos que no procesan los datos de E/S


La tabla siguiente lista las subclases que perm iten definir flujos para leer o escri­
bir inform ación en un m edio sin realizar ningún proceso añadido:

M edio Flujo de caracteres F lujo de bytes

M em oria C harA rrayR eader B y te A rray Inpu tS tream


C harA rrayW riter B yteA rrayO utputStream

StringR eader StringB ufferlnputStream


StringW riter

Fichero FileR eader FilelnputStream


FileW riter FileO utputStream

Tubería PipedR eader PipedlnputS tream


PipedW riter PipedO utputStream
C A PÍTU LO 12: TRA BA JA R C O N R C H E R O S 4 2 3

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.

El siguiente ejem plo m uestra cóm o utilizar las clases C h a r A r r a y R e a d e r y


C h a r A r r a y W r it e r . El resto de las clases expuestas en la tabla anterior se utilizan
de form a análoga.

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

Q ué hace este program a:


1. A lm acena datos en una m atriz m i.
2. A bre un flujo de entrada desde la m atriz m i . E ntonces, el program a puede
leer datos de este flujo de form a sim ilar a com o lo hace del flujo estándar de
entrada.
3. A bre un flujo de salida hacia una m atriz tem poral. C om o el tam año de la m a­
triz no ha sido especificado, éste se ajustará a la cantidad de inform ación que
se envíe al flujo. D e esta form a, el program a puede escribir datos en este flujo
de form a sim ilar a com o lo hace en el flujo estándar de salida.
4. Lee datos del flujo abierto desde m i , flujoE, y los escribe en el flujo de salida,
flu jo S .
5. C opia los datos enviados a flu jo S en una m atriz m 2 y la m uestra.

Flujos que procesan los datos de E/S


La tabla siguiente lista las subclases que perm iten definir flujos para leer o escri­
bir inform ación en un m edio, adem ás de realizar alguna operación com o añadir un
buffer, un filtro, realizar una conversión, etc.:

Operación Flujo de caracteres Flujo d e bytes

E stablecer un B ufferedR eader B ufferedlnputStream


buffer B ufferedW riter B ufferedO utputStream

E stablecer un FilterReader FilterlnputStream


filtro FilterW riter FilterO utputStream
C onversión InputStream R cader
(bytes - cars.) O utputStream W riter

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

M irar anticipa­ PushbackR eader PushbacklnputStream


dam ente

Escribir PrintW riter PrintStream


CA PÍTU LO 12: TRA B A JA R CON FIC H ER O S 4 2 5

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.

A lguno de los m étodos que proporciona la clase P u s h b a c k R e a d e r son:

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.

Para po d er im plem entar la capacidad de m irar cuál es el siguiente carácter en


la entrada, el flujo debe de ser de la clase P u sh b a c k R e a d e r. El constructor de
esta clase requiere un argum ento que haga referencia a un objeto, origen de los
datos a leer, de la clase R e a d e r o de alguna de sus subclases y opcionalm ente
acepta un segundo argum ento que especifica el tam año del buffer para alm acenar
los caracteres devueltos por el m étodo u n re a d de P u sh b a c k R e a d e r, que por
om isión es uno. Por lo tanto, la clase L eer puede ser ahora así:
426 JA VA : CU R SO D E PROGRAM A CIÓN

import j a v a . i o . * :

public class Leer


1
// O 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 : flujoE
p r i v a t e s t a t i c InputStreamReader i s r -
new l n P u t S t r e a m R e a d e r ( S y s t e m . i n ) :
private static P u s h b a c k R e a d e r f l u j o E - new P u s h b a c k R e a d e r ( i s r ) :

public static void lim piar!)


I
// lim piar flujoE
I

public static char m irar!)


I
// r e t o r n a r e l primer cará cter d is p o n ib le sin extraerlo
I

public static char c a rá c te r !)


I
II d e v o l v e r e l siguiente c a r á c t e r de l a entrada
I

public static String dato!)


I
// d e v o l v e r un S t r i n g que almac en e el dato tecleado
I

II...
I

A hora, la clase L eer define un m iem bro flu jo E de la clase P u sh b a c k R e a d e r


que se corresponde con el flujo abierto desde del origen de los caracteres. C om o
el origen real de los datos va a ser el teclado (dispositivo vinculado con System .in
que proporciona bytes), es preciso conectar am bos flujos por otro que convierta
los bytes procedentes del teclado a los caracteres que espera flu jo E . De esto se en­
carga isr:

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

La clase In p u t S t r e a m R e a d e r establece un puente para pasar flujos de bytes


a flujos de caracteres.

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.

public static void lim piarO


1
in t car - 0:
try
I
while ( f l u j o E . r e a d y ( )) f l u j o E . r e a d ( ); // l i m p i a r flujoE
1
c a t c h t I O E x c e p t i o n e)
I
S y s t e m . e r r .p r in t ín ( "E r r o r : ” + e.ge tM essage());

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.

public static char m irart)


I
int c a r = 0;

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

El m étodo carácter devolverá el siguiente carácter disponible en el flujo de


entrada. Si no hubiera ningún carácter disponible, quedará a la espera de que se
introduzca uno.

public static char c a rá c te r O


I
int car - 0:

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.

public static String dato()


I
StringBuffer s d a t o = new S t r i n g B u f f e r ( ):
i n t c a r = 0:

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

Es im portante tom ar buena nota de cóm o la salida de un flujo se puede c o ­


nectar a la entrada de otro, lo que perm ite disponer de nuevas capacidades. ¿Q ué
flujos pueden utilizarse de esta form a? T odos aquellos cuyo constructor tenga un
parám etro que haga referencia a otro flujo.

A continuación se m uestra un ejem plo que utiliza la nueva versión de la clase


L eer que acabam os de im plem entar. D icho ejem plo se lim ita a leer un valor de ti­
po d o u b le si el p rim er carácter de la entrada efectuada p o r el usuario que ejecuta
la aplicación es un dígito o el signo m enos; en otro caso lee un S trin g .

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.

U na vez descrita la jerarquía de clases que Java proporciona para realizar la


E/S, es el m om ento de plantearnos la utilización de esta jerarq u ía de clases.

ABRIENDO FICHEROS PARA ACCESO SECUENCIAL


El tipo de acceso m ás sim ple a un fichero de datos es el secuencial. Un fichero
abierto para acceso secuencial es un fichero que puede alm acenar registros de
cualquier longitud, incluso de un sólo byte. C uando la inform ación se escribe re­
gistro a registro, éstos son colocados uno a continuación de otro, y cuando se lee,
se em pieza por el prim er registro y se continúa al siguiente hasta alcanzar el final.
430 JA VA : CU R SO DE PRO G R A M A CIÓ 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

Un flujo de la clase F ile O u tp u tS tr e a m perm ite escribir bytes en un fichero.


A dem ás de los m étodos que esta clase hereda de O u tp u tS tre a m , la clase propor­
ciona los constructores siguientes:

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 p rim er constructor abre un flujo de salida hacia el fichero especificado por


nom bre, m ientras que el segundo hace lo m ism o, pero con la posibilidad de añadir
datos a un fichero existente (a ñ a d ir = true); el tercero lo hace a partir de un o b ­
je to File. Un ejem plo aclarará los conceptos expuestos.

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.

La aplicación definida p o r la clase C E scribirB ytes m ostrada a continuación,


realiza lo siguiente:

1. D efine una m atriz buffer de 81 bytes.

2. Lee una línea de texto desde el teclado y la alm acena en buffer.


3. D efine un flujo f s hacia un fichero denom inado texto.txt. T enga presente que
si el fichero existe, se borrará en el m om ento de definir el flujo que perm ite su
acceso, excepto si especifica com o segundo parám etro true.

F i l e O u t p u t S t r e a m f s = new F i l e O u t p u t S t r e a m t " t e x t o . t x t " ):


CA PÍTU LO 12: T R A B A JA R C O N FIC H ER O S 4 3 1

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

El program a com pleto se m uestra a continuación:

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.

Si lo q ue desea es añadir inform ación al fichero, cree el flujo hacia el m ism o


com o se indica a continuación:

f s = new F i 1e O u t p u t S t r e a m t " t e x t o . t x t " , t r u e ) :

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

En la biblioteca de Java puede observar que el m étodo cióse de F ile O u tp u tS -


tre am declara que puede lanzar una excepción de la clase IO E x c e p tio n , razón
p o r la que nuestro código debe atraparla. Q uizás haya pensado invocar al m étodo
cióse después de haber ejecutado el m étodo w rite dentro del prim er bloque try:

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 prim er constructor abre un flujo de entrada desde el fichero especificado


por nom bre, m ientras que el segundo lo hace a partir de un objeto File. Un ejem ­
plo aclarará los conceptos expuestos.

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.

La aplicación definida por la clase C LeerB ytes m ostrada a continuación, rea­


liza lo siguiente:

1. D efine una m atriz buffer de 81 bytes.

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.

FilelnputStream fe - new F i l e l n p u t S t r e a m t " t e x t o . t x t " ):

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.

nbytes - fe. re a d tb u ffe r. 0. 81):

4. C rea un objeto S t r in g con los datos leídos.

El program a com pleto se m uestra a continuación:

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

public static v o i d main !String[] args)


I
F i l e I n p u t S t r e a m fe = n u i l ;
b y t e [ ] b u f f e r = new b y t e [ 8 1 ] ;
int nbytes:

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 )

El p rim er constructor crea un objeto F ile a partir de un nom bre de fichero m ás


su ruta de acceso (relativa o absoluta). Por ejem plo, el siguiente código crea un
CA PÍTU LO 12: T R A B A JA R CON FIC H ER O S 4 3 5

objeto File a p artir de la ruta relativa proyecto2\texto.txt. O bserve que el separa­


d o r de directorios viene especificado p o r la secuencia de escape ‘\V. E ste separa­
d o r en un sistem a U N IX es 7 \

File f i c h e r o = new F i 1 e ( " p r o y e c t o W t e x t o . t x t ” );

S y s t e m . o u t . p r i n t l n ( " N o m b r e del f i c h e r o : " + f i c h e r o . g e t N a me í ) ) ;


S y s t e m . o u t . p r i n t l n ( “D i r e c t o r i o padre: " + f i c h e r o . g e t P a r e n t ( ));
S y s t e m . o u t . p r i n t l n ( “Ruta r e l a t i v a : " + fic h e ro .g e t P a t h í));
Sy ste m .o u t.p rin tln ("R u ta absoluta: ”+
fic h e ro .g e t A b s o lu t e P a t h C ));

Los resultados que se visualizan cuando se ejecute el código anterior serían


análogos a los siguientes:

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

File f i c h e r o = new F i 1e ( " p r o y e c t o " . " t e x t o . t x t " ) :

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

File d i r = new F i 1e ( " p r o y e c t o " );


File f i c h e r o = new F i l e í d i r , “ t e x t o . t x t " ) :

La tabla siguiente resum e los m étodos de la clase File:

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.

P ara m ás detalles sobre los m étodos anteriores recurra a la ayuda proporcio­


nada con el JDK. U tilizando las capacidades de la clase F ile podem os m odificar
la aplicación C LeerB ytes para que solicite un nom bre de un fichero existente:

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

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 - S y s t e m . in . r e 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): II menos C R+ L F
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 ) :
I
w hile ( ! f i c h e ro .e x is t s t )):
C A PÍTU LO 12: TRABAJAR CON FICHEROS 4 3 7

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

A nálogam ente, utilizando las capacidades de la clase File podem os m odificar


la aplicación C E scribirB ytes para que verifique si existe el fichero en el que se va
a escribir los datos leídos desde el teclado:

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

char resp = ’s ';


i f ( f i c h e r o . e x i s t s t ))
I
S y s t e m . o u t . p r i n t ( " E l f i c h e r o e x i s t e ¿ d e s ea s o b r e e s c r i b i r l o ? ( s / n ) ” );
resp - (c h a r )S y s te m .in .re a d O ;
II S a l t a r l o s b y t e s no l e í d o s d e l f l u j o i n
S y s te m , i n . s k i p( Sy s te m . i n . a v a i l a b l e 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

Un flujo de la clase F ile W r it e r perm ite escribir caracteres (c h a r) en un fichero.


A dem ás de los m étodos que esta clase hereda de W r ite r, la clase proporciona los
constructores siguientes:

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)

El prim er constructor abre un flujo de salida hacia el fichero especificado por


nom bre, m ientras que el segundo hace lo m ism o, pero con la posibilidad de añadir
datos a un fichero existente (a ñ a d ir = true); el tercero lo hace a partir de un ob­
je to File.

El siguiente ejem plo es la versión de la aplicación Java C E scribirB ytes reali­


zada anteriorm ente, adaptada para escribir caracteres en lugar de bytes. O bserve
que las variaciones son m ínim as:

import ja v a .i o . * ;

public class CEscribirCars


I
public static v o i d main (String[] args)

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

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


n b y t e s = S y s t e m . i n . r e a d t 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 ( b u f f e r . 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 ) ;

char resp = ' s 1;


i f ( f i c h e r o . e x i s t s ( ))
I
S y s t e m . o u t . p r i n t ( " E l f i c h e r o e x i s t e ¿desea s o b r e e s c r i b i r l o ? ( s /n) " ) ;
r e s p = ( c h a r ) S y s t e m . i n . r e a d t );
// S a l t a r l o s b y t e s n o l e í d o s d e l f l u j o i n
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 ( ) ) ;
CA PÍTU LO 12: TR A B A JA R C O N FICHEROS 4 3 9

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 )

El prim er constructor abre un flujo de entrada desde el fichero especificado


por nom bre, m ientras que el segundo lo hace a partir de un objeto File.

El siguiente ejem plo es la versión de la aplicación Java C LeerB ytes realizada


anteriorm ente, adaptada para leer caracteres en lugar de bytes. O bserve que las
variaciones son mínim as:

import j a v a . i o . * ;
440 JA V A: C U R S O DF. PROGRAM A CIÓN

public class CLeerCars


1
public static void ma i n ( S t r i n g ü args)
1
b y t e [ ] n o m F i c h = new b y t e [ 8 1 ] :
S t r i n g nombreFichero = n u i l ;
F ile fichero = nuil ;
int nbytes, ncars:
FileReader fe = n u il;
c h a r [ ] b u f f e r - new c h a r [ 8 1 ] :

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

• Se crea un flujo asociado con un origen o destino de los datos.


• Se aso cia un filtro con el flujo anterior.
• Finalm ente, el program a leerá o escribirá datos a través de ese filtro.

DataOutputStream

Un flujo de la clase D a ta O u tp u tS tr e a m , derivada indirectam ente de O u tp u tS -


tream , perm ite a una aplicación escribir en un flujo de salida subordinado, datos
de cualquier tipo prim itivo.

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:

F i 1e 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 C ' d a t o s . d a t " ) :


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 t f o s ) :

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 ;

Los m étodos m ás utilizados de esta clase se resum en en la tabla siguiente:

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

U n flujo de la clase D a ta ln p u tS tr e a m , derivada indirectam ente de In p u tS tre a m ,


perm ite a una aplicación leer de un flujo de entrada subordinado, datos de cual­
quier tipo prim itivo escritos por un flujo de la clase D a ta O u tp u tS tr e a m .

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 ;

Los m étodos m ás utilizados de esta clase se resum en en la tabla siguiente:

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.

Un ejemplo de acceso secuencial


D espués de la teoría expuesta hasta ahora acerca del trabajo con ficheros, habrá
observado que la m etodología de trabajo se repite. Es decir, para escribir datos en
un fichero:

• D efinim os un flujo hacia el fichero en el que deseam os escribir datos.


• L eem os los datos del dispositivo de entrada o de otro fichero y los escribim os
en nuestro fichero. E ste proceso se hace norm alm ente registro a registro. Para
ello, utilizarem os los m étodos proporcionados p o r la interfaz del flujo.
• C erram os el flujo.
444 JA V A: C U R SO DU PROGRAM A CIÓN

Para leer datos de un fichero existente:

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

E sto pone de m anifiesto que un fichero no es m ás que un m edio perm anente


de alm acenam iento de datos, dejando esos dalos disponibles para cualquier pro­
gram a que necesite m anipularlos. Lógicam ente, los datos serán recuperados del
fichero con el m ism o form ato con el que fueron escritos, de lo contrario los re­
sultados serán inesperados. Es decir, si en el ejercicio siguiente los datos son
guardados en el orden: una cadena, otra cadena y un long, tendrán que ser recupe­
rados en este orden y con este m ism o form ato. S ería un erro r recuperar prim ero
un lo n g después una cadena y finalm ente la otra cadena, o recuperar prim ero una
cadena, después un float y finalm ente la otra cadena; etc.

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

El m étodo m a in realiza las tareas siguientes:

• 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

• Si no existe, o bien si existe y se desea sobreescribir, invoca al m étodo crear-


F ichero pasando com o argum ento el objeto F ile creado.

import j a v a . i o . * ;

// Se u t i l i z a también la clase Leer m o d if ic a d a en e s t e c a p i t u l 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 ) :

f 1u j o S . p r i n t í " ¿ d e s e a e s c r i b i r otro registro? (s/n) ");


r e s p = L e e r . c a r á c t e r í );
L e e r . 1 i m p i a r ( );
1
w h i 1e ( r e s p — 's O :
1
f i n a l 1y
1
// C e r r a r el flujo
446 JA V A : C U R SO DE PRO G R A M A CIÓ N

if (dos != n u i l ) dos.closet);

public static void m a in (S trin g [] args)


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
S t r i n g nombreFichero = n u i l ; II no mb r e d e l f i c h e r o
F i l e f i c h e r o = n u i l ; // 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

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

Para leer el fichero creado p o r la aplicación anterior, vam os a escribir otra b a ­


sada en la clase M ostrarListaTfhos. E sta clase define dos m étodos: m ostrarFiche-
ro y m ain.

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:

• C rea un objeto F ile para identificar al fichero.


• Si el fich ero especificado existe, crea u n flujo desde el m ism o que perm ite
leer datos de tipos prim itivos utilizando un buffer.
C A P ÍT U L O 12: T R A B A JA R CON FIC H ER O S 4 4 7

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

• V erifica si se pasó un argum ento cuando se ejecutó la aplicación con el nom ­


bre del fichero cuyo contenido se desea visualizar.
• Si no se pasó un argum ento, la aplicación m ostrará un m ensaje indicando la
sintaxis que se debe de em plear p ara ejecutar la m ism a y finalizará. En otro
caso, invoca al m étodo m ostrarF ichero pasando com o argum ento argsjO].

import j a v a . i o . * ;

public class M ostrarListaífnos


I
public s t a t ic void m ostrarFich ero(String 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
D a t a l n p u t S t r e a m d i s = n u i l : / / e n t r a d a de d a t o s d e s d e e l f i c h e r o
F ile fichero = n u il: // o b j e t o q u e i d e n t i f i c a el f i c h e r 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 í );

// M o s t r a r l o s d a t o s n o mbr e , dirección y teléfono


flujoS.println(nom bre);
f l u j o S . p r i n t í n ( d i r e c e i ó n );
flujoS.println< teléfono):
f l u j o S . p r i n t l n ( );
I
w hile (true);
I
el se
flu jo S.p rintín ("El fichero no e x i s t e ” );
I
catch(EOFException e)
(
flu joS.p rintín ("Fin del listado");
)
finally
(
// C e r r a r el f l u j o
i f ( d i s ! = n u l 1) d i s . e l o s e ( ):

public static void m a in (S trin g [] args)


I
if ( a r g s . l e n g t h ! = 1)
S y ste m .e rr.p r i n t l n ( " S i n t a x i s : java M o s tra rL ista T fn o s “ +
"Ofichero fuente>"):
el se
[
try
{
m o s t r a r F i c h e r o í a r g s [ 0 ] );
I
catch(IOException e)
I
System .out.p rintlní"Error: " + e .g e tM e ssa g e ()):
1
I
1
i

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

m os aisladam ente; m ás bien se corresponderá con los atributos de un objeto, por


ejem plo de la clase C P ersona, lo que nos conducirá a escribir y leer objetos a y
desde un fichero.

N orm alm ente la operación de enviar u na serie de objetos a un fichero en disco


para hacerlos persistentes recibe el nom bre de seriación, y la operación de leer o
recuperar su estado del fichero para reconstruirlos en m em oria recibe el nombre
de deseriación. Para realizar estas operaciones de una form a autom ática, el paque­
te ja v a .io proporciona las clases O b je c tO u tp u tS tr e a m y O b je c tln p u tS tre a m .
A m bas clases dan lugar a flujos que procesan sus datos; en este caso, se trata de
convertir el estado de un objeto (los atributos excepto las variables estáticas), in­
cluyendo la clase del objeto y el prototipo de la m ism a, en una secuencia de bytes
y viceversa. Por esta razón los flujos O b je c tO u tp u tS tr e a m y O b je c tln p u t­
S tre a m deben ser construidos sobre otros flujos que canalicen esos bytes a y des­
de el fichero. El esquem a gráfico que responde a este proceso es el siguiente:

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

public class CPersona implements S e r i a l i z a b l e


I
// C u e r p o de l a el a s e
450 JA VA : C U R S O DE PRO G R A M A CIÓ N

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.

Escribir objetos en un fichero


Un flujo de la clase O b je c tO u tp u tS tr e a m perm ite enviar datos de tipos prim iti­
vos y objetos hacia un flujo O u tp u tS tre a m o derivado; concretam ente, cuando se
trate de alm acenarlos en un fichero, utilizarem os un flujo F ile O u tp u tS tre a m .
Posteriorm ente, esos objetos podrán ser reconstruidos a través de un flujo O b ject-
In p u tStre a m .

Para escribir un objeto en un flujo O b je c tO u tp u tS tr e a m utilizarem os el


m étodo w rite O bje ct. L os objetos pueden incluir Strings y m atrices, y el alm ace­
nam iento de los m ism os puede com binarse con datos de tipos prim itivos, y a que
esta clase im plem enta la interfaz D a ta O u tp u t. E ste m étodo lanzará la excepción
N o tS e ria liz a b le E x c e p tio n si se intenta escribir un objeto de una clase que no im ­
plem enta la interfaz Serializable.

P o r ejem plo, el siguiente código construye un O b je ctO u tp u tStre am sobre un


F ile O u tp u tS tre a m , y lo utiliza para alm acenar un S t r in g y un objeto C Persona
en un fichero denom inado datos:

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 a t o s " ):


O b j e c t O u t p u t S t r e a m o o s = new O b j e c t O u t p u t S t r e a m ( f o s );

oos.writeUTF( " Fichero da to s"):


o o s . w r i t e O b j e c t ( new C P e r s o n a t n o m b r e . dirección, teléfono)):
o o s . e l o s e ( ):

C om o ejercicio, vam os a m odificar la aplicación C rearListaTfnos anterior pa­


ra que perm ita alm acenar objetos C Persona en un fichero (la clase C Persona fue
im plem entada en el capítulo 9 al hablar de m atrices de objetos):

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 '):
/ / ...

Leer objetos desde un fichero


Un flujo de la clase O b je c tln p u tS tre a m perm ite recuperar datos de tipos prim iti­
vos y objetos desde un flujo In p u t S t r e a m o derivado; concretam ente, cuando se
trate de datos de tipos prim itivos y objetos alm acenados en un fichero, utilizare­
m os un flujo F ile ln p u tS tre a m . L a clase O b je c tln p u tS tre a m im plem enta la in­
terfaz D a t a ln p u t para perm itir leer tam bién datos de tipos prim itivos.

Para leer un objeto desde un flujo O b je c tln p u tS tre a m utilizarem os el m éto­


do read O bject. Si se alm acenaron objetos y datos de tipos prim itivos, deben ser
recuperados en el m ism o orden.

P o r ejem plo, el siguiente código construye un O b je c tln p u tS tre a m sobre un


F ile ln p u tS tre a m , y lo utiliza para recuperar un S t r in g y un objeto C Persona de
un fichero denom inado datos:

F i l e l n p u t S t r e a m f o s = new F i 1e I n p u t S t r e a m í " d a t o s " ) ;


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 I n p u t S t r e a m í f o 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

C om o ejercicio, vam os a m odificar la aplicación M ostrarL istaT fnos anterior


para que perm ita recuperar objetos C Persona desde un fichero (la clase C Persona
fue im plem entada en el capítulo 9 al hablar de m atrices de objetos):

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 ;

public static void m a in (S trin g [] args)


I
II ...

Seriar objetos que referencian a objetos


C uando en un fichero se escribe un objeto que hace referencia a otros objetos,
entonces todos los objetos accesibles desde el prim ero deben ser escritos en el
m ism o proceso para m antener así la relación existente entre todos ellos. Este pro­
ceso es llevado a cabo autom áticam ente por el m étodo w rite O b jcct, que escribe
el objeto especificado, recorriendo sus referencias a otros objetos recursivam ente,
escribiendo así todos ellos.

A nálogam ente, si el objeto recuperado del flujo por el m étodo re a d O b je c t


hace referencia a otros objetos, re a d O b je c t recorrerá sus referencias a otros ob­
jeto s recursivam ente, para recuperar todos ellos m anteniendo la relación que
existía entre ellos cuando fueron escritos.

V eam os un ejem plo donde se aplique lo expuesto. Si recuerda, en el capítulo


9 im plem entam os una clase C ListaTfnos para m antener una lista de teléfonos; y
después en el 11, 1a m ejoram os añadiendo el tratam iento de algunas excepciones.
Un objeto de esta clase representaba una lista de teléfonos; la lista está im ple-
m entada com o una m atriz de referencias a objetos C P ersona, y cada objeto C P er­
sona tiene com o atributos el nom bre, la dirección y el teléfono de un m iem bro de
la lista. D espués escribim os una clase aplicación Test, que utilizando las clases
CListaTfnos y C Persona perm itía buscar, añadir y elim inar teléfonos en una lista.
Para que d icha aplicación fuera útil sólo le faltaba un detalle m uy im portante, que
la lista creada fuera persistente. Esto im plica guardar la lista en un fichero cuando
la aplicación finalice, y recuperarla del fichero cuando la aplicación se inicie.
Realicem os pues, una tercera versión que incluya de form a autom ática las opera­
ciones de guardar y recuperar el objeto C ListaTfnos en un fichero denom inado
“ listatfnos.dat” .

Según lo explicado, para poder seriar un objeto de la clase CListaTfnos, ésta


debe im plem entar la interfaz Se rializable:
454 JA VA : C U R S O D E PROGRAM A CIÓN

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

public static void m a in (S trin g [] args)


C A PÍTU LO 12: T R A B A JA R C O N FICHEROS 4 5 5

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

ABRIENDO FICHEROS PARA ACCESO ALEATORIO


Hasta este punto, hem os trabajado con ficheros de acuerdo con el siguiente es­
quema: abrir el fichero, leer o escribir hasta el final del m ism o, y cerrar el fichero.
Pero no hem os leído o escrito a partir de una determ inada posición dentro del fi­
chero. Esto es particularm ente im portante cuando necesitam os m odificar algunos
de los valores contenidos en el fichero o cuando necesitem os extraer una parte
concreta dentro del fichero.

El paquete ja v a .io contiene la clase R a n d o m A c c e ss F ile la que proporciona


las capacidades que perm iten este tipo de acceso directo. A dem ás, un flujo de esta
clase perm ite realizar tanto operaciones de lectura com o de escritura sobre el fi­
chero vinculado con el m ism o. E sta clase se deriva directam ente de O bject, e im-
plementa las interfaces D a t a ln p u t y D a ta O u tp u t.

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

La clase R a n d o m A c c e ss F ile proporciona d os constructores:

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)

El prim er constructor abre un flujo vinculado con el fichero especificado por


nom bre-fichero, m ientras que el segundo hace lo m ism o, pero a partir de un o b ­
jeto File. El argum ento m odo puede ser:

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

A sim ism o, la clase R a n d o m A c c e ss F ile provee, adem ás de los m étodos de las


interfaces D a t a ln p u t y D a ta O u tp u t (vea el apartado “ flujos de datos" expuesto
anteriorm ente), los m étodos getF ile P ointer, length y seek que se definen de la
form a siguiente:

public lo n g g e t F i l e P o i n t e r í ) th rows IOException

Este m étodo devuelve la posición actual en bytes del puntero de L /E en el fi­


chero. Piense en el puntero de L /E análogam ente a com o lo hace cuando piensa en
el índice de una m atriz. Este puntero m arca siem pre la posición donde se iniciará
la siguiente operación d e lectura o de escritura en el fichero.

public long 1e n g t h ( ) throws IOException

Este otro m étodo devuelve la longitud del fichero en bytes.

public void seekdong pos) throws IOException

Y este otro m étodo, m ueve el puntero de L /E a una nueva localización d es­


plazada p o s bytes del principio del fichero. N o se perm iten desplazam ientos ne­
C A PÍTU LO 12: TR A B A JA R C O N FIC H ER O S 4 5 9

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.

Según lo expuesto, las dos líneas de código siguientes sitúan el puntero de


L/E, la prim era desp bytes antes del final del fichero y la segunda desp bytes des­
pués de la posición actual.

1 i s t a T e lé f o n o s . s e e k (1is t a T e l é f o n o s .1e n g t h ( ) - desp);


1 i s t a T e l é f o n o s . s e e k ( 1 i s t a T e l é f o n o s . g e t F i 1e P o i n t e r í ) + d e s p ) :

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

Si para nuestros propósitos, pensam os en los atributos nom bre, dirección y


teléfono com o si de un registro se tratara, ¿cuál es el tam año en bytes de ese re­
gistro? Si escribim os m ás registros ¿todos tienen el m ism o tam año? Evidente­
m ente no; el tam año de cada registro dependerá del núm ero de caracteres
alm acenados en los S trin g nom bre y dirección (teléfono es un dato de tam año fi­
jo, 8 bytes, puesto que se trata de un long) ¿A cuento de qué viene esta exposi­
ción?

Al principio de este apartado dijim os que el acceso aleatorio a ficheros es


particularm ente im portante cuando necesitem os m odificar algunos de los valores
contenidos en el fichero, o bien cuando necesitem os extraer una parte concreta
dentro del fichero. Esto puede resultar bastante com plicado si las unidades de
grabación que hem os denom inado registros no son todas iguales, ya que intervie­
nen los factores de: posición donde com ienza un registro y longitud del registro.
Tenga presente que cuando necesite reem plazar el registro n de un fichero por
otro, no debe sobrepasarse el núm ero de bytes que actualm ente tiene. T odo esto es
viable llevando la cuenta en una m atriz de la posición de inicio de cada uno de los
registros y de cada uno de los cam pos si fuera preciso (esta inform ación se alm a­
cenaría en un fichero índice para su utilización posterior), pero resulta m ucho m ás
fácil si todos los registros tienen la m ism a longitud.
460 JA V A : C U R S O D E P R O G R A M A C IÓ N

C om o ejem plo, vam os a escribir o tra versión d e la aplicación “lista de teléfo­


nos” desarrollada en el capítulo 9 y m odificada en el 11 y en éste. Esta nueva ver­
sión básicam ente sustituirá la m atriz de objetos C Persona encapsulada en
C ListaTfnos p o r un fichero con registros, conteniendo cada uno de ellos los atri­
butos n om bre, dirección y teléfono de un objeto C Persona.

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

p u b l i c C P e r s o n a C S t r i n g nom. S t r i n g dir. long t e l )


I
n o mb r e = nom;
d irección = d ir;
teléfono = t e l ;

public void asignarN om breíString nom)


I
nombre = nom;
)

public String obtenerNombre()


I
return n o mb r e :
I

p u b lic void asignarD ire cción(String dir)


I
di r e c c i ó n = d i r ;
)
CA PÍTU LO 12: TRA BA JA R C O N FIC H ER O S 4 6 1

public Strin g obtenerDirección()


í
return dirección:
I

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

public int tamañoO


(
II L o n g i t u d en b y t e s de l o s a t r i b u t o s ( u n L o n g = 8 b y t e s )
r e t u r n n o m b r e . 1 e n g t h ( ) * 2 + d i r e c c i ó n . l e n g t h ( ) * 2 + 8;

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

Según lo expuesto, la lista de teléfonos especificada se cargará desde un fi­


chero alm acenado en el disco y si ese fichero no existe, se creará uno nuevo. Para
ello, el constructor de la clase C ListaTfnos abrirá un flujo para acceso aleatorio
desde el fichero especificado, alm acenará una referencia al m ism o en un atributo
fe s de la clase, y en otro, nregs, alm acenará el núm ero de registros existentes en el
fichero. Un atributo m ás, tarnañoRegs, especificará el tam año que hayam os pre­
visto para cada registro. En nuestro caso, la inform ación alm acenada en un regis­
tro se corresponde con el nom bre, dirección y teléfono de un objeto CPersona.
462 JA V A : C U R S O D E P R O G R A M A C IÓ N

A teniéndonos a lo explicado, veam os a continuación el esqueleto de la clase


C ListaTfnos y el constructor de la misma:

/////////////////////////////////////////////////////////////////
// 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 C L ista T fn o s(F ile fichero) throws IOException


I
if ( f i c h e r o . e x i s t s t ) && ! 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 " ) :
f e s - new R a n d o m A c c e s s F i l e t f i c h e r o . " r w " ) ¡
// Como e s c a s i s e g u r o que e l ú l t i m o r e g i s t r o no o c u p e el
// t a m a ñ o f i j a d o , 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 il ( ( d o u b l e ) f e s . l e n g t h ( ) / (double)tamañoReg):

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

p u b lic boolean p o ne rV a lo rE ní int i. CPersona ob je to )


th rows IO E x c e p t io n
1
// ...

p u b lic CPersona v a lo r E n í int i ) th rows IOException

// ..

public void añadir(CPersona obj) throws IOException

// ...

public boolean e l i m i n a r ( l o n g tel) th rows IOException

II . ..

public int buscar(String str. in t pos) th rows IOException


I
// ...
CA PÍTU LO 12: TRA BA JA R C O N FIC H ER O S 4 6 3

O bserve que el constructor de la clase verifica si el argum ento pasado corres­


ponde a un nom bre existente en el directorio actual de trabajo y, en caso de que
exista, si realm ente se trata de un nom bre de fichero; si no es así, lanzará una ex­
cepción del tipo lO E x c e p tio n ; en otro caso, abre un flujo desde el fichero para
leer y escrib ir que perm ite el acceso aleatoriam ente al m ism o y calcula el núm ero
de registros del fichero.

Escribir un registro en e l fichero

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.

p u b lic boolean p o ne rV alorEnt int i, CPersona objeto )


throws lOException
1
if (i > = 0 && i <= n r e g s )
I
if ( o b j e t o . t a m a ñ o í ) + 4 > tamañoReg)
S y s t e m . e r r . p r i n t l n ( " t a m a ñ o del r e g i s t r o excedido"):
el se
I
f e s . s e e k d * t a m a ñ o R e g ) : I I s i t u a r e l p u n t e r o de L/E
f e s . w r i t e UT F ( o b j e t o . o b t e n e r N o m b r e ( ) ) :
fe s.w rite llT F (ob je to .o b te n e rD i r e c c i ó n ( ));
f e s . w r it e lo n g ( o b j e t o . o b t e n e r T e lé f o n o ( ));
return true:
I
I
el se
S y s t e m . e r r . p r i n t l n t " n ú m e r o de r e g i s t r o fuera de l i m i t e s " ) :
return false:
1

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

Añadir un registro al final del fichero

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.

public void añadir(CPersona obj) th rows IOException


I
if (ponerValorEn( nregs. obj )) nregs++:
I

Leer un registro del fichero

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.

public CPersona valorEnf int i ) th rows IOException


I
i f ( i > = 0 && i < n r e g s )
I
fes.seek(i * tamañoReg): // s i t u a r el p u n t e r o de L/E

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

r e t u r n new C P e r s o n a ( n o m b r e . dirección, teléfono):


I
el se
1
S y s t e m . o u t .p r i n t í n ( " n ú m e r o de r e g i s t r o fuera de l i m i t e s " ) :
return n u l1 ;

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

Elim inar un registro del fichero

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.

p u b lic boolean e l i m i n a r í l o n g t e l ) throws IOException


(
CPersona obj;
// B u s c a r e l t e l é f o n o y m a r c a r e l r e g i s t r o p a r a
// p o s t e r i o r m e n t e e l i m i n a r l o
f o r ( i n t re g _ i = 0; re g _ i < n r e g s ; r e g _ i + + )
I
o b j = v a l o r E n ( r e g _ i );
if (obj.obtenerTeléfono() = tel)
1
o b j .asignarTeléfono(O ):
p o n e r V a l o r E n ( r e g _ i , o bj );
re tu rn true:
I
I
return false:
I

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.

Buscar un registro en el fichero

U na operación m uy com ún en el trabajo con registros es localizar uno determ ina­


do. ¿C óm o buscar un teléfono en una lista de teléfonos? L o m ás com ún es buscar
466 JA V A : C U R SO DE PRO G R A M A CIÓ N

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.

public int buscar(String str, int pos) throws IOException


I
// B u s c a r un r e g i s t r o p o r una s u b c a d e n a d e l nombre
// a p a r t i r d e un r e g i s t r o d e t e r m i n a d o
CPersona obj:
S t r i n g nom;
if (str = n u i l ) re t u r n -1;
i f ( p o s < 0 ) p o s = 0;
f o r ( i n t reg_i = pos; reg_i < n regs: re g _ i+ + )
[
o bj = v a l o r E n ( r e g _ i ) ;
nom = o b j . o b t e n e r N o m b r e l ):
// ¿ s t r e s t á c o n t e n i d a en nom?
i f ( n o m. i n d e x O f ( s t r ) > - 1 )
return r e g _ i;
I
return -1;
I

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 ejemplo de acceso aleatorio a un fichero


A continuación vam os a escribir una aplicación basada en una clase Test para tra­
b ajar con u na lista de teléfonos construida a partir de un objeto C ListaTfnos. Esta
aplicación será prácticam ente la m ism a que vim os en la versiones de la aplicación
“lista de teléfonos” desarrollada en el capítulo 9 y m odificada en el 11 y al princi­
pio de éste; por eso no abundarem os en detalles en aquellas partes que ya hayan
sido explicadas. Esto dem uestra que cuando una clase, com o C ListaTfnos, se m o­
difica y no se alteran los prototipos de los m étodos que com ponen su interfaz, las
aplicaciones que la utilizan no se ven afectadas. En nuestro caso, esta clase tiene
C A P ÍT U L O 12: TRA BA JA R C O N FIC H ER O S 4 6 7

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

El esqueleto de esta aplicación se m uestra a continuación. En él se puede ob­


servar que se han definido com o atributos de la clase T est los flujos para acceder a
la entrada y salida estándar, así com o una referencia listatfnos al objeto que en-
capsulará la lista de teléfonos con la que deseam os trabajar.

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 i c boolean m o d i f i c a r ( i n t nreg) throws IOException


f1
// .
I

public s ta t ic void a c t u a l i z a r ( F i 1e f A c t u a l ) throws IOException


í1
// .
1

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

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 t f i c h e r o ) :

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

L a ejecución de la aplicación se iniciará p o r el m étodo m a in que, en prim er


lugar, crea el objeto C ListaT fnos cuya interfaz nos dará acceso aleatorio al fichero
especificado. D espués, ejecutará un bucle que invocará al m étodo m enú encarga­
d o de solicitar la elección de una de las opciones presentadas p o r él:

public static int me n ú ( )


I
f l u j o S . p r i n t ( " \ n \ n " ):
f 1 u j o S . p r i n t l n ( " 1 . B u s c a r " );
f l u j o S . p r i n t l n ( ”2. B u s c a r s i g u i e n t e " ) :
f 1 u j o S . p r i n t l n ( ”3. Modi f i c a r " );
f l u j o S . p r i n t l n t "4. A ñ a d i r " ) ;
f l u j o S . p r i n t l n ( " 5 . El i m i n a r " );
f 1 u j o S . p r i n t l n{ " 6 . S a l i r ' ' ) :
f 1 u j o S . p r i n t l n ( ):
flujoS.printl" Opción: " ) ;
C A P ÍT U L O 12: TRA BA JA R C O N FICHEROS 4 6 9

int op:
do
o p = Leer . d a t o l n t í ) ;

while ( o p< 1 || o p > 6):


r e t u r n o p :

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.

Para realizar tal m odificación, el proceso seguido por el m étodo es:

• 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

p u b l i c s t a t ic b o o l e a n m o d i f i c a r ( i n t nreg) throws IOException


I
S t r i n g nombre, d i r e c c i ó n ;
l ong teléfono;
int op;
// Leer el r e g i s t r o
C P e r s o na obj = 1 i s t a t f n o s .v a l o r E n í n r e g );
if (obj = nuil) r e t u r n false;

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

El proceso seguido para realizar lo expuesto es sencillo. B ásicam ente se crea­


rá un fichero tem poral (fichero que existe durante un corto espacio de tiem po,
m ientras lo necesitem os) para guardar todos los registros del fichero actual cuyo
núm ero de teléfono sea distinto de cero. D espués de realizar esta operación, cerra­
rem os am bos ficheros y utilizarem os la interfaz de la clase F ile para borrar el fi­
chero actual y renom brar el fichero tem poral con el nom bre que tenía el fichero
actual.

public static v o i d a c t u a l i z a r ( F i 1e f A c t u a l ) th rows IOException


I
// C r e a r un f i c h e r o t e m p o r a l
F i l e f i c h e r o T e m p - new F i l e í "1 i s t a t f n o s . t m p " ) :
C L i s t a T f n o s f t e m p - new C L i s t a T f n o s ( f i c h e r o T e m p ) :

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

UTILIZACIÓN DE DISPOSITIVOS ESTÁNDAR


La salida de un program a puede tam bién ser enviada a un dispositivo de salida
que no sea el disco o la pantalla; por ejem plo, a una im presora conectada al puerto
paralelo. C om o Java no tiene definido un flujo estándar para el puerto paralelo, la
solución es d efinir uno y vincularlo a dicho dispositivo.

U na form a de realizar lo expuesto es crear un flujo hacia el dispositivo L P T l,


LP T2, o P R N y escribir en ese flujo (los nom bres indicados son los establecidos
p o r W indow s para nom brar a la im presora; en U N IX la prim era im presora tiene
asociado el nom bre /devApO, la segunda / d ev A p I, etc.). Las siguientes líneas de
código m uestran cóm o realizar esto:

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 om o ejem plo, vam os a añadir al m enú de la aplicación anterior, una opción


im prim ir q ue perm ita obtener la lista de teléfonos por la im presora.

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

El código anterior indica que cuando el usuario seleccione la opción 6 del


m enú de la aplicación, se invocará al m étodo estático imprimirLÁstaTfnos de la
clase aplicación Test. E ste m étodo, creará un flujo hacia la im presora, obtendrá el
núm ero total de registros del fichero “lista de teléfonos” y establecerá un bucle
para im prim ir cada uno de ellos. El código com pleto se m uestra a continuación:

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

for (in t n = 0: n < nregs: n++)


I
// S a l t a r p á g i n a i n i c i a l m e n t e y d e s p u é s c a d a 6 0 l i n e a s
i f (n % 60 “ 0) f l u j o S . w r i t e ( f f ) :
II I m p r i m i r e l r e g i s t r o n d e l a l i s t a de t e l é f o n o s
i = new I n t e g e r ( n ) ; // nú me r o d e r e g i s t r o
flujoS.w riteC Registro: " + i.to S trin g O + c rlf);
flujoS.w rite (listatfnos.valorEn (n).obtene rN om bre() + c r lf ):
flu jo S .w ri t e ( 1ist a t fn o s .v a lo rE n (n ).o b t e n e rD i recei ó n ( ) + c r l f ) :
1 = new L o n g ( 1 i s t a t f n o s . v a l o r E n ( n ) . o b t e n e r T e l é f o n o ( ) ) :
flu jo S.w rite d .to Stringí) + crlf):
f l u j o S . w r i t e t c r l f ) : // s a l t a r una l i n e a
I
f l u j o S . w r i t e t f f ): // s a l t a r a l a s i g u i e n t e página
f l u j o S . c l o s e ( ): II c e r r a r e l f l u j o h a c i a la impresora

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:

java C o p ia r F ic h e ro ¿ f i c h e r o fuente> ¿ f i c h e r o destino?

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.

La funcionalidad de la clase C opiarF ichero estará soportada fundam entalm ente


por el m étodo copiar que tiene el prototipo siguiente:

public static void copiar(String fuente. String destino)

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.

A continuación se m uestra la aplicación com pleta, suficientem ente com entada


com o para no tener que abundar en m ás explicaciones:

import j a v a . i o . * :

public class CopiarF


i
public s t a t ic void c o p ia r(S trin g fuente, Strin g destino)
th rows IO E x c e p tio n
I
// S i el f i c h e r o f u e n t e y e l d e s t i n o s o n e l mi s mo f i c h e r o . . .
i f ( f u e n t e . c o m p a r e T o ( d e s t i n o ) = = 0)
t h r o w new E C o p i a r F i c h e r o ( " N o p u e d e s o b r e e s c r i b i r s e un " +
" f i c h e r o s o b r e s i m i s m 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 );

d m ain (Strin g [] args)

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

copiar(args[0], a rgs[l]); // r e a l i z a r la copia

catch(IOException e)

S y s t e m .o u t .p r in t ín ("E r r o r : " + e.getM essage()):


CA PÍTU LO 12: TRA B A JA R CON FICHEROS 4 7 7

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

La clase aplicación, G rep, deberá proporcionar al m enos los siguientes m étodos:

a) B uscarC adena para buscar una cadena de caracteres dentro de otra. El prototi­
po de este m étodo será:

static boolean B u s c a rC a d e n a (S t rin g cadenal. String cadena?)

E ste m étodo devolverá tr u e si cadena2 se encuentra dentro de c a d e n a l; en


otro caso, devolverá false.

b) B uscarE nF ich para buscar una cadena de caracteres en un fichero de texto e


im prim ir el núm ero y el contenido de la línea que contiene a la cadena. El
prototipo de este m étodo será:

static void B u s c a r E n F ic h (S t r in g nombrefich, String cadena)

c) m a in para que utilizando los m étodos anteriores perm ita buscar una palabra en
uno o m ás ficheros.

La form a de invocar a la aplicación será así:

java Grep p a l a b r a fichero_l fichero_2 ... fichero_n

A continuación se m uestra la aplicación com pleta, suficientem ente com entada.

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

• C rear un flujo F ile ln p u tS tr e a m asociado con el fichero de texto.


• C onectar un filtro In p u t S t r e a m R e a d e r con el flujo anterior.
• Y finalm ente, conectar el filtro B u ffe r e d R e a d e r con el filtro anterior.

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

public static void B u s c a r E n F i c h ( S t r i n g nombrefich, Strin g cadena)


I
// D e f i n i c i o n e s de v a r i a b l e s
F i l e f i c h F u e n t e = new F i l e ( n o m b r e f i c h ) ;
BufferedReader flu jo E - n u il;

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

public static void m ainíStringC ] args) . ,/


I
// m a i n d e b e r e c i b i r d o s o más p a r á m e t r o s : la cadena a buscar
// y l o s f i c h e r o s f u e n t e . P o r e j e m p l o :
// j a v a G r e p c a t c h G r e p . j a v a L e e r . j a v a

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:

Nombre d e l fichero: artículos

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.

Según el enunciado, la funcionalidad de la clase C Registro estará soportada


por los atributos referencia y precio y por los m étodos siguientes:

• 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

La declaración de esta clase se m uestra a continuación:

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

public void asignarR eferencia(Strin g ref)

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:

public void asignarPreciotdouble pre)


I
p r e c io = pre:

p u b lic double o b t e n e r P r e c io t )
I
return precio:

public int tamañot)


(
// L o n g i t u d en b y t e s de l o s a t r i b u t o s (un D o u b le = 8 b y t e s )
r e t u r n r e f e r e n c i a . 1 e n g t h ( ) * 2 + 8:

S iguiendo con el ejem plo, la funcionalidad d e la clase C BaseD edatos deberá


perm itir, abrir el fichero, cerrarlo, calcular su longitud, insertar, obtener, buscar y
elim in ar un registro, así com o actualizar el fichero cuando sea preciso. Para ello,
482 JA VA : C U R S O DE PRO G R A M A CIÓ N

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.

• Los m étodos cerrar y longitud para cerrar el fichero y calcular su longitud,


respectivam ente.

• Los m étodos ponerV alorE n , para sobreescribir un registro en una posición


cualquiera dentro del fichero, y añ a d ir para insertarlo al final.

• El m étodo valorEn para obtener un registro del fichero.

• El m étodo buscar para localizar un determ inado registro en el fichero.

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

Según lo expuesto, la declaración de la clase C B aseD eD atos puede ser com o


se m uestra a continuación. L os com entarios introducidos son suficientes para en­
tender el código sin necesidad de tener q ue abundar m ás explicaciones.

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

p u b lic void cerrarO th rows IOException I fes.closeO ; I

public int longitudO | return nregs: I // nú me r o de r e g i s t r o s

p u b lic boolean ponerValorEn( int i. CRegistro objeto )


th rows IO E x c e p tio n
I
if (i >=0 && i <= n r e g s )
I
// L o s 2 p r i m e r o s b y t e s q u e e s c r i b e w r i t e U T F i n d i c a n l a
// l o n g i t u d de l a S t r i n g q u e e s c r i b e a c o n t i n u a c i ó n . E s t a
// i n f o r m a c i ó n e s u t i l i z a d a p o r r e a d U T F .
i f ( o b j e t o . t a m a ñ o ! ) + 2 > tamañoReg)
S y s t e m . e r r . p r i n t l n ( " t a m a ñ o del r e g i s t r o e x c e d i d o " ) :
el s e
I
// S i t u a r e l p u n t e r o de L/E en e l r e g i s t r o i .
f e s . s e e k t i * tamañoReg):
// S o b r e e s c r i b i r e l r e g i s t r o con l a n u e v a i n f o r m a c i ó n
fes.w rite U TF(objeto.obte nerR efere ncia());
fe s.w ri te ü o u b le (o b je t o .o b t e n e r P r e c io ());
return true;
I
I
el se
S y s t e m . e r r . 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 ” );
return false;

public void a ñ a d ir(C R e g istro obj) throws IOException


I
// A ñ a d i r un r e g i s t r o a l f i n a l d e l f i c h e r o e incrementar
// e l nú mero de r e g i s t r o s
i f ( p o n e r V a l o r E n ( n r e g s . obj ) ) n r e g s + + ;

public CRegistro valorEnt int i ) th rows IOException


I
if (i > = 0 && i < nregs)
[
// S i t u a r e l p u n t e r o de L /E en e l registro i.
fes.seek(i * 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;

public int buscar(String str. int nreg) th rows IOException


I
// B u s c a r un r e g i s t r o p o r una s u b c a d e n a de l a r e f e r e n c i a
// a p a r t i r d e un r e g i s t r o d e t e r m i n a d o . S i s e e n c u e n t r a .
// s e d e v u e l v e e l nú me r o de r e g i s t r o , o -1 en o t r o c a s o .
C R e g i s t r o obj;
S trin g ref;
if (str “ n u i l ) re tu rn -1;
i f ( n r e g < 0 ) n r e g = 0;
fo r ( in t reg_i - nreg; reg_i < nregs: re g _ i+ + )
I
// O b t e n e r e l r e g i s t r o r e g _ i
o b j = v a l o r E n ( r e g _ i );
// O b t e n e r s u r e f e r e n c i a
r e f = o b j . o b t e n e r R e f e r e n c i a í );
// ¿ s t r e s t á c o n t e n i d a en r e f e r e n c i a ?
i f ( r e f . in d e x O f( s t r ) > -1)
r e t u r n r e g _ i ; // d e v o l v e r e l nú me r o de r e g i s t r o
I
return -1; // la búsqueda falló

p u b lic boolean e l i m i n a r ( S t r i n g ref) th rows IOException


I
CRegistro o b j:
// B u s c a r l a r e f e r e n c i a y m a r c a r e l r e g i s t r o c o r r e s p o n d i e n t e
// p a r a p o d e r e l i m i n a r l o en o t r o p r o c e s o ,
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
// Obtener el r e g i s t r o re g _ i
obj = valorEn(reg_i) ;
// ¿T iene la re fe re n c ia re f ?
if (ref.com pareTo(obj.obtenerReferencia()) = = 0)
I
// M a r c a r el r e g i s t r o con l a r e f e r e n c i a "borrar”
o b j . a s i g n a r R e f e r e n c i a ( " b o r r a r " );
CA PÍTU LO 12: T R A B A JA R C O N FIC H ER O S 4 8 5

// G r a b a r l o
ponerValorEn! re g _ i . obj );
return true:

return false:

public void actualizare) th rows IOException


I
// C r e a r un f i c h e r o t e m p o r a l .
F i l e f i c h e r o T e m p = new F i 1e ( " a r t i c u l o s . t m p " ) ;
C B a s e D e D a t o s f t em p = new C B a s e D e D a t o s ( f i c h e r o T e m p ) ;

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

public static void nuevoFichl) throws IOException

// ...

public static void a b r i r F i c h l ) throws IOException

// ...

public static void añadirRegO th rows IOException

II...

public static void m o d if ic a r R e g t ) throws IOException

// ...

public static boolean e l i m i n a r R e g l ) th rows IOException

II...

public static void v i s u a l i z a r R e g s ! ) throws IOException


I
// ...

public static i n t menú O


I
f 1 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 ' l . N u e v o f i c h e r o ” );
flu jo S .p rin tln ("2 . A brir fiche ro ");
f l u j o S . p r i n t l n ( " 3 . A ñ a d i r r e g i s t r o ” );
f lu j o S . p r i n t l n ( "4. M od ificar r e g is t r o ") ;
f l u j o S . p r i n t l n ( ” 5. E l i m i n a r r e g i s t r o " ) ;
flu jo S.p rin tln ("6 . V isualizar re g istro s");
f l u j o S . p r i n t í n ( “ 7. S a l i r " ) ;
f l u j o S . p r i n t l n í );
flu joS.printl" Opción: ") ;
i n t op;
do
CA PÍTU LO 12: TRA BA JA R CON FIC H ER O S 4 8 7

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

if ( o p > 2 && op < 7 && I f i c h e r o A b i e r t o )


I
f l u j o S . p r i n t l n( " N o h a y un f i c h e r o a b ie rto .'');
r e t u r n 0:
I
return op;

public static void m a in (S trin g [] args)


I
i n t o p c i ó n - 0;
boolean elim inado = f a ls e : // t r u e c u a n d o s e m a r q u e un r e g i s t r o
// p a r a "borrar”
try
I
do
I
o p c ió n - menú():
sw itch (opción)
(
c a s e 1: // n u e v o f i c h e r o
n u e v o F i c h t );
break;
c a s e 2 : // a b r i r f i c h e r o
a b r i r F i c h ( );
break;
c a s e 3: // a ñ a d i r r e g i s t r o a l f i n a l d e l f i c h e r o
a ñ a d i r R e g ( ):
break;
c a s e 4 : // m o d i f i c a r r e g i s t r o
m o d i f i c a r R e g ( );
break;
c a s e 5: // e l i m i n a r r e g i s t r o
e l i m i n a d o = e l i m i n a r R e g í ):
break;
c a s e 6: // v i s u a l i z a r r e g i s t r o s
v i s u a l i z a r R e g s í ):
break;
c a s e 7: // s a l i r
i f ( e l i m i n a d o ) a r t í c u l o s . a c t u a l i z a r ( );
a rtíc u lo s = nuil;

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.

F inalm ente, cuando se seleccione la opción Salir, se actualizará el fichero


sólo si se m arcó algún registro para borrar. El resto de las operaciones (añadir y
m odificar) realizan los cam bios directam ente sobre el fichero.

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.

public static void nuevoFichO throws IOException


I
if (ficheroAbierto)
1
f 1u j o S . p r i n t l n ( " Y a h a y un f i c h e r o abierto."):
return:
I

f l u j o S . p r i n t í " N o m b r e del f i c h e r o : " ) ;


F i l e o b j F i c h e r o = new F i 1e ( L e e r . d a t o ( ) ) ; // no mb r e f i c h e r o
w h i l e ( o b j 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 l n í “E s t e f i c h e r o e x i s t e . Escriba otro."):
o b j F i c h e r o = new F i 1 e ( L e e r . d a t o ( ) ) :
I
artículos = new C B a s e D e D a t o s ( o b j F i c h e r o ) ;
C A P ÍT U L O 12: TRA BA JA R CON FIC H ER O S 4 8 9

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.

public static void abrirFichO th rows IOException


I
if (ficheroAbierto)
I
f 1u j o S . p r i n t l n ( " Y a h a y un f i c h e r o abierto."):
return;

f l u j o S . p r i n t ( " N o m b r e del f i c h e r o : " ) :


F i l e o b j F i c h e r o = new F i 1 e ( L e e r , d a t o ( ) ) ; // nombre f i c h e r o

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;

A ñadir un registro al fichero

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

m ensaje a ñ a d ir (se ejecuta el m étodo a ñadir de su clase) pasando com o argu­


m ento el objeto C Registro obtenido a partir de los datos leídos.

public static void añadirRegO th rows IOException


I
Strin g referencia:
double pre cio:

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

M odificar un registro del fichero

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:

Finalm ente, sólo si se eligió la opción 3, enviará al objeto artículos el m ensaje


ponerV alorE n (se ejecuta el m étodo ponerV alorE n de su clase) pasando com o a r­
gum ento el objeto C R egistro obtenido a partir de los nuevos datos leídos.

public static void m odificarReg!) throws IOException


I
S trin g referencia:
double p re cio :
i n t o p, n r e g ;

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

if (op = = 3) artículos.ponerValorEntnreg. obj);

Eliminar un registro del fichero

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

public static b o o lea n e l i m i n a r R e g í ) th rows IOException


I
S trin g referencia;
flu jo S . p rin t ( "R e fe re n c ia : ") : referencia = Leer.datoí);
b o o l e a n e l i m i n a d o = a r t i c u l o s . e l i m i n a r ( r e f e r e n c i a ):
i f (elim inado)
f lu j o S . p r in t ln í" r e g is t r o elim inado");
el se
i f ( a r t i c u l o s . 1 o n g i t u d ( ) ! = 0)
f l u j o S . p r i n t í n( " r e f e r e n c i a no e n c o n t r a d a ' ' ) ;
else
f 1 u j o S . p r i n t l n ( ” 1 i s t a v a c i a " );
return eliminado;

Visualizar registros del fichero

El m étodo visualizar R egs se diseñará para que visualice el conjunto de registros


cu y o cam po referencia coincida o contenga la cadena/subcadena solicitada a tra­
vés del teclado. Para ello, enviará al objeto artículos el m ensaje buscar (se ejecuta
el m étodo buscar de su clase) pasando com o argum entos la cadena/subcadena a
b uscar y el núm ero de registro donde debe em pezar la búsqueda (inicialm ente el
cero), proceso que se repetirá utilizando el siguiente núm ero de registro al últim o
encontrado, m ientras la búsqueda no falle.

public static void v i s u a l i z a r R e g s í ) throws IOException


I
i n t n r e g = - 1 , n r e g s = a r t i c u l o s . 1 o n g i t u d í );
i f (nregs = 0)
f l u j o S . p r i n t l n ( “1i s t a v a c i a " ) ;

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


Strin g s t r = Leer.datoí);
CRegi s t r o o bj = n u i l ;

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

while (nreg != -1);


C A PÍTU LO 12: TRA BA JA R CON FIC H ER O S 4 9 3

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:

public class Cabecera


I
private class Posición
I
// A t r i b u t o s
in t grad os, minutos;
f l o a t segundos:
II M é t o d o s
// . . .
I
// P o s i c i ó n g e o g r á f i c a d e l p u n t o
private Posición latitud;
private Posición longitud;
private int total_m uestras;
II M é t o d o s
// . . .
I

A continuación de la cabecera estarán especificadas todas las tem peraturas. Cada


una de ellas es un valor float.

El program a deberá perm itir realizar las operaciones siguientes:

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:

public class CRegistro


I
// A t r i b u t o s
p r i v a t e i n t n ú m e r o M a t r 1c u l a ;
p r i v a t e S t r i n g nombre:
private Strin g c a lific a c ió n :
// M é t o d o s
// . . .
I

La calificación viene dada por dos caracteres: SS (suspenso), A P (aprobado), NT


(notable) y SB (sobresaliente). R ealizar un program a que perm ita visualizar el %
de los alum nos suspendidos, aprobados, notables y sobresalientes.

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.

En el fichero m odificaciones se han grabado las m odificaciones que posterior­


m ente realizarem os sobre el fichero alum nos. E n m odificaciones hay com o má­
xim o un registro por alum no y se corresponden con:

• 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

E ste capítulo introduce técnicas en program ación orientada a objetos para


co n stru ir estructuras abstractas de datos. U na vez que haya trabajado los ejem plos
d e este capítulo, será capaz de explotar en sus aplicaciones la potencia de las listas
enlazadas, pilas, colas y árboles binarios.

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.

Listas lineales simplemente enlazadas


U na lista lineal sim plem ente enlazada es una colección de objetos (elem entos de
la lista), cada uno d e los cuales contiene datos o una referencia a los datos, y una
referencia al siguiente objeto en la colección (elem ento de la lista). G ráficam ente
puede representarse de la form a siguiente:
C A P ÍT U L O 13: ESTR U C TU R A S DINÁM ICA S 4 9 7

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.

U na vez creada la clase de objetos C E lem entoLse la asignación de m em oria


para un elem ento se haría así:

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 código C E lem entoLse p define una referencia p a un objeto de la clase


C Elem entoLse. La sentencia p = new C E lem entoL sef) crea (asigna m em oria para)
un objeto de tipo C Elem entoLse, genera una referencia (dirección de m em oria)
que direcciona este nuevo objeto y asigna esta referencia a la variable p . L a sen­
tencia p .siguiente = nuil asigna al m iem bro siguiente del objeto referenciado por
p el valor nuil, indicando así que después de este elem ento no hay otro; esto es,
que este elem ento es el últim o de la lista.

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

tenem os una lista de un elem ento:

nuil

Para añadir un nuevo elem ento a la lista, procederem os así:

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

Por últim o, la sentencia p = q hace que la lista quede de nuevo referenciada


por p \ es decir, para nosotros p es siem pre el p rim er elem ento de la lista.

/ ■
n u il

A hora p y q referencian al m ism o elem ento, al prim ero. Si ahora se ejecutara


una sentencia com o la siguiente ¿qué sucedería?

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:

¿Q ué sucede? Sucede que com o q.siguiente vale nuil, a q se le ha asignado el va­


lor nuil. C onclusión, cuando en una lista utilizam os una referencia para ir d e un
elem ento al siguiente, en el ejem plo anterior q, direm os que hem os llegado al fi­
nal de la lista cuando q tom a el valor nuil.

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

1. Insertar un elem ento en una lista.


2. B orrar un elem ento de una lista.
3. R ecorrer los elem entos de una lista.
4. B orrar todos los elem entos de una lista.
5. B uscar un elem ento en una lista.

Partiendo de las definiciones:

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:

public elass Test


I
public static v o i d main( StringC ] args )
I
C E l e m e n t o L s e p. q. r: // r e f e r e n c i a s
II ...

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.

Inserción de un elemento al comienzo de la lista

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;

El orden en el que se realizan estas operaciones es esencial. El resultado es:

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

System .out.p rin t("d a to : "):


w h i le ( ( n = L e e r .dato I n t í )) != eof)
(
q = new C E 1 e m e n t o L s e t ):
q .dato = n :
q . s i g u i e n t e = p;
P - q;
System .out.p r in t ( "d a t o : "):

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

Inserción de un elemento en general

La inserción de un elem ento en la lista, a continuación de otro elem ento cualquie­


ra referenciado por r, es de la form a siguiente:

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

Inserción en la lista detrás d e l elem ento referenciado p o r r

La inserción de un elem ento en la lista antes de otro elem ento referenciado


por r, se hace insertando un nuevo elem ento detrás del elem ento referenciado por
r, intercam biando previam ente los valores del nuevo elem ento y del elem ento re­
ferenciado por r.

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

Inserción en la lista antes d e l elem ento referenciado p o r r


C A P ÍT U L O 13: ESTRU C TU R A S D IN Á M ICA S 5 0 3

Borrar un elemento de la lista

Para borrar el sucesor d e un elem ento referenciado p o r r, las operaciones a reali­


zar son las siguientes:

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 )

B o rrar el su ceso r d e l elem ento referenciado p o r r

Un objeto es enviado a la basura para ser recogido p o r el recolector de basura


de Java, sólo cuando se elim inan todas las referencias que perm iten acceder al
mismo.

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:

B orrar el elem ento referenciado p o r r

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 )

C om o ejercicio, escribir la secuencia de operaciones que perm itan borrar el


últim o elem ento de una lista.
504 JA V A : C U R S O D E P R O G R A M A C IÓ N

Recorrer una lista

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

Borrar todos los elementos de una lista

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:

p = nuil; // b o r r a r la lista referenciada por p

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.

Buscar en una lista un elemento con un valor x

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 .

UNA CLASE PARA LISTAS LINEALES


B asándonos en las operaciones básicas sobre listas lineales descritas anterior­
mente, vam os a escribir a continuación una clase que perm ita crear una lista lineal
sim plem ente enlazada en la que cada elem ento conste de dos m iem bros: un valor
real de tipo d o u b le y una referencia a un elem ento del m ism o tipo.

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

private cla ss CElemento


I
// A t r i b u t o s
p r i v a t e double dato;
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
p r i v a t e CElementoí) II // c o n s t r u c t o r
I

Finalm ente, para sim plificar, la interfaz pública de la ciase C ListaLinealSE


proporcionará solam ente tres m étodos: un constructor sin parám etros, añadirAl-
Príncipio y m ostrarTodos.

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.

Según lo expuesto, C L istaL inealSE será así:


506 J A V A : C U R S O D E P R O G R A M A C IÓ N

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

II E l e m e n t o de una l i s t a lineal simplemente enlazada


p r i v a t e c l a s s CElemento
I
// A t r i b u t o s
p r i v a t e double dato;
p r i v a t e C E l e m e n t o 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
p r i 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 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

public void m ostra rT o d o sí)


I
// R e c o r r e r l a l i s t a
C E l e m e n t o q = p; // r e f e r e n c i a al prim er elemento
w h i l e ( q ! = n u l 1)
I
System .out.printCq.dato + ” ");
q = q.siguiente;

//////////////////////////////////////////////////////////////////

A poyándonos en esta clase, vam os a escribir una aplicación basada en una


clase T est que cree una lista lineal sim plem ente enlazada que alm acene una serie
de valores de tipo d o u b le introducidos desde el teclado. F inalm ente, para verificar
que todo ha sucedido com o esperábam os, m ostrarem os la lista de valores.

Para llevar a cabo lo expuesto, el m étodo m a in de esta aplicación realizará


tres cosas:
C A PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 0 7

1. D efinirá un objeto Ise de la clase C ListaLinealSE .


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 Ise el m ensaje añadirA lP rincipio p o r cada dato que añada.
3. M ostrará la lista de datos, para lo cual enviará al objeto Ise el m ensaje mos-
trarTodos.

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

C on el fin de acercarnos m ás a la realidad de cóm o debe ser la clase C Lista­


L inealSE , vam os a sustituir el m étodo m ostrarTodos p o r otro m étodo obtener que
devuelva el v alo r alm acenado en el elem ento i de la lista. D e esta form a, será la
aplicación que utilice la clase C L istaL inealSE la que decida qué hacer con el valor
retom ado (im prim irlo, acum ularlo, etc.).

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

p u b lic double obtener(int i)


I
if ( p == n u il)
I
S y s t e m . e r r .pri n t l n ( “1 i s t a v a c i a " ):
r e t u r n Double.NaN;

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

A hora, para que el m étodo m a in de la aplicación anterior m uestre los datos


utilizando este m étodo, tenem os que reescribir la parte del m ism o que realizaba
este proceso, com o se indica a continuación:

public static void m a in (S trin g [] args)


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

Clase genérica para listas lineales


La clase C L istaL inealSE im plem entada anteriorm ente ha sido diseñada para m a­
nipular listas de un tipo específico de elem entos: datos de tipo double. No cabe
duda que esta clase tendría un m ayor interés para el usuario si estuviera diseñada
para perm itir listas de objetos de cualquier tipo. É sta es la dirección en la que va­
mos a trabajar a continuación. E n otros lenguajes com o C++, esto se hace u tili­
zando plantillas. En Java se hace utilizando la clase O bject.

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:

public class C ListaLinealSE 1 ... )

public class C ListaLinealSE extends Object I ... 1

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 :

// E l e m e n t o de una l i s t a lineal simplemente enlazada


5 10 JA V A : CU R SO D E PRO G R A M A CIÓ N

prívate class CElemento


I
// A t r i b u t o s
p riva te 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
p rivate CElementoO II // c o n s t r u c t o r

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

p u b lic Object o b te n e r íin t i)


I
i f (p - - n u l 1)
I
System .err.println("1 ista v a c i a " ):
r e t u r n n u l 1;

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:

1. D efinirá un objeto Ise de la clase C ListaLinealSE.


CListaLinealSE l s e = new C l i s t a L i n e a l S E ( ) :
CA PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 1 1

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

3. M ostrará la lista de valores, para lo cual enviará al objeto h e el m ensaje obte­


ner por cada uno de los elem entos de la lista. El m étodo obtener devuelve una
referencia de tipo O b je c t que, en nuestro caso, apunta a un objeto de su sub­
clase D ou ble . Pero, si accedem os a un objeto de una subclase por m edio de
una referencia a su superclase, ese objeto sólo puede ser m anipulado por los
m étodos de su superclase. T endrem os entonces que convertir la referencia de
tipo O b je ct a tipo Dou ble . Según estudiam os en el capítulo 10, se trata de
una conversión explícita de una referencia del tipo de una superclase a una
referencia a una de sus subclases, lo cual sólo puede hacerse si el objeto refe­
renciado es del tipo de la subclase, condición que en nuestro ejem plo se cum ­
ple, ya que el o bjeto es de la clase D ou ble .
Double d = ( D o u b l e ) 1 s e . o b t e n e r í i );

A continuación se m uestra la aplicación Test com pleta:

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

A continuación se m uestra el código com pleto de la clase C ListaLinealSE.


O bservar que adem ás de los m étodos especificados en la tabla anterior, se ha aña­
dido a la clase C E lem ento un constructor con parám etros.

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

// E l e m e n t o de una l i s t a lineal sim plemente en laz ada


p r i v a t e c l a s s CElemento
I
// A t r i b u t o s
p riv a t e Object datos:
p r i v a t e C E l e m e n t o s i g u i e n t e : // s i g u i e n t e e l e m e n t o
// M é t o d o s
p r i v a t e C E l e m e n t 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 l O b j e c t d . C E l e m e n t o 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;
I

public C ListaLinealSE!) II // c o n s t r u c t o r

public int tamaño!)


I
// D e v u e l v e e l nú me r o de e l e m e n t o s d e l a l i s t a
C E l e m e n t o q = 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
i n t n = 0: // nú me r o de e l e m e n t o s
w h i1e ( q != n u i l )
I
n++;
q = q.siguiente:
I
return n:
1

public boolean a ñ a d i r ! i n t i, Object obj)


I
// A ñ a d i r un e l e m e n t o en 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)
I
System .err.p rin tln !"índ ice fuera de l í m i t e s " ) :
514 JA V A : C U R SO D E PRO G R A M A CIÓ N

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 r e f e r e n c i a d a por p e stá vacia, añadirlo sin más


i f (númeroDeElementos = 0)
I
// A ñ a d i r e l prim er elemento
P = q:
return true;

// 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 boolean añadirAlPrincipioíO bject obj)


I
// A ñ a d i r un e l e m e n t o a l principio
return añadir(0. obj):

public boolean a ñ a d ir A l F i n a l ( O b j e c t obj)


I
// A ñ a d i r un e l e m e n t o a l f i n a l
r e t u r n a ñ a d i r ( t a m a ñ o ! ). o b j ) ;
I
CA PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 1 5

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 rra r P rim e ro ()


I
// B o r r a r e l p r i m e r e l e m e n t o
return borrar(O);

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

p u b lic Object o b te n e rtin t i)


I
// O b t e n e 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 - ta mañot):
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;
516 JA V A: C U R S O DE PROGRAM A CIÓN

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:

p u b lic Object ob te n e rP rim e ro ()


I
// R e t o r n a r e l p r i m e r e l e m e n t o
r e t u r n o b t e n e r ( 0 ):

p u b lic Object o b te n e rú ltim o ()


I
// R e t o r n a r e l ú l t i m o e l e m e n t o
re tu r n o b te n e r ( tamaño() - 1):

//////////////////////////////////////////////////////////////////

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

public void a s i g n a r N o m b r e l S t r i n g nom)


CA PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 1 7

nombre = nom:
)

public String obtenerNombret)


I
return n o mbr e :
1

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

El código de la aplicación Test que se m uestra a continuación enseña cóm o


crear y m anipular una estructura de datos com o la de la figura anterior:

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

public s t a t ic void m a in (S trin g [] args)


(
// C r e a r una l i s t a lin e a l vacia
C ListaLinealSE lse = new C L i s t a L i n e a l S E ( ) ;

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

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


o b j.o b t e n e r N o t a í)):
// M o s t r a r t o d o s
S y s te m . o u t.p r i n t l n ( ”Li s t a : " ) ;
m ostrarLista(lse):

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

A continuación se presenta otra versión de la aplicación Test utilizando ahora


la clase L in k e d L is t en lugar de C ListaLinealSE .

import java .u t i 1 .*;


//////////////////////////////////////////////////////////////////
// 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 class Test
1
public static void m o st r a r L is t a ( L in k e d L ist 1s e )
I
i n t i = 0, tam = l s e . s i z e O :
CDatos o b j ;
w h i l e ( i < tam)
I
o bj = ( C D a t o s ) 1 s e . g e t ( i );
System .out.p r in t ín ( i + " + o b j .obtenerNombre() + " " +
o b j .o b t e n e r N o t a ( ) ) ;
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 v a c i a
L i n k e d L i s t I s e = new L i n k e d L i s t ( ) :
CA PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 2 1

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

private class 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
C A P ÍT U L O 13: E S T R U C T U R A S D IN Á M IC A S 523

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


tam año D evuelve el núm ero de elem entos de la lista. N o tiene pa­
rám etros.
añadirAIPrincipio A ñade un elem ento al principio (el prim er elem ento es el
referenciado p o r últim o.siguiente). 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.
añadirAlF inal A ñade un elem ento al final (el últim o elem ento siem pre
estará referenciado por últim o). 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.
borrar B orra el elem ento prim ero (el p rim er elem ento es el refe­
renciado por últim o.siguiente). N o tiene parám etros. D e­
vuelve una referencia al objeto borrado o n u il si la lista está
vacía.
524 JA V A: C U R SO D E PRO G R A M A CIÓ N

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.

A continuación se presenta el código correspondiente a la definición de la cla­


se C L is ta d rcularSE:

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 ;

// E l e m e n t o de una l i s t a lineal circular sim plemente enlazada


p r i v a t e c l a s s CElemento
I
// 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 C E l e m e n t o s i g u i e n t e : // s i g u i e n t e e l e m e n t o
// M é t o d o s
p r i v a t e C E l e m e n t o O I ) // c o n s t r u c t o r
p r i v a t e C E 1 e m e n t o ( O b j e c t d . C E l e m e n t o s ) // c o n s t r u c t o r
I
datos = d :
siguiente = s :

public C L ista C ire u la rS E ( ) 11 // c o n s t r u c t o r

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

public void añadirAlPrincip io(0bject obj)


I
// A ñ a d e un e l e m e n t o a l p r i n c i p i o d e l a lista.
// C r e a r e l n u e v o e l e m e n t o
C E l e m e n t o q = new C E l e m e n t o t o b j , n u i l ) :

if( último != nu il ) // e x i s t e una lista


I
q .siguiente = últim o.siguiente:
últim o.siguiente - q:
I
else // i n s e r c i ó n del prim er elemento
I
último = q ;
últim o.siguiente - q;
I

public void a ñ a d irA lF in a l(O b je c t obj)


I
// A ñ a d e un e l e m e n t o a l f i n a l de l a l i s t a .
// P o r l o t a n t o , ú l t i m o r e f e r e n c i a r á e s t e n u e v o e l e m e n t o .
// C r e a r e l n u e v o e l e m e n t o .
C E l e m e n t o q = new C E l e m e n t o t o b j . n u i l ) :

if( último != n u il ) // e x i s t e una l i s t a


I
q .siguiente = últim o.siguiente:
último = ú ltim o .sig u ie n te = q;
I
else // inserción del prim er elemento
I
último = q :
últim o.siguiente = q:

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 .

p u b lic Object o b te n e r tin t i)


I
II O b t e n e r e l e l e m e n t o d e l a p o s i c i ó n i
i n t númeroDeElementos = t a m a ñ o O ;
i f ( i > = n ú m e r o D e E l e m e n t o s || i < 0 )
return nu il:

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

U na v ez que hem os escrito la clase C ListaC ircularSE vam os a realizar una


aplicación que utilizándola cree una lista circular y ponga a prueba las distintas
operaciones que sobre ella pueden realizarse. L os elem entos de esta lista serán
objetos de la clase C D atos utilizada en ejem plos anteriores. El código de esta
aplicación puede ser el siguiente:

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

while (i < tam)


I
o b j = ( C D a t o s )1 e s e . o b t e n e r ( i );
CA PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 2 7

System .out. p r in t ln ( i + ".- " + o b j .obtenerNombre() + " " +


o b j .o b t e n e r N o ta í)):
i++:
I
if ( t am = = 0 ) S y s t e m . o u t . p r i n t í n ( "1 i s t a vacia"):

public static void m a in (S trin g [] args)


I
II C r e a r una l i s t a c i r c u l a r v a c i a
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 Í );

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

M éto d o____________ S ig n ificad o ______________________________________________


m eterE nP ila M ete un elem ento en la cim a de la pila (todas las insercio­
nes se hacen por el principio de la lista). T iene un parám e­
tro que es una referencia de tipo O b je c t al objeto a añadir.
N o devuelve ningún valor.
sacarD eP ila S aca el p rim er elem ento de la cim a de la pila, elim inándolo
de la m ism a (todas las supresiones se hacen por el principio
de la lista). N o tiene parám etros. D evuelve una referencia
_____________________ al objeto sacado de la pila o n u il si la pila está vacia._______

Según lo expuesto, la definición de esta clase puede ser así:

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

public void meterEnPila(Object obj)


I
a ñ a d i r A l P r i n c i p i o ( o b j );
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
//////////////////////////////////////////////////////////////////

El constructor de la clase C Pila llam a prim ero al constructor de la clase base


que crea una lista con cero elem entos. El que la lista sea circular es transparente al
usuario de la clase.
CA PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 2 9

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.

O bserve que la derivación de la clase C P ila de C ListaC ircularSE no oculta al


usuario la interfaz pública de ésta, lo que perm itiría utilizarla. Si quisiéram os
ocultarla podríam os haber declarado todos sus m iem bros de form a predeterm ina­
da, adem ás de d efinir la clase dentro de un paquete concreto (por ejem plo, es­
tructuras). E ste es un trabajo que queda fuera de los propósitos de este capítulo,
pero con lo explicado en el capítulo de “S ubclases e interfaces" no tendría ningu­
na dificultad en hacerlo. Lo que se ha pretendido aquí es crear un interfaz especí­
fica para el trabajo con pilas, y eso es lo que se ha hecho. V erem os una pequeña
aplicación después de exponer el apartado relativo a colas.

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:

M éto d o_____________S ig n ificad o _____________________________________________


m eterE nC ola M ete un elem ento al final de la cola (todas las inserciones
se hacen por el final de la lista). T iene un parám etro que es
una referencia de tipo O b je c t al objeto a añadir. No d e ­
vuelve ningún valor.
sacarD eC ola S aca el prim er elem ento de la cola, elim inándolo de la
m ism a (todas las supresiones se hacen por el principio de la
lista). N o tiene parám etros. D evuelve una referencia al ob-
_____________________ je to sacado de la co la o n u il si la cola está vacía.__________

Según lo expuesto, la definición de esta clase puede ser así:

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

// h a c e n p o r e l o t r o extremo (por el principio).


II
public class CCola extends CListaCircularSE
I
public CCola() 11

public void meterEnCola(Object obj)


I
a ñ a d i r A l F i n a l ( o b j );
)

public Object sacarDeColaf)


I
r e t u r n b o r r a r ( ):
1
I
//////////////////////////////////////////////////////////////////

El constructor de la clase C C ola llam a p rim ero al constructor de la clase base


que crea una lista con cero elem entos. El que la lista sea circular es transparente al
usuario de la clase.

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.

O bserve que la derivación de la clase C C ola de C L istaC ircularSE no oculta al


usuario la interfaz pública de ésta, lo que perm itiría utilizarla. Lo que se ha pre­
tendido aq u í es crear un interfaz específica para el trabajo con colas, y eso es lo
que se ha hecho. V ea la explicación que hem os dado en este sentido al exponer las
pilas.

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

objeto in sta n c e o f clase

El resultado de una expresión com o la anterior es tr u e si el objeto es un


ejem plar de la clase y false en caso contrario. C om o ejem plo, observe el m étodo
m ostrarLista de la aplicación siguiente. E ste m étodo tiene un parám etro que le
perm ite recib ir objetos de la clase C ListaC ircularSE o de sus derivadas: C P ila y
CCola. U tiliza el operador in s ta n c e o f para saber con qué clase de objeto está tra­
bajando y enviarle así los m ensajes adecuados. Se puede observar que para sacar
un elem ento del objeto lista, si es de la clase CPila se le envía el m ensaje sacar-
D eP ila y si es la clase C C ola, sacarD eC ola\ en otro caso, no se hace nada.

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

System .out.p r i n t l n ( " 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 ( “nombre:
w h i le ((nombre = L e e r . d a t o O ) != n u i l )
I
System .out.p r in t í" n o t a : ");
n o t a = L e e r . d a t o D o u b l e t ):
p i 1a . m e t e r E n P i l a ( n e w C D a t o s ( n o m b r e . nota)):
cola.m eterEnColatnew CDatos(nombre, nota)):
System .out.printt"nom bre: "):
I
S y s t e m . o u t . p r i n t í n ( " \ 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 '

Si ejecutam os esta aplicación e introducim os los siguientes datos.

In tro d u c ir datos. Finalizar con C t r l + Z .


n o mbr e : A l u m n o 1
nota: 7.5
n o mbr e : A l u m n o 2
nota: 8.5
n o mbr e : A l um n o 3
nota: 9.5
nombre: [ C t r l + Z ]

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.

LISTA DOBLEMENTE ENLAZADA


En una lista doblem ente enlazada, a diferencia de una lista sim plem ente enlazada,
cada elem ento tiene inform ación de dónde se encuentra el elem ento posterior y el
elem ento anterior. E sto perm ite leer la lista en am bas direcciones. E ste tipo de
listas es útil cuando la inserción, borrado y m ovim iento de los elem entos son ope­
raciones frecuentes. U na aplicación típica es un procesador de textos, donde el ac­
ceso a cada línea individual se hace a través de una lista doblem ente enlazada.

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.

En el apartado siguiente se expone la form a de construir y m anipular una lista


circular doblem ente enlazada.
534 JA V A : C U R S O D E P R O G R A M A C IÓ N

Lista circular doblemente enlazada


U na lista circular doblem ente enlazada (led e) es una colección de objetos, cada
uno de los cuales contiene datos o una referencia a los datos, una referencia al
elem ento siguiente en la colección y una referencia al elem ento anterior. G ráfica­
m ente puede representarse de la form a siguiente:

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:

private class CElemento


I
// 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 CElemento a n t e r i o r : // a n t e r i o r e l e m e n t o
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 e l e m e n t o

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

A continuación se presenta el código correspondiente a la definición de la cla­


se C ListaC ircularD E :
536 JA V A : C U R S O D E P R O G R A M A C IÓ N

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

// E l e m e n t o de una l i s t a lineal circular doblemente enlazada


p r i v a t e c l a s s CElemento
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 CElemento a n t e r i o r ; // a n t e r i o r e l e m e n t o
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 e l e m e n t o

// M é t o d o s
p riva te CElementoO II // c o n s t r u c t o r

public C ListaC ircu larD EO // c o n s t r u c t o r


I
a c t u a l = ú l t i m o = n u l 1:
n ú m e r o D e E l e m e n t o s = OL;
p o s i c i ó n = - I L : // l a p o s i c i ó n del p rim e r elemento s e rá la 0

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:

public void insertar(Object obj)


I
II A ñ a d e un n u e v o e l e m e n t o a l a l i s t a a c o n t i n u a c i ó n
// d e l e l e m e n t o a c t u a l ; e l n u e v o e l e m e n t o p a s a a s e r el
// a c t u a l .
C E l e m e n t o q:

if (últim o == n u il) // lista vacia


I
ú l t i m o = new C E l e m e n t o O :
CA PÍTU LO 13: E S T R U C T U R A S D IN Á M IC A S 5 3 7

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

actual = q: // e l nuevo elemento pasa a s e r el actual.


I // f i n else

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 irA lSigu ie n te í)


I
// A v a n z a l a p o s i c i ó n a c t u a l al s i g u i e n t e elemento,
i f (posición < n ú m e r o D e E l e m e n t o s - 1)
I
actual = a c t u a l.s ig u ie n te ;
p o s i c i ón++:

public void irA lA nterior()


I
II R e t r a s a l a posición actual al elemento a n t e r i o r ,
i f ( posición > 01 )
I
actual = a c t u a l.a n t e r i o r ;
p o s i c i ó n - -;
)
)

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:

public boolean irAl(long i)


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
l o n g n ú m er o D e E l e m e n t o s - t a m a ñ o O ;
i f ( i > - n ú m e r o D e E l e m e n t o s || i < 0 ) return false:

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:

p u b lic Object o b te n e rí)


I
// El m ét o d o o b t e n e r d e v u e l v e l a r e f e r e n c i a a los datos
// a s o c i a d o s c o n e l e l e m e n t o a c t u a l .
i f ( ú l t i m o = = n u i l ) r e t u r n n u i l : // l i s t a vacia

return a c tu a l.d a to s:

p u b lic Object o b te n e rílo n g i)


I
// El m ét o d o o b t e n e r d e v u e l v e l a r e f e r e n c i a a los datos
// a s o c i a d o s c o n e l e l e m e n t o de I n d i c e i .
i f ( ! i r A l ( i ) ) r e t u r n n u l 1:
r e t u r n o b t e n e r ( );

pu b lic void m o d ifica r(0 b je c t pNuevosDatos)


i
// El m ét o d o m o d i f i c a r e s t a b l e c e n u e v o s d a t o s p a r a el
// e l e m e n t o a c t u a l .
i f í ú l t i m o = = n u i l ) r e t u r n : // l i s t a v a c i a

a c t u a l. d a t o s = pNuevosDatos:

//////////////////////////////////////////////////////////////////

C uando se declara un objeto de la clase C U staC ircularD E , se ejecuta el


constructor de la m ism a que realiza las siguientes operaciones:
540 JA V A: C U R S O DE PROGRAM A CIÓN

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

• A signa un valor 0 al atributo núrneroD eElem entos y un valor -1 a p o si­


ción; el valor de este atributo pasará a ser 0 cuando se añada el prim er
elem ento.

El m étodo insertar de la clase C L istaC ircularD E añade un elem ento a la lista


a continuación del elem ento actual. C ontem pla dos casos: que la lista esté vacía, o
que la lista ya exista. El elem ento insertado pasa a ser el elem ento actual, y si se
añade al final, éste pasa a ser el últim o y el actual. A ñadir un elem ento im plica
realizar los enlaces con el anterior y siguiente elem entos y actualizar los parám e­
tros a ctu a l, núrneroD eElem entos y últim o, si procede.

El m étodo b orrar devuelve una referencia al objeto de datos asociado con el


elem ento actual, elem ento que será enviado a la basura cuando finalice la ejecu­
ción del m étodo por quedar desreferenciado. C ontem pla dos casos: que el ele­
m ento a borrar sea el últim o o que no lo sea. Si el elem ento a borrar es el últim o, y
sólo quedaba éste, los atributos de la lista deben iniciarse igual que lo hizo el
constructor; si quedaban m ás de uno, el que es ahora el nuevo últim o p asa a ser
tam bién el elem ento actual. Si el elem ento a borrar no era el últim o, el elem ento
siguiente al elim inado pasa a ser el elem ento actual. El m étodo devuelve n u il si la
lista está vacía.

Para el resto de los m étodos es suficiente con la explicación dada al principio


de este apartado, adem ás de en el código.

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

public static void m a in (S trin g [] args)


(
// C r e a r un a l i s t a v a c i a
C L i s t a C i r c u l a r D E l c d e - new C L i s t a C i r c u l a r D E ( ) :

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

a) Existe un nodo especial llam ado raíz del árbol, y


b) los nodos restantes están agrupados en n > 0 conjuntos disjuntos A¡, ... , A,„
cada uno de los cuales es a su v ez un árbol que recibe el nom bre de subárbol
de la raíz.

Evidentem ente, la definición dada es recursiva; es decir, hem os definido un


árbol com o un conjunto de árboles, que es la form a m ás apropiada de definirlo.

De la definición se desprende, que cada nodo de un árbol es la raíz de algún


subárbol contenido en la totalidad del m ism o.

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

El árbol binario es una estructura de datos m uy útil cuando el tam año de la


estructura no se conoce, se necesita acceder a sus elem entos ordenadam ente, la
velocidad de búsqueda es im portante o el orden en el que se insertan los elem en­
tos es casi aleatorio.

En definitiva, un árbol binario es una colección de objetos (nodos del árbol)


cada uno de los cuales contiene datos o una referencia a los datos, una referencia a
su subárbol izquierdo y una referencia a su subárbol derecho. Según lo expuesto,
la estructura de datos representativa de un nodo puede ser de la form a siguiente:

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

La definición dada de árbol binario, sugiere una form a natural de representar


árboles binarios en un ordenador. U na variable raíz referenciará el árbol y cada
nodo del árbol será un objeto de la clase C Nodo. E sto es, la declaración genérica
de un árbol binario puede ser así:

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 CNo d o r a í z ; // 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
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.

Formas de recorrer un árbol binario


O bserve la figura “expresión algebraica” m ostrada anteriorm ente ¿partiendo del
nodo raíz, qué orden seguim os para poder evaluar la expresión que representa el
árbol? Hay varios algoritm os para el m anejo de estructuras en árbol y un proceso
que generalm ente se repite en estos algoritm os es el de recorrido de un árbol. Este
proceso consiste en exam inar sistem áticam ente los nodos de un árbol, d e forma
que cada nodo sea visitado solam ente una vez.

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

F orm as de recorrer un árbol

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.

Si se aplican estas definiciones al árbol binario de la figura “expresión alge­


braica” m ostrada anteriorm ente, se obtiene la siguiente solución:

Preorden: / + a * b c - d / e f
Inorden: a + b * c / d - e / f
Postorden: a b c * + d e f / - /

El recorrido en preorden produce la notación p refija ; el recorrido en inorden


produce la notación convencional; y el recorrido en postorden produce la notación
p ostfijo o inversa.

L os nom bres de preorden, inorden y postorden derivan del lugar en el que se


visita la raíz con respecto a sus subárboles. E stas tres form as, se exponen a conti­
nuación com o tres m étodos recursivos de la clase C A rbolB inario, con un único
parám etro r que representa la raíz del árbol cuyos nodos se quieren visitar.

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

public void 1 no r d e n ( G N ' o d o r )


I
if ( r != n u i l )
(
i n 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
// 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
inorden( r.d e re c h o ): // s e v i s i t a e l s u b á r b o l derecho
1

public void postorden(CNodo r)


I
if ( r ! = n u 11 )
I
p o s t o r d e n t r . i z q u i e r d o ) : II s e v i s i t a e l s u b á r b o l izquierdo
postordení r.derecho): // s e v i s i t a e l s u b á r b o l derecho
// 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
// con e l n o d o r e f e r e n c i a d o r

/////////////////////////////////////////////////////////////////

ÁRBOLES BINARIOS DE BÚSQUEDA


U n árbol binario de búsqueda es un árbol ordenado; esto es, las ram as de cada
nodo están ordenadas de acuerdo con las siguientes reglas: para todo nodo a¡, to­
das las claves del subárbol izquierdo de a, son m enores que la clave de a¡, y todas
las claves del subárbol derecho de a¡ son m ayores que la clave de a¡.

C on un árbol de estas características, encontrar si un nodo de una clave de­


term inada existe o no es una operación m uy sencilla. P or ejem plo, observando la
figura siguiente, localizar la clave 35 es aplicar la definición de árbol de busque-
CA PÍTU LO 13: ESTRU C TU R A S D IN Á M ICA S 5 4 7

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.

Á rbol binario de búsqueda

En Java podem os autom atizar el proceso de im plem entar un árbol binario de


búsqueda diseñando una clase C ArbolBinB (C lase A rb o l Binario de Búsqueda)
que proporcione los atributos y m étodos necesarios para crear cada nodo del ár­
bol, 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 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:

M éto d o ____________ S ig n ificad o _____________________________________________


C ArbolBinB Es el constructor. C rea un árbol vacío (raíz a nuil).
insertar Inserta un nodo en el árbol binario basándose en la defini­
ción de árbol binario de búsqueda. T iene un parám etro que
es una referencia de tipo O b je c t al objeto a añadir. D evuel­
ve un 0. definido por la constante C O RREC TO , si la opera­
ción de inserción se realiza satisfactoriam ente, o un valor
548 JA V A : C U R S O D E P R O G R A M A C IÓ N

distinto de 0, definido por alguna de las constantes si­


guientes: N O _D A T O S si la raíz es igual a nuil, o
YA_EXISTE si el nodo con esos datos ya existe.
borrar B orra un nodo de un árbol binario de búsqueda. T iene un
parám etro para alm acenar una referencia de tipo O b je c t a
los datos que perm itirán localizar en el árbol el nodo que se
desea borrar. D evuelve una referencia al área de datos del
nodo borrado, o bien n u il si el árbol está vacío o no existe
un nodo con esos datos.
buscar Busca un nodo determ inado en el árbol. T iene un parám e­
tro para alm acenar una referencia de tipo O b je c t a los d a­
tos que perm itirán localizar el nodo en el árbol. D evuelve
una referencia al área de datos del nodo, o bien n u il si el
árbol está vacío o no existe un nodo con esos datos.
inorden Recorre un árbol binario utilizando la form a inorden. Tiene
dos parám etros: el prim ero especifica la referencia al nodo
a partir del cual se realizará la visita; el valor del prim er pa­
rám etro sólo será tenido en cuenta si el segundo es false,
porque si es tr u e se asum e que el p rim er parám etro es la
raíz del árbol. N o devuelve ningún valor.
com parar M étodo que debe ser redefinido por el usuario en una sub­
clase para especificar el tipo de com paración que se desea
realizar con dos nodos del árbol. D ebe de devolver un ente­
ro indicando el resultado de la com paración (-1, 0 ó I si
n o d o l< n o d o 2 , n o d o l = =nodo2. o n o d o l> n o d o 2 , respecti­
vam ente). E ste m étodo es invocado por los m étodos inser­
tar, borrar y buscar.
proceso M étodo que debe ser redefinido p o r el usuario en una sub­
clase para especificar las operaciones que se desean realizar
con el nodo visitado. Es invocado p o r el m étodo inorden.
visitarlnorden M étodo sin parám etros que debe ser redefinido por el usua­
rio en una subclase para invocar al m étodo inorden.

A continuación se presenta el código correspondiente a la definición de la cla­


se C ArbolBinB:

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

// P o s i b l e s e r r o r e s q u e s e p u e d e n dar re la tiv o s a un nodo


public s t a t i c f i n a l i n t CORRECTO = 000;
public s t a t i c f i n a l i n t NO_DATOS = 100
public s t a t i c f i n a l i n t YA_EXISTE = 101
public s t a t i c f i n a l i n t N0_EXISTE = 102

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

p u b lic Object b u sc a r(0 b je c t obj)


I

public int insertar(0bject obj)


550 J A V A : C U R S O D E P R O G R A M A C IÓ N

public void inordent CNodo r , boolean nodoRaiz)


I
// El m ét o d o r e c u r s i v o i n o r d e n v i s i t a l o s n o d o s d e l á r b o l
II u t i l i z a n d o l a forma i n o r d e n ; e s t o e s , p r i m e r o s e v i s i t a
// el su b á r b o l i z q u i e r d o , después se v i s i t a la r a í z , y por
// ú l t i m o , el su b á r b o l derecho.
// S i el segundo parám etro es t r u e , la v i s i t a comienza
// 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 p a r á m e t r o .

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 .

/////////////////////////////////////////////////////////////////

Buscar un nodo en el árbol


El m étodo buscar cuyo código se m uestra a continuación perm ite acceder a los
datos de un nodo del árbol. Su sintaxis es la siguiente:

pu blic Object buscartObject obj)

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.

pu b lic Object buscar(Object obj)


I
// El m ét od o b u s c a r p e r m i t e a c c e d e r a un d e t e r m i n a d o n o do .
CNodo a c t u a l = r a í z ;
i n t nComp = 0:

// B u s c a r un n o d o q u e t e n g a asociados los datos dados p o r obj


w h i 1e ( a c t u a l ! = n u i l )
I
if ( ( nComp = c o m p a r a r e o b j . a c t u a l . d a t o s ) ) - - 0)
r e t u r n ( a c t u a l . d a t o s ): // CORRECTO ( n o d o e n c o n t r a d o )
e l s e i f ( nComp < 0 ) // b u s c a r en e l s u b á r b o l i z q u i e r d o
actual - actual .izquierdo;
else // b u s c a r en e l subárbol derecho
actual = actual.derecho:
I
return n u l 1; // N 0 _ E X I S T E

Insertar un nodo en el árbol


El m étodo insertar cuyo código se m uestra a continuación perm ite añadir un nodo
que aún no existe en el árbol. Su sintaxis es la siguiente:

public int insertar(Object obj)

El parám etro o b j se refiere al objeto de datos que será apuntado p o r el nodo


que se añadirá al árbol. D evuelve un entero N O _D A T O S si o b j es nuil, C O RREC ­
TO si la operación de insertar se ejecuta con éxito, y Y A_EXISTE si ya hay un no­
do con los datos referenciados p o r obj.

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

m ism o proceso descrito en el m étodo buscar), en cuyo caso, com o ya se indicó en


el párrafo anterior, lo notificará. Si ese nodo no se encuentra, el proceso de bús­
queda nos habrá conducido hasta un nodo term inal, posición donde lógicamente
debe añadirse el nuevo nodo que alm acenará la referencia o b j a los datos.

public int in se rta r(O b je ct obj)


I
// El m é t od o i n s e r t a r p e r m i t e a ñ a d i r un n o d o que a ún no e s t á
// en e l á r b o l .
CNodo ú l t i m o = n u i l , a c t u a l = r a i z :
i n t nComp = 0;
i f ( obj “ n u i l ) r e t u r n N0_DAT0S;

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

if ( actual = nuil ) // n o s e e n c o n t r ó e l nodo, añadirlo


I
CNodo n u e v o N o d o = new C N o d o ( ) :
nuevoNodo.datos = obj:
n u evoN od o.izq uierd o - nuevoNodo.derecho = n u i l :
// El n o d o a a ñ a d i r p a s a r á a s e r l a r a í z d e l á r b o l t o t a l s i
// é s t e e s t á v a c i o , d e l s u b á r b o l i z q u i e r d o d e " ú l t i m o " s i l a
// c o m p a r a c i ó n f u e m e n or , o d e l s u b á r b o l d e r e c h o de " ú l t i m o " s i
// l a c o m p a r a c i ó n f u e m a y o r ,
i f ( último = n u i l ) // á r b o l v a c i o
r a í z = nuevoNodo:
e l s e i f ( nComp < 0 )
ú l t i m o . i z q u i e r d o = nuevoNodo:
el se
ú l t i m o . d e r e c h o = nuevoNodo:
r e t u r n CORRECTO:
) // f i n d e l b l o q u e i f ( a c t u a l - - n u i l )
e l s e // e l n o d o y a e x i s t e en e l á r b o l
r e t u r n Y A _ E X 1S T E ;
C A P ÍT U L O 13: ESTR U C TU R A S D IN Á M ICA S 5 5 3

Borrar un nodo del árbol


A continuación se estudia el problem a de borrar un determ inado nodo de un árbol
que tiene las claves ordenadas. E ste proceso es una tarea fácil si el nodo a borrar
es un nodo term inal o si tiene un único descendiente. La dificultad se presenta
cuando deseam os borrar un nodo que tiene dos descendientes (en la figura, 17), ya
que con una sola referencia no se puede apuntar en dos direcciones. E n este caso,
el lugar en el árbol del nodo a borrar será reem plazado p o r su sucesor presentán­
dose dos casos: si tom am os com o sucesor la raíz d e su subárbol izquierdo (13), su
subárbol derecho (21) lo será ahora del nodo m ás a la derecha (13) en el subárbol
izquierdo (13), y si tom am os com o sucesor la raíz de su subárbol derecho (21), su
subárbol izquierdo (13) lo será ahora del nodo m ás a la izquierda (18) en el subár­
bol derecho (21).

B orrar e l nodo con clave 17

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:

1. El nodo a borrar es un nodo term inal; no tiene descendientes.


2. El nodo a borrar no tiene subárbol izquierdo.
3. El nodo a borrar no tiene subárbol derecho.
554 JA V A : C U R S O D E P R O G R A M A C IÓ N

4. El nodo a borrar tiene subárbol izquierdo y derecho.

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.

p u b lic Object b o rra r(O b je c t obj)


I
// El m ét o d o b o r r a r p e r m i t e e l i m i n a r un n o d o d e l árbol.
CNodo ú l t i m o = n u i l , a c t u a l = r a í z ;
CNodo m a r c a d o = n u i l , s u c e s o r = n u i l ;
i n t n A n t e r i o r C o m p = 0. nComp = 0;

if (obj == n u i l ) return n u il: // N 0 _ D A T 0 S

// C o m i e n z a l a búsqueda para ve rificar si 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 .
while ( actual != n u l 1 )
I
n A n t e r i o r C o m p = nComp; // r e s u l t a d o de l a comparación a n t e r i o r
i f ( ( nComp = c o m p a r a r t 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
último = actual ;
i f ( nComp < 0 ) // b u s c a r en e l subárbol izquierdo
actual = a c t u a l. izquierdo;
else // b u s c a r en e l subárbol derecho
actual = actual.derecho;
I
) // f i n del bloque w hile ( actual != n u i l )

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

Utilización de la clase CArboIBinB


La clase C ArboIBinB es una clase abstracta; p o r lo tanto, para hacer uso del so­
porte que proporciona para la construcción y m anipulación de árboles binarios de
búsqueda, tendrem os que derivar una clase de ella y redefinir los m étodos abs­
tractos heredados: com parar, p ro ce sa r y visitarlnorden. L a redefinición de estos
m étodos está condicionada a la clase de objetos que form arán parte del árbol.

C om o ejem plo, vam os a construir un árbol binario de búsqueda en el que cada


nodo haga referencia a un objeto de la clase C D atos ya utilizada anteriorm ente en
este m ism o capítulo. Esto sugiere pensar en la clave de ordenación que se utilizará
para construir el árbol. En nuestro ejem plo vam os a ordenar los nodos del árbol
p o r el atributo nom bre de CD atos. Se trata entonces de una ordenación alfabética;
por tanto, el m étodo com parar d ebe ser redefm ido para que devuelva - 1 , 0 ó 1 se­
gún sea el nom bre de un objeto C D atos, m enor, igual o m ayor, respectivam ente,
que el nom bre del otro objeto con el que se com para.

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

C uando se declare un objeto de la clase C A rbolB inarioD eB usqueda, el cons­


tructor de esta clase invoca al constructor C ArbolBinB de su clase base, que creará
un árbol vacío (raíz = nuil). El atributo raíz apunta siem pre a la raíz del árbol.

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:

1. C reará un o b jeto arbolbb de la clase C ArbolBinarioD eBusqueda.

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.

3. D urante la construcción del árbol, perm itirá m odificar la nota de un alum no ya


existente, o bien elim inarlo. Para discrim inar una operación de otra tomaremos
com o referencia la nueva nota: si es positiva, entenderem os que deseam os m o­
dificar la nota del alum no especificado y si es negativa, que hay que eliminarlo.

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;

S y s t e m . o u t .p rin t ín ( " In tr o d u c ir datos. Finalizar con C t r l + Z . " ) :

System .out.printí"nom bre: "):


w h i l e ((nombre = L e e r . d a t o O ) !- nu il)
I
System .out.p r i n t í" n o t a : ");
n o t a = L e e r . d a t o D o u b l e í ):
cod - á r b o l b b . i n s e r t a r t n e w C D a to s ( n o m b re , nota)):
558 JA V A : C U R S O D E P R O G R A M A C IÓ N

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

System .out.print("nom bre: ”):


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

ÁRBOLES BINARIOS PERFECTAMENTE EQUILIBRADOS


U n árbol binario está perfectam ente equilibrado si, para todo nodo, el núm ero de
nodos en el subárbol izquierdo y el núm ero de nodos en el subárbol derecho, di­
fieren com o m ucho en una unidad.

Á rboles p erfectam ente equilibrados

C om o ejem plo, considere el problem a de construir un árbol perfectam ente


equilibrado siendo los valores de los nodos, n referencias a objetos de la clase
C D atos im plem entada anteriorm ente en este m ism o capítulo. R ecuerde que cada
objeto de esta clase encapsula el nom bre de un alum no y la nota de una determ i­
nada asignatura que está cursando.
C A P ÍT U L O 13: ESTR U C TU R A S D IN Á M IC A S 5 5 9

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:

1. U tilizar un nodo para la raíz.


2. G enerar el subárbol izquierdo con n i = n/2 nodos utilizando la m ism a regla.
3. G enerar el subárbol derecho con n d = n-ni-1 nodos utilizando la m ism a regla.

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.

private class CNodo

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

En Java podem os autom atizar el proceso de im plem entar un árbol binario


perfectam ente equilibrado diseñando una clase C A rholB inE (C lase A rb o l Binario
E quilibrado) que proporcione los atributos y m étodos necesarios para crear cada
nodo del árbol, así com o para perm itir el acceso a los m ism os.

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:

M éto d o_____________S ig n ificad o_____________________________________________


C ArboIB inE Es el constructor; com o es igual que el constructor por
om isión, podría om itirse. C rea un árbol vacío (raíz a nuil).
construirA rbol Es un m étodo privado que perm ite construir un árbol bina­
rio perfectam ente equilibrado. T iene un parám etro de tipo
in t que se corresponde con el núm ero d e nodos que va a te­
ner el árbol. D evuelve una referencia a la raíz del árbol.
560 JA V A : C U R S O DF. P R O G R A M A C IÓ N

construí rA rbolE qui librado


Invoca al m étodo construirA rbol pasando com o argum ento
el núm ero de nodos y alm acena el v alor devuelto p o r él, en
el atributo raíz de la clase. N o devuelve nada. Su m isión es
evitar que el usuario de la clase tenga que utilizar directa­
m ente el atributo raíz,
b u sca r B usca un nodo determ inado en el árbol. T iene un parám e­
tro para alm acenar una referencia de tipo O b je c t a los da­
tos que perm itirán localizar el nodo en el árbol. D evuelve
una referencia al área de datos del nodo, o bien nuil si el
árbol está vacío o no existe un nodo con esos datos. O pcio­
nalm ente se puede especificar un segundo parám etro co­
rrespondiente a la posición del nodo según el orden de
acceso seguido p o r el m étodo inorden (consideram os q ue la
prim era posición es la 0).
inorden R ecorre un árbol binario utilizando la form a inorden. Tiene
dos parám etros: el prim ero especifica la referencia al nodo
a partir del cual se realizará la visita; el valor del p rim er pa­
rám etro sólo será tenido en cuenta si el segundo es false.
porque si es tr u e se asum e que el p rim er parám etro es la
raíz del árbol. N o devuelve ningún valor.
leerD atos M étodo q ue debe ser redefinido por el usuario en una sub­
clase para que perm ita leer los datos que serán referencia-
dos p o r un nodo del árbol. D evuelve el objeto de datos. Es
invocado p o r el m étodo construirArbol.
com parar M étodo que debe ser redefinido p o r el usuario en una sub­
clase para especificar el tipo de com paración que se desea
realizar con dos nodos del árbol. D ebe d e devolver un ente­
ro indicando el resultado de la com paración (-1, 0 ó 1 si
n o d o l < nodo2, n o d o l = =nodo2, o n o d o l> n o d o 2 . respecti­
vam ente). E ste m étodo es invocado por los m étodos inser­
tar, borrar y buscar,
pro ceso M étodo que debe ser redefinido p o r el usuario en una sub­
clase para especificar las operaciones que se desean realizar
con el nodo visitado. Es invocado p o r el m étodo inorden.
visitarlnorden M étodo sin parám etros que debe ser redefinido por el usua-
_____________________ rio en una subclase para invocar al m étodo inorden.________

A continuación se presenta el código correspondiente a la definición de la cla­


se C A rbolB inE :

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

private CNodo c o n s t r u í ' r A r b o l ( i n t n )


I
// C o n s t r u y e un á r b o l de n n o d o s p e r f e c t a m e n t e e q u i l i b r a d o

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

p riva te void buscar(Object obj. CNodo r . O b j e c t [ ] datos, i n t [] pos)


(
II El m ét o d o b u s c a r p e r m i t e a c c e d e r a un d e t e r m i n a d o n o do .
// S i l o s d a t o s e s p e c i f i c a d o s p o r " o b j " s e l o c a l i z a n en el
// á r b o l r e f e r e n c i a d o p o r " r “ a p a r t i r de l a p o s i c i ó n " p o s [ 0 ] " ,
// " b u s c a r " d e v u e l v e en d a t o s [ 0 ] l a r e f e r e n c i a a e s o s d a t o s :
// en o t r o c a s o , d e v u e l v e n u i l .
// L o s n o d o s s e c o n s i d e r a n n u m e r a d o s ( 0 . 1. 2. . . . ) s e g ú n
// e l o r d e n en e l q u e s o n a c c e d i d o s p o r e l m ét o d o " i n o r d e n " .
CNodo a c t u a l = r ;

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

public Object b u sc a r(0 b je c t obj)


I
return buscar(obj, 0):

p u b lic Object b u scar(O b ject obj. int posición)


I
O b j e c t t ] d a t o s = ( n u i l ):
i n t [ ] pos = I p o s i c i ó n ) ;
buscar(obj. ra íz , datos, pos):
return d a to stO ]:
CA PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 6 3

public void inorden( CNodo r . boolean nodoRaiz )


I
// El m ét od o r e c u r s i v o i n o r d e n v i s i t a l o s n o d o s d e l á r b o l
// u t i l i z a n d o l a f o r m a i n o r d e n : e s t o e s , p r i m e r o s e v i s i t a
// el s u b á r b o l i z q u i e r d o , d e s p u é s s e v i s i t a l a r a í z , y p o r
// ú l t i m o , e l s u b á r b o l d e r e c h o .
// 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 .
CNodo a c t u a l = n u i l ;

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 .

/////////////////////////////////////////////////////////////////

El proceso de construcción lo lleva a cabo el m étodo recursivo denom inado


construir A rb o l, el cual construye un árbol de n nodos (éste, es a su vez invocado
por construirA rbolE quilibrado). El prototipo de este m étodo es:

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.

El m étodo privado bu sca r tam bién se ha declarado com o un m étodo recursi­


vo. P erm ite acceder a unos datos determ inados, com enzando la búsqueda desde
cualquier nodo. Para facilitar la labor del usuario de la clase, se ha añadido a la
interfaz pública de la m ism a dos sobrecargas de este m étodo: una con un parám e­
564 JA V A: C U R S O DE PRO G R A M A CIÓ N

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.

Utilización de la clase CArboIBinE


L a clase C ArboIBinE es una clase abstracta; p o r lo tanto, para h acer uso del so­
porte que proporciona para la construcción y m anipulación de árboles binarios
perfectam ente equilibrados, tendrem os que derivar una clase de ella y redefinir ios
m étodos abstractos heredados: leerD atos, com parar, p ro ce sa r y visitarlnorden.
La redefm ición de estos m étodos está condicionada a la clase de objetos que for­
m arán parte del árbol.

C om o ejem plo, vam os a construir un árbol binario perfectam ente equilibrado


en el que cada nodo haga referencia a un objeto de la clase C D atos y a utilizada
anteriorm ente en este m ism o capítulo.

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.

S egún lo expuesto, la clase C A rbolB inarioE quilibrado derivada de CAr-


bolB inE puede ser de la form a siguiente:

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

/////////////////////////////////////////////////////////////////

C uando se declare un objeto de la clase C A rbolB inario E quilibrado, el cons­


tructor de esta clase invoca al constructor C ArboIBinE de su clase base, que creará
un árbol vacío (ra íz = nuil). El atributo raíz apunta siem pre a la raíz del árbol.

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:

1. C reará un objeto arbolbe de la clase C ArbolBinarioEquilibrado.


2. C onstruirá el árbol equilibrado de n nodos, enviando al objeto arbolbe el men­
saje construirArbolEquilibrado.
3. Buscará un determ inado nodo enviando al objeto arbolbe el mensaje buscar.
4. Finalm ente, 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 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 í );

CLASES RELACIONADAS DE LA BIBLIOTECA JAVA


Jav a soporta diferentes grupos de objetos, entre los que cabe destacar de forma
genérica los siguientes:

• C ollection. U na colección no tiene un orden especial y perm ite claves dupli­


cadas.
• List. U na lista está ordenada y perm ite claves duplicadas. En unos casos los
elem entos se colocan en el orden en el que son añadidos y en otros, los mis­
m os elem entos asum en un orden natural.
• Set. U n conjunto no tiene un orden especial pero no perm ite claves duplica­
das.
• M ap. Un m apa utiliza un conjunto de claves no duplicadas (un índice) para
acceder a los datos alm acenados.

Pues bien, adem ás de la clase L in k e d L is t para trabajar con listas enlazadas,


la biblioteca de Java proporciona en su paquete ja v a .u til las clases TreeSet.
T r e e M a p , H a sh S e t, H a s h M a p y H a sh T a b le .

La clase T re e Se t proporciona un conjunto ordenado, utilizando para el alma­


cenam iento de los datos un árbol. Los elem entos deben ten er un orden asociado
(saber qué elem ento sigue a cuál) im plem entando la interfaz C o m p a r a b le o pro­
porcionando u na clase C o m p a r a t o r para efectuar las com paraciones.
C A PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 6 7

L a clase T re e M a p proporciona un m apa ordenado, utilizando para el alm ace­


nam iento de los datos un árbol. Igual que la clase T re eS et, los elem entos deben
tener un orden asociado.

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

La clase C ListaLinealSE O debe incluir los atributos:

private CElemento p = nuil: // ele m en t o de c a b e c e ra


private CElemento elem Anterior - n u il ; // elemento a n t e r i o r
private CElemento ele mActual - n u i l : // elemento actual

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:

public a b stra c t in t comparartObject o b j l. O bject o b j 2):


public boolean l i s t a V a c i a O
public Object bu scartO bject obj)
public void a ña d irtO b je ct obj)
public Object b o rra r íO b je c t obj)
public Object o b te n e rP rim e ro ()
public Object o b t e n e r S ig u ie n t e ( )

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 a ñ a d ir inserta un elem ento en la lista en orden ascendente de una


clave seleccionada del área de datos. T iene un parám etro q u e es una referencia de
tipo O b je c t al objeto a añadir. N o devuelve nada.

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.

El m étodo obtenerSiguiente devuelve una referencia al área de datos del ele­


m ento siguiente al actual, o bien n u il si la lista está vacía.

S egún el enunciado, la clase C ListaLinealSE O puede ser com o se m uestra a


continuación:

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

// E l e m e n t o de una l i s t a lineal simplemente enlazada


C A PÍTU LO 13: ESTR U C TU R A S D IN Á M ICA S 5 6 9

prívate class 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 C E l e m e n t o s i g u i e n t e : // s i g u i e n t e e l e m e n t o
II M é t o d o s
p r i v a t e C E l e m e n t o ! ) 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 f O b j e c t d . C E l e m e n t o 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;

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

public boolean U staV aciaO


I
r e t u r n p = = n u l 1;

p u b lic Object b u scar(O b ject obj)


I
// B u s c a r un e l e m e n t o d e t e r m i n a d o en una l i s t a o r d e n a d a .
II El m ét o d o a l m a c e n a en e l e m A c t u a l l a r e f e r e n c i a del
// e l e m e n t o b u s c a d o y en e l e m A n t e r i o r l a r e f e r e n c i a d e l
// e l e m e n t o a n t e r i o r .
e le m A n t e r i o r = ele m Actual - n u i l :

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

public void añ a d ir(O b je c t obj)


I
// A ñ a d i r un e l e m e n t o en o r d e n a s c e n d e n t e s e g ú n una c l a v e
II p r o p o r c i o n a d a p o r o b j .
C E l e m e n t o q = new C E l e m e n t o í o b j , n u i l ) ; // c r e a r e l e l e m e n t o

// S i l a l i s t a r e f e r e n c i a d a por p e stá vacia, añadirlo s i n más


if ( listaV aciaO )
I
II A ñ a d i r e l prim er elemento
P - q:
e le m A nte rio r = elemActual = p; // a c t u a l i z a r referencias
return;

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

public Object b o rra r(O b je c t obj)


I
// B o r r a r un d e t e r m i n a d o e l e m e n t o .
// S i l a l i s t a e s t á v a c i a , r e t o r n a r ,
i f ( li s t a V a c i a O ) return n u il;

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

Object borrado = elem Actual.datos:


e l e m A c t u a l = e l e m A c t u a l . s i g u i e n t e : // a c t u a l i z a r r e f e r e n c i a
r e t u r n b o r r a d o : // r e t o r n a r el e l e m e n t o b o r r a d 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 b o r r a d o s e r á e n v i a d o a l a
// b a s u r a a l q u e d a r d e s r e f e r e n c i a d o , p o r t r a t a r s e de una
// v a r i a b l e l o c a l .

p u b lic Object o b te n e r P rim e ro í)


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 p rim e r elemento.
// 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 ( lis t a V a c ia í) ) return n u il:
e l e m A c t u a l = e l e m A n t e r i o r - p:
return p.datos:

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:

public class CDatos


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 riv a t e double n o t a :
// M é t o d o s
public CD atosO II // 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 D a t o s t S t r i n g 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;
572 JA V A : C U R SO D E PROGRAM A CIÓN

public void a s i g n a r N o m b r e ( S t r i n g nom)

nombre = nom;

public String obtenerNombre( )

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;

Pero, para utilizar la clase abstracta C ListaLinealSE O tenem os que derivar de


ella otra clase, p o r ejem plo C ListaLinealSE O rdenada, que redefina el m étodo
com parar para que perm ita com parar dos objetos C D atos p o r el atributo n om bre:

public class CListaLinealSEO rdenada extends C ListaLin e alSE O


1
// P e r m i t e c o m p a r a r d o s e l e m e n t o s d e l a l i s t a por
// e l a t r i b u t o no mbre.
p u b lic in t comparartObject o b j l. Object obj2)
I
String s t r i = 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 ( ) ) ;
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 s t r i . c o m p a r e T o í s t r 2 );

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 ;

public static void m a in (S trin g [] args)


I
// C r e a r un a lista lineal vacia

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

D e form a resum ida, el program a realizará las siguientes operaciones:

a) L eerá un dato, operando u operador, y lo alm acenará en la variable oper.


b) A nalizará o p er; si se trata de un operando lo m ete en la pila y si se trata de un
operador saca los dos últim os operandos de la pila, realiza la operación indi­
cada por dicho operador y m ete el resultado en la pila para poder utilizarlo
com o operando en una posible siguiente operación.

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.

El program a com pleto se m uestra a continuación:

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

public static void obtenerOperandosi)


I
i f ( p i l a . t a m a ñ o ( ) < 2 ) t h r o w new N u l l P o i n t e r E x c e p t i o n C );
o p e r a n d o [ l ] = ( ( D o u b le ) p i 1a . s a c a r D e P i 1a ( ) ) . d o u b l e V a l u e ( );
o p e r a n d o [ 0 ] = ( ( D o u b l e ) p i 1 a . s a c a r D e P i 1 a ( ) ) . d o u b l e V a l u e ( );
1

public static void m a in (S trin g [] args)


I
// o p e r a l m a c e n a l a entrada realizada desde el teclado
S t r i n g oper = n u il :

System .out.p rin tln ("O p e r a c io n e s : + - * / \ n ” ):


CA PÍTU LO 13: ESTRU C TU R A S DINÁM ICA S 5 7 5

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

java Palabras fichero_de_texto

donde fich ero _ d e_ texto es el nom bre del fichero de texto del cual deseam os obte­
ner la estadística.

El proceso de contabilizar las palabras que aparezcan en el texto de un deter­


m inado fichero, lo podem os realizar de la form a siguiente:

a) Se lee la inform ación del fichero y se descom pone en palabras, entendiendo


por palabra una secuencia de caracteres delim itada p o r espacios en blanco, fa­
buladores, signos de puntuación, etc.

b) C ada palabra deberá insertarse p o r orden alfabético ascendente ju n to con un


contador que indique su núm ero de apariciones, en el nodo de una estructura
en árbol. E sto facilitará la búsqueda.

c) U na vez construido el árbol de búsqueda, se presentará p o r pantalla una esta­


d ística con el siguiente form ato:

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

Total palabras diferentes: 35

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

public class CDatos


I
// A t r i b u t o s
p riv a t e S t r in g palabra:
p riv a t e in t contador:

// 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 CDatostString pal) // c o n s t r u c t o r c o n un p a r á m e t r o


I
palabra » p a l :
c o n t a d o r = 0:
1

public CDatostString pal. int cont) // c o n s t r u c t o r c o n d o s p a ra m s


I
palabra = p a l :
contador = cont:
I

public void a s ig n a r P a la b r a íS t r in g pal)


I
palabra = p a l :
I

public String obtenerPalabra()


I
return palabra;
1

public void a sig n a rC o n ta d o r(in t cont)


I
contador = cont:
I

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

El árbol de búsqueda que tenem os que construir será un objeto de la clase


C ArbolBinarioD eBusqueda derivada de C ArbolBinB. R ecuerde que la clase CAr­
bolB inB fue im plem entada anteriorm ente en este m ism o capítulo, al hablar de ár­
boles binarios d e búsqueda. La razón de por qué derivam os un clase de
C ArbolBinB es porque esta clase es abstracta, con la intención de redefinir los
m étodos que procesan inform ación contenida en el área de datos referenciada por
cada nodo, que en nuestro caso se corresponde con objetos CDatos.

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

/////////////////////////////////////////////////////////////////

Se puede observar que el m étodo p ro cesa r de la clase C ArbolBinarioD eBus­


queda, adem ás de visualizar la inform ación alm acenada en el objeto C D atos refe-
CA PÍTU LO 13: ESTR U C TU R A S DINÁM ICA S 5 7 9

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 m a in verifica que, cuando se ejecute la aplicación, se haya pasado


com o parám etro el nom bre del fichero de texto, invoca al m étodo leerFichero y
una vez construido el árbol, lo recorre para visualizar los resultados pedidos.

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

El código com pleto de la aplicación que hem os denom inado P alabras, se


m uestra a continuación:

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

public static void palabras(String linea)


I
// D e s c o m p o n e r l i n e a en p a l a b r a s
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 ( 1 1 n e a , “ , ; . : \ n \ r \ t \ f " ) ;

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

while ( ca d e n a .ha sM o reT o kenst))


I
p a l a b r a - c a d e n a . n e x t T o k e n ( );
i f ((o b j = (C D ato s)a rb o lb b .b usca r(n ew CDatos(pal a b r a ) ) ) — nuil)
á r b o l b b . i n s e r t a r t n e w C D a t o s ( pal a b r a . 1 ) ) ;
el se
o b j . a s i g n a r C o n t a d o r ( o b j . o b t e n e r C o n t a d o r ( ) + l );

public static void 1e e r F i c h e r o t S t r i n g nombrefich)


I
// D e f i n i c i o n e s de v a r i a b l e s
F i l e f i c h F u e n t e = new F i l e ( n o m b r e f i c h ) ;
BufferedReader flu jo E = n u il;

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;

while ((linea = f 1uj oE . r e a d L i n e ( )) != n u i l )


[
// S i s e a l c a n z ó e l f i n a l del fichero.
// r e a d L i n e d e v u e l v e n u i l
p a l a b r a s ( 1 i n e a );

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

public static void m a in (S trin g [] args)


I
// mairi d e b e r e c i b i r un p a r á m e t r o : el no mb r e d e l fichero
// j a v a P a l a b r a s p a l a b r a s . t x t

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

á rb olb b.visitarlnorde ní):


S y s t e m . e r r . p r i n t l n í );
S y s t e m . e r r . p r i n t í n ( " T o t a l p a l a b r a s : " + á r b o l b b . t o t a l P a l a b r a s );
S y ste m .e rr .p r in tln í"T o ta l palabras d ife re n te s: " +
á r b o l b b . t o t a 1Pal a b r a s O i f e r e n t e s ) :

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:

2 x \ ' - xyJ + 8.25 m ás 5xsy - 2x[y + 7x2 - J igual a 5 r y + 7x2 - x y s + 5.25

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.

La clase C Term ino ya fue desarrollada en el apartado “ejercicios resueltos”


del capítulo 10. Tam bién se im plem ento una clase CPoiinom io. E sta clase debe
ser ahora reem plazada por una lista lineal sim plem ente enlazada ordenada.
582 JA V A: C U R SO D E PROGRAM A CIÓN

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 uando se m uestre la inform ación alm acenada en el árbol, el listado de los


nom bres y sus correspondientes notas debe aparecer en orden ascendente. Por de­
finición de árbol de búsqueda, para todo nodo, las claves m enores que la del pro­
pio nodo form an el subárbol izquierdo y las m ayores, el subárbol derecho. Según
esto, la clave m enor se encuentra en el nodo m ás a la izquierda y la clave m ayor
en el nodo m ás a la derecha del árbol. P or lo tanto, para visualizar los nom bres en
orden ascendente tendrem os que recorrer el árbol en inorden. Entonces, si pensa­
m os en el proceso inverso, esto es, si partim os de un fichero con las claves orde­
nadas y construim os un árbol perfectam ente equilibrado tenem os que utilizar la
form a inorden para conseguir al m ism o tiem po que el árbol sea de búsqueda. Es
decir, el m étodo que cree el árbol debe incluir los siguientes procesos en el orden
m ostrado:

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:

java Ordenar fic h e r o _ d e _ t e x t o [-r]

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.

L a recursión es un proceso extrem adam ente potente, pero consum e m uchos


recursos, razón por la que la analizarem os detenidam ente, para saber cuándo y
cóm o aplicarla. D e este análisis deducirem os que aunque un problem a por defini­
ción sea recursivo, no siem pre será el m étodo de solución m ás adecuado.

En las aplicaciones prácticas, antes de poner en m archa un proceso recursivo


es necesario dem ostrar que el nivel m áxim o de recursión, esto es. el núm ero de
veces que se va a llam ar a sí m ism o, es no sólo finito, sino realm ente pequeño. La
razón es que se necesita cierta cantidad de m em oria para alm acenar el estado del
proceso cada vez que se abandona tem poralm ente, debido a una llam ada para eje­
cutar otro proceso que es él m ism o. El estado del proceso de cálculo en curso hay
586 J A V A : C U R S O D E P R O G R A M A C IÓ N

que alm acenarlo para recuperarlo cuando se acabe la nueva ejecución del proceso
y haya que reanudar la antigua.

En térm inos de un lenguaje de program ación, un m étodo es recursivo cuando


se llam a a sí mismo.

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)

El seudocódigo que especifica cóm o solucionar este problem a aplicando la


recursión, es el siguiente:

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

A continuación presentam os este m étodo com o parte de una clase C Recur-


sio 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

public class Test


I
public static void m a in (S trin g [] args)
I
i n t m. n . a ;
S y s t e m . o u t . p r i n t l n ( " C á l c u l o de A ( m , n ) = A ( m - l , A ( m , n - l ) ) \ n ” ):
S y s t e m . o u t . p r i n t C ' V a l o r d e m: " ) ; m = L e e r . d a t o í n t C );
S y s t e m . o u t . p r i n t ( " V a l o r d e n: " ) : n = L e e r . d a t o I n t ( ):
a = CRecursion.Ackerm an(m .n);
System .out.p rintln("\nA(" + m + + n + ” ) = " + a);

Supongam os ahora que nos planteam os el problem a de resolver el m étodo de


A ckerm an, pero sin aplicar la recursión. Esto nos exigirá salvar las variables ne­
cesarias del proceso en curso, cada vez que el m étodo se llam e a sí m ism o, con el
fin de poder reanudarlo cuando finalice el nuevo proceso invocado.

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.

El seudocódigo para este m étodo puede ser el siguiente:

< 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

A continuación presentam os el código correspondiente a este m étodo que


hem os denom inado A ckernum N R . D icho m étodo se ha incluido en la interfaz de
la clase CRecursion anterior y utiliza la clase CPila, im plem entada en el capítulo
de “Estructuras dinám icas” , para crear una pila que alm acene los valores m y n
cada vez que es invocado para una nueva ejecución.

public static i n t A c k e r m a n N R t i n t m. int n)


I
C P i l a p i l a = new C P i l a O ; // p i l a de e l e m e n t o s (m.n)
CDatos dato:
i n t Ac k e r ma n _ m_ n = 0:
p ila . m e te rE n P ila tn e w CDatosím, n ) ) :
w hile (true)
1
// Tomar l o s d a t o s de l a c i m a d e 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 ( ):
m = d a t o . o b t e n e r M ( ):
n - d a t o . o b t e n e r N í );
i f (m “ 0 ) // A c k e r m a n ( O . n ) = n+1
(
Ac k e r ma n _ m_ n = n + 1 ;
i f ( p i l a . t a m a ñ o t ) ! - 0)
1
// S a c a r m y n d e l a p i l a
d a t o - ( C D a t o s ) p i 1 a . s a c a r D e P i 1a ( ) :
m = d a t o . o b t e n e r M ( );
n = d a t o . o b t e n e r N Í );
// M e t e r m y Ac k e r ma n _ m_ n en l a p i l a
p i l a . m e t e r E n P i l a í n e w C D a t o s t m . A c k e r m a n _ m _ n ) ):
I
el se
r e t u r n Ackerman_m_n:
I
e l s e i f (n ~ 0 ) // A c k e r m a n í m - 1 , 1 )
// M e t e r m-1 y 1 en l a p i l a
pila.m eterEnPila(new CDatosdn-l. 1)):
e l s e // A c k e r m a n í m - 1 , A c k e r m a n ( m , n - 1 ) )
I
II M e t e r m-1 y Ac k e r ma n _ m_ n en l a p i l a
p i 1a . m e t e r E n P i 1 a ( n e w C D a t o s ( m - l . A c k e r m a n _ m _ n ) ) :
// M e t e r m y n -1 en l a p i l a
p i l a . m e t e r E n P i l a ( n e w CDatostm, n - 1 ) ) :

Según se puede observar, los valores de m y n son encapsulados p o r objetos


de la clase C D atos definida así:
CA PÍTU LO 14: A LG O RITM O S 5 8 9

public class CDatos


I
// A t r i b u t o s
p r i v a t e i n t m. 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

public int obtenerM()


I
r e t u r n m;
I

public int obtenerN()


I
r e t u r n n;

Un proceso en el que es realm ente eficaz aplicar la recursión es el problem a


de las torres d e H anoi. Este problem a consiste en tres barras verticales A , B y C y
n discos, de diferentes tam años, apilados inicialm ente sobre la barra -4, en orden
de tam año decreciente.

A B C

l 1
I ]

El objetivo es m over los discos desde la barra A a la C, conservando su orden,


bajo las siguientes reglas:

1. Se m overá un sólo disco cada vez.


2. Un disco no puede situarse sobre otro m ás pequeño.
3. Se utilizará la barra B com o pila auxiliar.

U na posible solución, es el algoritm o recursivo que se m uestra a continua­


ción:

1. M over n-1 discos de la barra A a la B (el disco n es el del fondo).


590 JA V A : C U R SO D E PROGRAM A CIÓN

2. M over el disco n de la barra A a la C, y


3. M over los n-1 discos de la barra B a la C.

R esum iendo estas condiciones en un cuadro obtenem os:

n° discos origen otra torre destino


inicialmente n A B C
1 n-1 A C B
2 1 A B C
3 n-1 B A C

El m étodo a realizar será m over n discos de origen a destino :

m over(n_diseos, origen, otratorre, destino):

El seudocódigo para este program a puede ser el siguiente:

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

A continuación presentam os el m étodo correspondiente a este problem a. El


resultado será los m ovim ientos realizados y el núm ero total de m ovim ientos.

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:

public static in t mover(int n_discos, c h a r a. c h a r b, c h a r c)


I
if (n_discos > 0)
1
m o v e r ( n _ d i s c o s - 1. a . c . b ) :
System .out.println("m over d isco de " + a + " a " + c ) ;
movimientos++;
m o v e r ( n _ d i s e o s - 1 , b. a . c ) ;
)
return movimientos:

Para probar cóm o funciona este m étodo escribim os la aplicación siguiente:


CA PÍTU LO 14: ALGORITM OS 5 9 1

public class Test


I
public static void m a in ( S t r in g [ ] args)
I
in t n _ d isc o s. movimientos;
S y s t e m . o u t . p r i n t ( " N ú m e r o de d i s c o s : " ) ;
n _ d i s c o s = L e e r . d a t o l n t í );
m o v i m i e n t o s = C H a n o i . m o v e r ( n _ d i s e o s , ’ A ’ . ' B ' . ’ C ’ ):
S y ste m .o u t.p rin tln ("\n m o v im ie n to s efectuados: " + movimientos);

Si ejecuta la aplicación anterior para n_discos = 3, el resultado será el si­


guiente:

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

movimientos efe ctuados: 7

C om o ejercicio se propone realizar el m étodo m o ver sin u tilizar recursión.

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

Podem os agrupar los m étodos de ordenación en dos categorías: ordenación de


m atrices u ordenación interna (cuando los datos se guardan en m em oria interna) y
ordenación de ficheros u ordenación externa (cuando los datos se guardan en me­
m oria externa: generalm ente en discos).

En este apartado no se trata de analizar exhaustivam ente todos los m étodos de


ordenación y ver sus prestaciones de eficiencia, rapidez, etc. sino que sim ple­
m ente analizam os desde el punto de vista práctico los m étodos m ás com unes para
ordenación de m atrices y de ficheros.

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.

V eam os a continuación el algoritm o correspondiente a este m étodo para or­


den ar una lista de m enor a m ayor, partiendo de que los datos a ordenar están al­
m acenados en una m atriz de n elem entos:

1. C om param os el prim er elem ento con el segundo, el segundo con el tercero, el


tercero con el cuarto, etc. C uando el resultado de una com paración sea
“m ayor que” , se intercam bian los valores de los elem entos com parados. Con
esto conseguim os llevar el v alor m ayor a la posición n.

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.

4. La ordenación estará realizada cuando al repetir el iésim o proceso de com pa­


ración no haya habido ningún intercam bio o, en el peor de los casos, después
de rep etir el proceso de com paración descrito n-1 veces.

El seudocódigo para este algoritm o puede ser el siguiente:

< m é t o d o o r d e n d r ( m d t r i z "a" d e "n" e l e m e n t o s )>


[ " a " es un m a t r i z c u y o s e l e m e n t o s son a 0. a¡, .... a„.¡]
n - n-1
D O U H I L E ("a" no e s t é o r d e n a d o y n > 0 )
i - 1
D O h/HILE ( i < = n )
IF ( a[i -1] > a [ i ] ) THEN
p e r m u t a r a [ i - l ] con a l i]
CA PÍTU LO 14: A LG O RITM O S 5 9 3

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:

while ( s && ( - - n ú m e r o _ d e _ e l e m e n t o s >0))


I
s = f a l s e : // no p e r m u t a c i ó n
f o r ( i = 1; i < = n ú m e r o _ d e _ e l e m e n t o s : i + + )
// ¿ e l e l e m e n t o ( i - 1 ) e s m a y o r que el ( i ) ?
i f ( m [ i - 1 ] > m[ i ] )
I
// p e r m u t a r l o s elementos ( i -1) e (i)
a u x = m[ i - 1 ] ;
m[i -1] = m[ i ];
m[ i ] = a u x ;
s = t r u e : // p e r m u t a c i ó n
)

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:

while ( s && ( - - n ú m e r o _ d e _ e l e m e n t o s > 0))


I
s = f a l s e : // n o p e r m u t a c i ó n
f o r (i = 1 : i <= núm ero_de_elementos: i+ +)
// ¿ e l e l e m e n t o ( i - 1 ) e s m a y o r q u e e l ( i ) ?
594 JA V A : C U R SO D E PROGRAM A CIÓN

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.

C uando se analiza un m étodo d e ordenación, hay que determ inar cuántas


com paraciones e intercam bios se realizan para el caso m ás favorable, para el caso
m edio y para el caso m ás desfavorable.

En el m étodo de la burbuja se realizan (n -l)(n /2 )= (n 2-n)/2 com paraciones en


el caso m ás desfavorable, donde n es el núm ero de elem entos a ordenar. P ara el
caso m ás favorable (la lista está ordenada) el núm ero de intercam bios es 0. Para el
caso m edio es 3 (n 2-n)/4; hay tres intercam bios por cada elem ento desordenado. Y
para el caso m enos favorable, el núm ero de intercam bios es 3(n -n)/2. El análisis
m atem ático que conduce a estos valores, queda fuera del propósito de este libro.
El tiem po de ejecución es un m últiplo de n2 y está directam ente relacionado con el
núm ero de com paraciones y de intercam bios.

La siguiente aplicación ordena una m atriz d o u b le y otra de tipo S trin g utili­


zando el m étodo ordenar de la clase CM atriz.

public class Test


I
public static void m a in ( S t r in g [] args)
I
// M a t r i z n u m é r i c a
double[] m - 13.2.1.5.41;
CMatriz.ordenar(m);
f o r ( i n t i = 0: i < m . l e n g t h ; i + + )
System.out.p r i n t (m [ i ] + " ");
S y s t e m . o u t . p r i n t l n ( ):

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

El seudocódigo para este algoritm o puede ser el siguiente:

< m é t o d o i n s e r c i ó n t m a t r i z "a" d e "r" e l e m e n t o s ) >


[ “a " es un m a t r i z c u y o s e l e m e n t o s s o n a0 . a a n.¡]
i - 1
DO UH1LE ( i < n )
x “ a [i]
i n s e rt ar x e n la p o s i c i ó n c o r r e c t a e n t r e a 0 y a t
E N DDO
END <inserción>

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:

public static void insercion(double[] m)


I
i n t i . k, n_elementos = m .length:
d o u b l e x:
596 JA V A : C U R S O D E P R O G R A M A C IÓ N

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

A nálisis del m étodo de inserción directa:

com paraciones intercam bios


caso m ás fa vo ra b le n-1 2 (n -l)
caso m edio (n 2 +n-2)/4 (n 2 + 9 n -10)/4
caso m enos favorable (n2 + n )/2 -1 (n 2 +3n-4)/2

Para el m étodo de inserción, el tiem po de ejecución es función de n2 y está


directam ente relacionado con el núm ero de com paraciones y de intercam bios.

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:

1. Se selecciona un valor perteneciente al rango de valores de la m atriz. Este


valor se puede escoger aleatoriam ente o haciendo la m edia de un pequeño
conjunto de valores tom ados de la m atriz. El valor óptim o sería la m ediana (el
valor que es m enor o igual que los valores correspondientes a la m itad de los
elem entos de la m atriz y m ayor o igual que los valores correspondientes a la
otra m itad). N o obstante, incluso en el peor de los casos (el valor escogido
está en un extrem o), quicksort funciona correctam ente.

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

El proceso descrito es esencialm ente recursivo. Según lo expuesto, el seudo-


código para este algoritm o puede ser el siguiente;

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

A continuación se m uestra una versión de este algoritm o, que selecciona el


elem ento m edio de la m atriz para proceder a dividirla en dos partes. Esto resulta
fácil de im plem entar, aunque no siem pre da lugar a una buena elección. A pesar
de ello, funciona correctam ente.

public static void q u ic k s o r t ( S t r in g [ ] m)


I
qs(m. 0. m . l e n g t h - 1);
1

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

while ( m[ i z q ] . c o m p á r e l o ( m i t a d ) < 0 && i z q < s u p ) izq++:


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

no serían necesarias. En cam bio, si el valor m itad es un valor no coincidente con


un elem ento de la lista, pero que está dentro del rango de valores al que pertene­
cen los elem entos de la m ism a, esas condiciones son necesarias para evitar q ue se
puedan sobrepasar los lím ites de los índices de la m atriz.

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

w hile ( m[ i z q ] < m i t a d && izq < sup) izq++;


w hile ( m i t a d < m [ d e r ] && der > i n f ) d e r - - ;

En el m étodo quicksort, en el caso m ás favorable, esto es, cada vez se selec­


ciona la m ediana obteniéndose dos particiones iguales, se realizan n.log n com pa­
raciones y n/ó.log n intercam bios, donde n es el núm ero de elem entos a ordenar;
en el caso m edio, el rendim iento es inferior al caso óptim o en un factor de 2.log 2;
y en el caso m enos favorable, esto es, cada vez se selecciona el valor m ayor obte­
niéndose una partición de n-1 elem entos y otra de un elem ento, el rendim iento es
del orden de n.n= n2. C on el fin de m ejorar el caso m enos favorable, se sugiere
elegir, cada vez, un valor aleatoriam ente o un valor que sea la m ediana de un pe­
queño conjunto de valores tom ados de la m atriz.

El m étodo qs sin utilizar la recursión puede desarrollarse de la form a si­


guiente:

public static void q u ic k s o rt N R fS t rin g [] m)


I
qs NR ( m, 0. m.length - 1);
I

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

public class CDatos


1
// A t r i b u t o s
p r i v a t e i n t i n f , sup;
// M é t o d o s
public CDatostint i. int s) // c o n s t r u c t o r con p a r á m e t r o s
(
inf - i ;
sup - s ;
600 JA V A : C U R S O D E P R O G R A M A C IÓ N

public int obtenerlnfO


I
return inf;
1

public int obtenerSupO


I
return sup;

Comparación de los métodos expuestos


Si m edim os los tiem pos consum idos p o r los m étodos de ordenación estudiados
anteriorm ente, observarem os que el m étodo de la burbuja es el peor de los m éto­
dos; el m étodo de inserción directa m ejora considerablem ente y el m étodo qui-
cksort es el m ás rápido y m ejor m étodo de ordenación de m atrices con diferencia.

BÚSQUEDA DE DATOS

El objetivo de ordenar un conjunto de objetos es, generalm ente, facilitar la bús­


queda de uno o m ás elem entos pertenecientes a un conjunto, aunque es posible
realizar dicha búsqueda sin que el conjunto d e objetos esté ordenado, pero esto
trae com o consecuencia un m ayor tiem po de proceso.

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.

El seudocódigo para este m étodo de búsqueda puede ser el siguiente:

< m é t o d o b ú s q u e d a _ S ( m a t r i z a. val or q u e q u e r e m o s b u s c a r ) >


i - 0
DO U H I LE ( no encont ra d o )
IF ( va lo r = a [ i ] )
encontrado
ENDIF
i = 1+1
FNDDO
END <búsqueda_S>
C A PÍTU LO 14: A LG O RITM O S 6 0 1

C om o ejercicio, escribir el código correspondiente a un m étodo que perm ita


buscar un valor previam ente leído, en un matriz.

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.

El seudocódigo para este algoritm o puede ser el siguiente:

< m é t o d o b ú s q u e d a B i n l m a t r i z a. valo r q u e q u e r e m o s b u s c a r )>


D O U H I L E ( e x i s t a un i n t e r v a l o d o n d e b u s c a r )
x « e l e m e n t o m i t a d del i n t e r v a l o d e b ú s q u e d a
IF ( v a l o r > x ) THEN
b u s c a r "valor" en la s e g u n d a m i t a d del i n t e r v a l o de b ú s q u e d a
E LSE
b u s c a r " v al or " e n la p r i m e r a m i t a d del i n t e r v a l o de b ú s q u e d a
ENDIF
ENDDO
IF ( se e n c o n t r ó v a l o r ) THEN
r e t o r n a r su Índice
ELSE
r e t o r n a r -1
ENDIF
END <búsquedaBin>

A continuación se m uestra el código correspondiente a este m étodo.

public static int búsquedaBin(double[] m, d o u b l e v)


I
// El m ét od o b ú s q u e d a B i n d e v u e l v e como r e s u l t a d o l a p o s i c i ó n
// d e l v a l o r . S i e l v a l o r no s e l o c a l i z a d e v u e l v e - 1 .

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;

En el algoritm o de B oyer y M oore la com paración se realiza de derecha a iz­


quierda, em pezando desde el principio del texto. Es decir, se em pieza com parando
el últim o carácter de la cadena que se busca con el correspondiente carácter en el
texto donde se busca; si los caracteres no coinciden, la cadena que se busca se
desplaza hacia la derecha un núm ero de caracteres igual al valor indicado p o r la
entrada en la tabla delta correspondiente al carácter del texto que no coincide. Si
CA PÍTU LO 14: A LG O RITM O S 6 0 3

el carácter no aparece en la cadena que se busca, su valor delta es la longitud de la


cadena que se busca.

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

d e lta [ ' c '] - 3


d e lta [' i ' ] = 2
d e l t a [* e ‘ ] = 1
d e lta t’n ’ ] - 0

El resto de las entradas valen 4 (longitud de la cadena). Según esto, la cadena


que se busca se desplaza cuatro posiciones a la derecha (el espacio en blanco no
aparece en la cadena que se busca).

Texto: Más v a l e un y a que cien después s e hará


C a de n a a buscar: cien

A hora, n no coincide con e\ luego la cadena se desplaza una posición a la d e­


recha {e tiene un valor asociado de uno).

Texto: Más v a l e un y a que cien después s e hará


C a de n a a b u s c a r : cien

n no coincide con espacio', se desplaza la cadena cuatro posiciones a la dere­


cha.

Texto: Más vale un y a que cien después se hará


C a de n a a b u s c a r : cien

n no coincide con y; se desplaza la cadena cuatro posiciones a la derecha.

Texto: Más v a l e .r¡ y a que c i e n d e s p u é s se hará


C a de n a a buscar: cien
604 JA VA: C U R SO D E PROGRAM A CIÓN

n no coincide con «; se desplaza la cadena cuatro posiciones a la derecha.

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

n no coincide con /; se desplaza la cadena dos posiciones a la derecha.

Texto: Más vale un y a que c i e n d e s p u é s s e hará


C aden a a buscar: cien

T odos los caracteres de la cadena coinciden con los correspondientes caracte­


res en el texto. P ara encontrar la cadena se han necesitado sólo 7+3 com paracio­
nes (7 hasta que se dio la coincidencia del carácter c\ más 3 para verificar que
coincidían los 3 caracteres restantes). El algoritm o directo habría realizado 20+3
com paraciones, que en el peor de los casos, serían i * longC adB uscar, donde i es
la posición m ás a la izquierda de la prim era ocurrencia de la cadena a buscar en el
texto (20 en el ejem plo anterior, suponiendo que la prim era posición es la 1) y
longC adB uscar es la longitud de la cadena a buscar (4 en el ejem plo anterior). En
cam bio, el algoritm o B o y e r y M oore em plearía k * (i + longC adB uscar) com para­
ciones, donde k < 1.

El algoritm o B oyer y M oore es m ás rápido porque tiene inform ación sobre la


cadena que se busca, en la tabla delta. El carácter que ha causado la no coinciden­
cia en el texto indica cóm o m over la cadena respecto del texto. Si el carácter no
coincidente en el texto no existe en la cadena, ésta puede m overse sin problem as a
la derecha un núm ero de caracteres igual a su longitud, pues es un gasto de tiem po
com parar la cadena con un carácter que ella no contiene. C uando el carácter no
coincidente en el texto está presente en la cadena, el valor delta para ese carácter
alinea la ocurrencia m ás a la derecha de ese carácter en la cadena, con el carácter
en el texto.

A continuación se m uestra el código correspondiente al algoritm o B oyer y


M oore. El m étodo buscarC adena es el que realiza el proceso descrito. E ste m éto­
do devuelve la posición de la cadena en el texto o -1 si la cadena no se encuentra
(la prim era posición es la 0).

public s t a t ic int buscarCadena( S t r in g stexto, String scadena)


I
// B u s c a r una " c a d e n a " en un " t e x t o ”
c h a r [ ] t e x t o = s t e x t o . t o C h a r A r r a y ( ):
c h a r [ ] cadena = s c a d e n a . t o C h a r A r r a y ( );

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

ORDENACIÓN DE FICHEROS EN DISCO


Para ordenar un fichero, dependiendo del tam año del m ism o, podrem os proceder
de alguna de las dos form as siguientes. Si el fichero es pequeño, tiene pocos re­
gistros, se puede copiar en m em oria en una m atriz y utilizando las técnicas vistas
anteriorm ente, ordenam os dicha m atriz y a continuación copiam os la m atriz orde­
nada de nuevo en el fichero. Sin em bargo, m uchos ficheros son dem asiado gran­
des y no cabrían en una m atriz en m em oria, por lo que para ordenarlos
recurrirem os a técnicas que actúen sobre el propio fichero.
606 JA V A : C U R S O D F P R O G R A M A C IÓ N

Ordenación de ficheros. Acceso secuencial


El siguiente program a desarrolla un algoritm o de ordenación de un fichero utili­
zando el acceso secuencial, denom inado m ezcla natural. La secuencia inicial de
los elem entos viene dada en el fichero c y se utilizan dos ficheros auxiliares de­
nom inados a y b. C ada pasada consiste en una fa s e de distribución que reparte
equitativam ente los tram os ordenados del fichero c sobre los ficheros a y b. y una
fa s e que m ezcla los tram os de los ficheros a y b sobre el fichero c.

Este proceso se ilustra en el ejem plo siguiente. Partim os de un fichero c. Con


el fin de ilustrar el m étodo de m ezcla natural, separarem os los tram os ordenados
en los ficheros por un guión ( - ) .

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.

Según lo expuesto, el algoritm o de ordenación m ezcla natural podría ser así:

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

La estructura de la aplicación que perm ita ordenar un fichero utilizando el al­


goritm o descrito puede ser de la form a siguiente:

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

public static int d is t r ib u ir íF il e fuente. F i l e destinoA,


File d e st in o B ) throws IOException
I
// D i s t r i b u i r l o s t r a m o s o r d e n a d o s de fu en te e n t r e
II d e s t i n o A y d e s t i n o B

public static int m ezclaríFile fuenteA, F i l e fu enteB,


File destino) th rows IO E x c e p tio n
I
// F u s i o n a r o r d e n a d a m e n t e los t r a m o s de d e s t i n o A y d e s t i n o B
/ / e n fuente
608 JA V A: C U R SO DE PROGRAM A CIÓN

public s t a t ic void m a in (S trin g [] args)


I
f i l e n o m b r e F i c h e r o = new F i 1e ( a r g s [ 0 ] ) :
mezclaNatura1(nom breFichero);

La aplicación com pleta y com entada se m uestra a continuación.

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 u x - n u ll: fc.closeO : fa .closeO : fb.closeO ;


return nro_tramos;
I II d i s t r i b u i r

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

S trin g UneaDeFa. UneaDeFb. 11 n e a D e F a _ a n t , 1 1 n e a D e F b _ a n t ;


i n t n r o _ t r a m o s = 1;

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

else if (lineaDeFa = nuil)


I
f e . w r i t e ( 1 i n e a D e F b ) : f e . ne wLi n e ( );
w h i l e ( ( U n e a D e F b = f b. r e a d L i ne( ) ) != n u i l )
(
fc.w rite(líneaDeFb); fc.new LineO:

fc.close(); fa.closeO ; fb.closet):


return nro_tramos:
) // de m e z c l a r

public static void m a in ( S t r in g [ ] args)


I
// m a i n d e b e r e c i b i r un p a r á m e t r o : e l f i c h e r o a o r d e n a r ,
i f ( a r g s . 1 e n g t h ! = 1)
S y s t e m . e r r . p r i n t l n t " S i n t a x i s : java CMezclaNatural " +
" < n o m b r e _ f i c h e r o > " ):
el se
1
File n o m b r e F i c h e r o = new F i 1e ( a r g s [ 0 ] >:
try
I
// A s e g u r a r s e de q u e " n o m b r e F i c h e r o " e x i s t e y s e pu ede l e e r
i f ( ¡ n o m b r e F i c h e r o . e x i s t s í ) || ! n o m b r e 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 " N o e x i s t e e l f i c h e r o ” +
nom breFichero);
i f ( ¡ n o m b r e F ic h e r o . c a n R e a d ( ))
t h r o w new I O E x c e p t i o n ( " E l f i c h e r o " + n o m b r e F i c h e r o +
" no s e p u e d e 1e e r " );
m e z c l a N a t u r a l ( n o m b r e F i c h e r o ) ; // r e a l i z a r l a o r d e n a c i ó n
// M o s t r a r e l c o n t e n i d o d e l f i c h e r o
char resp:
S y s t e m . o u t . p r i n t ( " ¿ D e s e a v e r el c o n t e n i d o del f i c h e r o ? s /n : " ) :
r e s p = L e e r . c a r á c t e r ( ) : L e e r . 1 i m p i a r ( ):
i f ( resp == ' s ’ )
1
// A b r i r un f l u j o de e n t r a d a d e s d e n o m b r e F i c h e r o
// 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 í n e a a l í n e a .
F i 1e 1n p u t S t r e a m f i s = new F i 1 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 ) :
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 ):
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 ) :

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

El flujo B u ffe r e d R e a d e r (B u fF e re d ln p u tS tre a m cuando tratem os con bytes


en lugar de con caracteres; ver el capítulo 5) es uno de los m ás eficientes. Se deri­
va de R e a d e r y añade un b uffer que actúa com o una m em oria interm edia desde la
que el program a obtendrá los datos. C om o su tam año, generalm ente, es bastante
m ás grande que el tam año del bloque físico asociado con el dispositivo desde el
cual se obtiene la inform ación, el núm ero de accesos a este dispositivo por parte
de la aplicación dism inuirá, ya que el b uffer que se llenó cuando la aplicación
realizó una operación de lectura, no necesitará volverse a llenar m ientras la apli­
cación no procese los datos alm acenados en el m ism o. R esum iendo, la utilización
de un b u ffer repercute en una m ejora de la velocidad de ejecución y adem ás, de­
sacopla el tam año de las unidades de inform ación que requiere el program a, del
tam año de las unidades de inform ación asociadas con el dispositivo.

De la clase B u ffe re d R e a d e r. cabe destacar los m étodos: r e a d L in e que per­


m ite leer una línea de texto sin alm acenar el carácter delim itador de la m ism a
(norm alm ente \n, \r, o \r\n), m a r k que perm ite poner una m arca en la posición
actual del flujo y reset que perm ite reanudar la lectura desde la m arca m ás re ­
ciente.

A nálogam ente B u ffe r e d W r ite r (B u ffe r e d O u tp u tS tr e a m ) tam bién añade un


buffer que no será volcado al dispositivo asociado con el flujo hasta que no esté
lleno, dism inuyendo el núm ero de accesos al dispositivo, lo que repercute en una
m ejora de la velocidad de ejecución, adem ás de desacoplar el tam año de las uni­
dades de inform ación que requiere el program a, del tam año de las unidades de
inform ación asociadas con el dispositivo.

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

Ordenación de ficheros. Acceso aleatorio


El acceso aleatorio a un fichero, a diferencia del secuencial, perm ite ordenar la
inform ación contenida en el m ism o sin tener que copiarla sobre otro fichero, para
lo cual aplicarem os un proceso análogo al aplicado a las m atrices, lo que sim plifi­
ca enorm em ente el proceso ordenación. Esto quiere decir que los m étodos ex­
puestos para ordenar m atrices, pueden ser aplicados tam bién para ordenar ficheros
utilizando el acceso aleatorio.

C om o ejem plo, vam os a añadir 'á la clase C ListaTfnos de la aplicación reali­


zada en el apartado “U n ejem plo de acceso aleatorio a un fichero” del capítulo 12,
un m étodo denom inado quicksort para ordenar el fichero “lista de teléfonos” en-
capsulado p o r la m ism a, en el que cada registro estaba form ado p o r los campos:
nom bre, dirección y teléfono. L a ordenación del fichero la realizarem os por el
cam po nom bre, de tipo alfabético, em pleando el m étodo quicksort explicado ante­
riorm ente en este m ism o capítulo.

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

public C L ista T fn o stF ile fichero) th rows IO E x c e p tio n


I
// . . .

public void cerrar!) th rows IOException I fes.closeO ; I

public int longitud!) I return nregs; } // número de r e g i s t r o s

p u b lic boolean p o n e rV a lo rE n ( i n t i, CPersona ob je to )


th rows IO E x c e p tio n
1
// E s c r i b i r o b j e t o en e l r e g i s t r o de l a posición i
I

p u b lic CPersona valorEn! int i ) throws IOException


I
// O b t e n e r los d a t o s del registro i
C A PÍTU LO 14: ALGORITM OS 6 1 5

public void añadir(CPersona obj) th rows IOException

// A ñ a d i r un r e g i s t r o al final del fichero

public boolean el i m i n a r d o n g tel) th rows IOException

// E l i m i n a r e l registro e s p e c if i c a d o por tel

public int buscaríString str, int pos) throws IOException

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

private void q s í i n t inf, int sup) th rows IOException


I
in t izq = in f, d e r = sup:
S t r i n g mitad:

// 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 quicksort realiza la ordenación de los nregs registros del fichero


vinculado con el flujo fe s . Para ello invoca al m étodo recursivo qs.

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.

El m étodo cam po es llam ado p o r qs (quicksort) cada vez que es necesario


obtener el cam po nom bre, utilizado para realizar la ordenación, de un registro.

C om o ejercicio, puede añadir al m enú presentado por la aplicación Test a la


que nos hem os referido anteriorm ente, que creaba una lista de teléfonos a partir de
la clase C ListaTfnos, una opción m ás, ordenar, que perm ita o rdenar 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 " ) :
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.

Por lo n g itud d e búsqueda se entiende el núm ero de accesos que es necesario


efectuar sobre una m atriz para encontrar el elem ento deseado.
C A PÍTU LO 14: ALGORITM OS 6 1 7

Este m étodo de búsqueda perm ite, com o operaciones básicas, adem ás de la


búsqueda de un elem ento, insertar un nuevo elem ento y elim inar un elem ento
existente.

Matrices hash

Una m atriz producto de la aplicación de un algoritm o hash se denom ina matriz


hash y son las m atrices que se utilizan con m ayor frecuencia en los procesos don­
de se requiere un acceso rápido a los datos. G ráficam ente estas m atrices tienen la
siguiente forma:

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)

C onociendo la posición, tenem os acceso al contenido. El m iem bro contenido


puede albergar directam ente la inform ación, o bien una referencia a dicha infor­
m ación, cuando ésta sea m uy extensa. El acceso, tal cual lo hem os definido, reci­
be el nom bre de acceso directo.

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:

i = fa (cla ve¡) = fa ( clave 2)

El m étodo hash está basado en esta técnica; el acceso a la m atriz es directo a


través del núm ero i y cuando se produce una colisión (dos claves diferentes dan
un m ism o núm ero i) este elem ento se busca en una zona denom inada área d e d es­
bordam iento.

Método hash abierto


Éste es uno de los m étodos m ás utilizados. El algoritm o para acceder a un ele­
m ento de la m atriz de longitud L, es el siguiente:

1. Se calcula /' = fa (cla ve).

2. Si la posición i de la m atriz está libre, se inserta la clave y el contenido. Si no


está libre y la clave es la m ism a, error: “clave duplicada” . Si no está libre y la
clave es diferente, increm entam os i en una unidad y repetim os el proceso des­
crito en este punto 2. C om o ejem plo, vea la tabla siguiente:

Cláve Contenido

5040

3721

688 : 4007

3900
c

6375

En la figura, se observa que se quiere insertar la clave 6383. S upongam os que


aplicando la función de acceso, obtenem os un v alor 3\ esto es:

i = fa (6 3 8 3 ) = 3

C om o la posición 3 está ocupada y la clave es diferente, tenem os que incre­


m entar i y volver de nuevo al punto 2 del algoritm o.
CA PÍTU LO 14: A LG O RITM O S 6 1 9

L a longitud m edia de búsqueda en una m atriz hash abierta viene dada p o r la


expresión:

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

accesos = (2-60/100)/(2-2*60/100) = 1,75

En el m étodo de búsqueda binaria, el núm ero de accesos viene dado p o r el


v alor log 2 N , siendo N el núm ero de elem entos de la m atriz.

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

O tra función de acceso m uy utilizada es la fu n c ió n m ódulo (resto de una divi­


sión entera):

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

Método hash con desbordamiento

U na alternativa al m étodo anterior es la de disponer de otra m atriz separada, para


insertar las claves que producen colisión, denom inada m atriz de desbordam iento,
en la que se alm acenan todas estas claves de form a consecutiva.
620 JA VA: C U R SO DE PROGRAM A CIÓN

Clave Contenido Clave Contenido

5040 JT 2039 0

2039-+ 3722 6383 1


^ /
2
2 /
6383-*' 4007 3 /

O tra form a alternativa m ás norm al es organizar una lista encadenada p o r cada


posición de la m atriz donde se produzca una colisión.

Clave Contenido R Clave Contenido R

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:

M éto d o_____________S ig n ificad o ______________________________________ ______


C H ashAbierto Es el constructor; está sobrecargado d os veces, una sin pa­
rám etros, que crea una m atriz de 101 elem entos, y otra con
un parám etro, que especifica el núm ero de elem entos. Ini­
cialm ente todas los elem entos alm acenan el valor nuil.
núm eroD eE lem entos M étodo que devuelve el núm ero de elem entos de la m atriz.
núm eroP rim o M étodo que devuelve un núm ero prim o a partir de un nú­
m ero pasado com o argum ento. C om o vam os a utilizar la
función de acceso m ódulo es im portante elegir un núm ero
prim o com o longitud de la m atriz, con la finalidad de que
el núm ero de colisiones sea pequeño.
fa Se trata de un m étodo abstracto con la intención de redefi-
nirlo en una clase derivada y escribir la función de acceso
en función de los datos m anipulados.
com parar M étodo que debe ser redefinido por el usuario en una sub­
clase para perm itir com parar las claves de dos objetos de
los referenciados por la m atriz. D ebe de devolver un entero
indicando el resultado de la com paración (0 para ==).
622 JA V A : C U R S O D E P R O G R A M A C IÓ N

ha shln M étodo hash abierto para añadir un elem ento a la m atriz.


N o devuelve nada.
hashO ut M étodo hash abierto para buscar un objeto con una clave
determ inada. Si se encuentra, devuelve una referencia de
_____________________ tipo O b je c t al m ism o; en otro caso devuelve nuil.

A continuación se presenta el código correspondiente a la definición de la cla­


se C H ashA bierto:

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

public CHashAbierto(int númeroDeElementos)


I
if ( n ú m e r o D e E l e m e n t o s < 1)
n ú m e r o D e E l e m e n t o s = 1 01;
el se
n ú m e r o D e E l e m e n t o s = n ú m e r o P r i m o ( n ú m e r o D e E l e m e n t o s );
m a t r i z h a s h = new C A I u m n o f n ú m e r o D e E l e m e n t o s ] :

public int númeroDeElementos() I r e t u r n m a t r i z h a s h . 1e n g t h ; )

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

public Object h ash 0u t(0b je ct x)


I
624 J A V A : C U R S O D E P R O G R A M A C IÓ 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;

Un ejemplo de una matriz hash


C om o ya hem os indicado, para utilizar esta clase tenem os que derivar otra de ella
y redefinir los m étodos f a y com parar en función de la inform ación encapsulada
por los objetos de datos que deseem os m anipular. P or ejem plo, supongam os que
deseam os c o n stm ir una m atriz hash de objetos d e la clase C A lum no:

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

public CAlumno(String nom. int ma t )

no mb r e = nom;
m a t r i c u l a = mat;

public void asignarN om bretString nom)

no mb r e - nom;

public String obtenerNombre()

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

public int fa(0bject obj)


(
r e t u r n ( i n t ) ( ( C A I u m n o ) o b j ) . o b t e n e r M a t r i c u l a ( ) % n ú m e r o D e E l e me n t o s ( ):
I
626 JA V A : C U R S O D E P R O G R A M A C IÓ N

public int comparar(Object o b jl, Object obj2)


I
if ( ( ( C A Iu m n o ) o b j 1 ) . o b t e n e r M a t r l c u l a () ==
( (CA Iu m no )o b j 2 ) . o b t e n e r M a t r l c u l a ( ) )
r e t u r n 0;
else
r e t u r n 1;

//////////////////////////////////////////////////////////////////

O bserve que para definir la función de acceso m ódulo (% ) necesitam os utili­


zar un valor num érico. Esto no q uiere decir que la clave tenga que ser num érica,
com o sucede en nuestro ejem plo, sino que puede ser alfanum érica. C uando se tra­
baje con claves alfanum éricas o alfabéticas, p o r ejem plo nom bre, antes de aplicar
la función de acceso es necesario convertir dicha clave en un v alor num érico utili­
zando un algoritm o adecuado.

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

C om o alternativa, la biblioteca de Java proporciona en su paquete ja v a .u til


las clases H a sh S et, H a sh M a p y H a sh T a b le que proporcionan conjuntos de datos
que no perm iten duplicados y que utilizan algoritm os hash para el alm acena­
m iento de los datos y para su posterior acceso.

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.

public static int b ú s q u e d a B i n l ( d o u b l e [ ] m. d o u b l e v )


I
// El m ét o d o b ú s q u e d a B i n d e v u e l v e como r e s u l t a d o l a p o s i c i ó n
// d e l v a l o r . S i e l v a l o r no s e l o c a l i z a d e v u e l v e - 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
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;

public static int búsquedaBin2(double[] m. d o u b l e v )


(
// El m ét od o b ú s q u e d a B i n d e v u e l v e como r e s u l t a d o l a p o s i c i ó n
// d e l v a l o r . S i e l v a l o r no s e l o c a l i z a d e v u e l v e - 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;

w h i1e ( i n f < sup );

if ( m [ i n f ] = = v)
return in f;
else
r e t u r n -1;
Ii

En cada iteración, en am bos casos, se divide en partes iguales el intervalo en­


tre los índices i n f y sup. P or ello, el núm ero necesario de com paraciones es com o
m ucho log 2 n , siendo n el núm ero de elem entos de la m atriz. H asta aquí el com ­
portam iento de am bas versiones es el m ism o, pero ¿qué pasa con la condición de
la sentencia w h ile? Se observa que en la prim era versión dicha sentencia realiza
dos com paraciones frente a una que realiza en la segunda versión, lo que se trad u ­
cirá en un m ayor tiem po de ejecución, resultando, por tanto, ser m ás eficiente la
versión segunda.
CA PÍTU LO 14: ALGORITM OS 6 2 9

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

public static int búsquedaBin2(double[] m, d o u b l e v)


I
// V e r s i ó n 2

public static void m a in (S trin g [] args)


I
d o u b l e ü a = new d o u b l e [ 1 0 0 0 0 0 ] :
long t i . n = a .length:
int i :
for (i = 0 ; i < n ; i++)
a[ i ] = i+ 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 .

El program a com pleto se m uestra a continuación:

import j a v a .uti 1 .*;


//////////////////////////////////////////////////////////////////
II C a l c u l a r l o s c e n t r o s n u m é r i c o s e n t r e 1 y n.
//
public c la ss Test
I
// M é t o d o de b ú s q u e d a b i n a r i a
//
// en: c e n t r o n u m érico
// ( 1 a c n - 1 ) en ( c n + 1 a m i t a d )
II s u m a _ g r u p o l = suma d e l o s v a l o r e s desde 1 a cn-1
// s u m a _ g r u p o 2 = suma de l o s v a l o r e s d es de cn+1 a mit ad
//
// El m ét o d o d e v u e l v e como r e s u l t a d o e l v a l o r m i t a d .
// S i en no e s un c e n t r o n u m é r i c o d e v u e l v e un v a l o r 0.
//
public static long b ú sq u e d a B in d o n g en, l o n g n)
I
if (en < = 0 || n < = 0 ) return 0;

long suma_grupol = ((cn-1) * ((cn-1) + 1)) / 2;


lo ng suma_grupo2 = 0:
l o n g m i t a d = 0;

long i n f = cn+1: // l i m i t e i n f e r i o r del g r u p o 2


long sup = n; // l i m i t e s u p e r i o r del g r u p o 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 ) ;

if (suma_grupo2 = = sum a_gru po l)


r e t u r n mitad;
el se
r e t u r n 0;

public static void m a in (S trin g [] args)


I
long n; // c e n t r o s n u m é r i c o s e n t r e 1 y n
long en; // p o s i b l e c e n t r o n u m é r i c o
long 1 i m _ s u p _ g r u p o 2 ; // l i m i t e s u p e r i o r d e l g r u p o 2

S y s t e m . o u t . p r in t ( " C e n t r o s numéricos entre 1 y ");


n - L e e r . d a t o L o n g í );
S y s t e m . o u t . p r i n t l n ( );
f o r ( e n = 3 ; en < n ; c n + + )
I
1 im_sup_grupo2 = b ú s q u e d a B in ( c n . n);
i f ( 1 i m _ s u p _ g r u p o 2 ! - 0)
S y s t e m . o u t . p r i n t í n ( c n + " es c e n t r o n u m é r i c o de 1 a ” +
( c n - 1 ) + " y " + (cn+1) + " a " +
1im _sup_grupo2):

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.

2. M odificar la aplicación “lista de teléfonos” expuesta en el apartado “O rdenación


de ficheros. A cceso aleatorio" de este m ism o capítulo, para que el m étodo elim i­
na r de la clase C ListaT fnos utilice el algoritm o de búsqueda binaria, lo que exigi­
rá q ue los registros del fichero estén clasificados.
632 JA V A : C U R SO DE PROGRAM A CIÓN

3. A ñadir a la clase C ListaTfnos de la aplicación realizada en el apartado “ O rdena­


ción de ficheros. A cceso aleatorio” de este m ism o capítulo, un m étodo denom ina­
do inserción que perm ita o rdenar el fichero “ lista de teléfonos” encapsulado por la
m ism a, en orden descendente por el cam po nom bre.

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.

6. E scribir una aplicación que perm ita:

a) C rear un fichero con la inform ación de elem entos del tipo:

public class CAl u mno


I
// A t r i b u t o s
p rivate in t m atricula:
p r i v a t e S t r i n g nombre:
p r i v a t e do u b le nota:

// M é t o d o s
// . . .
I

b) A lm acenar los registros en el fichero utilizando el m étodo hash abierto.

c) O btener un registro p o r el núm ero de m atrícula utilizando el m étodo hash


abierto.
CAPÍTULO 15
© F.J. CebaHos/RA -MA

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.

El concepto central de cualquier sistem a operativo es el de proceso. C ualquier


ordenador hoy en d ía es capaz de hacer varias cosas sim ultáneam ente; por ejem ­
plo, puede estar im prim iendo un docum ento p o r la im presora y ejecutando un
program a del usuario. Esto requiere que la U C P (unidad central de proceso) alter­
ne de un program a a otro en m uy cortos espacios de tiem po, lo que conocem os
com o tiem po com partido. De esta form a, todos los program as, incluyendo los que
com ponen el sistem a operativo, que tengan que ejecutarse sim ultáneam ente
(m ultiprogram ación) se organizan en varios procesos secuenciales.

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

• su propio espacio de direcciones,


• su m em oria
• sus variables,
• ficheros abiertos,
• procesos hijo,
• contador de program a, registros, pila, señales, sem áforos, etc.

Lo anterior, es equivalente a decir que cada proceso tiene su propia U CP vir­


tual, lo que nos perm ite com prender m ejor cóm o un sistem a puede ejecutar varios
procesos sim ultáneam ente, aunque la realidad sea que la U C P alterna entre esos
procesos.

Según lo expuesto, sería un erro r confundir un program a con un proceso. Para


evitar este posible m alentendido, considere el siguiente ejem plo: cuando instala­
m os un ju eg o en nuestro ordenador lo hacem os siguiendo las instrucciones ad­
juntas. En este caso, las instrucciones serían el program a, la actividad que hay que
desarrollar para realizar la instalación (leer las instrucciones, introducir el CD-
ROM , etc.) el proceso y nosotros la UCP. Si m ientras estam os desarrollando esta
actividad, alguien solicita nuestra colaboración para otra cosa, registram os el
punto en el que nos encontram os y acudim os a resolver lo propuesto. En este ca­
so, la U C P alterna de un proceso a otro.

De lo anterior se deduce que un proceso puede estar en ejecución (está utili­


zando la UCP), preparado (está detenido tem poralm ente para que se ejecute otro
proceso) o bloqueado (no se puede ejecutar debido a que ocurrió algún evento al
que hay que responder adecuadam ente). Entre estos tres estados son posibles, c o ­
mo m uestra la figura siguiente, cuatro transiciones:

P re p a ra d o

^ lo q u e a d ^ J

^ E n eje cu c ió n

Si un proceso en ejecución no puede continuar, p asa al estado de bloqueado o


tam bién, si puede continuar y el planificador decide que ya ha sido ejecutado el
tiem po suficiente, pasa al estado de preparado. Si el proceso está bloqueado pasa­
rá a preparado cuando se dé el evento externo por el que se bloqueó y si está p r e ­
pa ra d o , pasa a ejecución cuando el planificador lo decida porque los dem ás
procesos ya han tenido su parte de tiem po de UCP.
CA PÍTU LO 15: H II.O S 6 3 5

En la U C P puede haber varios program as con varios procesos ejecutándose


concurrentem ente. En este caso se utilizan distintos m ecanism os para la sincroni­
zación y com unicación entre procesos. T ales conceptos son parte del estudio de
sistem as operativos.

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 egún se ha expuesto en el apartado anterior, en un sistem a operativo tradi­


cional, cada proceso tiene un espacio de direcciones y un único hilo de control.
Por ejem plo, considere un program a que incluya la siguiente secuencia de opera­
ciones para actualizar el saldo de una cuenta bancaria cuando se efectúa un nuevo
ingreso:

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

Este m odelo de program ación, en el que se ejecuta un solo hilo, es en el que


estam os acostum brados a trabajar habitualm ente. Pero, continuando con el ejem ­
plo anterior, piense en un banco real; en él, varios cajeros pueden actuar sim ultá­
neam ente. E jecutar el m ism o program a por cada uno de los cajeros tiene un coste
elevado (recuerde los recursos que necesita). En cam bio, si el program a perm itie­
ra lanzar un hilo p o r cada petición de un cajero para actualizar una cuenta, esta­
ríam os en el caso de m últiples hilos ejecutándose concurrentem ente (m ulti-
threading). E sta característica ya es una realidad en los sistem as operativos m o­
dernos de hoy y com o consecuencia contem plada en los lenguajes de program a­
ción actuales.

C om o ya hem os indicado, cada hilo se ejecuta en form a estrictam ente secuen-


cial y tiene su propia pila, el estado de los registros de la U C P y su propio conta­
d o r de program a. En cam bio, com parten el m ism o espacio de direcciones, lo que
significa com partir tam bién las m ism as variables globales, el m ism o conjunto de
ficheros abiertos, procesos hijos (no hilos hijo), señales, sem áforos, etc.

E ntonces ¿qué ventajas aporta un hilo respecto a un proceso? L os hilos com ­


parten un espacio de m em oria, el código y los recursos, p o r lo que el lanzam iento
636 J A V A : C U R S O D E P R O G R A M A C IÓ N

y la ejecución de un hilo es m ucho m ás económ ica que el lanzam iento y la ejecu­


ción de un proceso. P or otra parte, m uchos problem as pueden ser resueltos m ejor
con m últiples hilos; y si no, piense cóm o escribiría un program a con un solo hilo
de control para m ostrar anim ación, sonido, visualizar docum entos y traer ficheros
de Internet, al m ism o tiem po. No obstante, habrá situaciones en las que la m ejor
solución para ayudar en el trabajo sea crear un nuevo proceso (proceso hijo).

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

• En ejecución. El hilo está activo y le ha sido asignada la U C P (sólo los hilos


activos, preparados, pueden ser ejecutados).
• B loqueado. El hilo espera que otro elim ine el bloqueo. Un hilo bloqueado
puede estar:
0 D orm ido. El hilo está bloqueado durante una cantidad de tiem po determ i­
nada. después de la cual despertará y pasará al estado preparado.
0 Esperando. El hilo está esperando a que ocurra alguna cosa: un m ensaje
n o tify , una operación de E/S o adquirir la propiedad de un m étodo sin­
cronizado. C uando ocurra, pasará al estado preparado.
• M uerto. El hilo ha finalizado (está m uerto) pero todavía no ha sido recogido
por su padre. Los hilos m uertos no pueden alcanzar ningún otro estado.

O bservar que en la figura no se m uestran los estados nuevo y m uerto ya que


no son estados de transición durante la vida del hilo; esto es, no se puede transitar
al estado nuevo ni desde el estado m uerto.

La transición entre estados está controlada p o r un planificador: parte del ker-


nel encargada de que todos los hilos que esperan ejecutarse tenga su porción de
tiem po de UCP. Si un hilo en ejecución no puede continuar, pasará al estado blo­
queado', o tam bién, si puede continuar y el planificador decide que ya ha sido eje­
cutado el tiem po suficiente, pasará al estado preparado. Si el proceso está
bloqueado pasará a preparado cuando se dé el evento p o r el que espera; por
ejem plo, puede estar esperando a que otro hilo elim ine el bloqueo, o bien si está
dorm ido, esperará a que pase el tiem po por el que fue enviado a este estado para
ser activado; y si está preparado, pasará a ejecución cuando el planificador lo de­
cida porque los dem ás hilos ya han tenido su parte de tiem po de UCP.

Cuándo se debe crear un hilo


Según lo expuesto anteriorm ente, cada vez que se crea un proceso, el sistem a ope­
rativo crea un hilo prim ario. Para m uchos procesos éste es el único hilo necesario.
Sin em bargo, un proceso puede crear otros hilos para ayudarse en su trabajo, utili­
zando la U C P al m áxim o posible. Por ejem plo, supongam os el diseño de una apli­
cación procesador de texto ¿Sería acertado crear un hilo separado para m anipular
cualquier tarea de im presión? Esto perm itiría al usuario continuar utilizando la
aplicación m ientras se está im prim iendo. En cam bio, ¿qué sucederá si los datos
del docum ento cam bian m ientras se im prim e? Éste es un problem a que habría que
resolver, quizás creando un fichero tem poral que contenga los datos a im prim ir.

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.

Cómo se crea 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 .

La clase T h re a d . que im plem enta la interfaz R u n n a b le , de form a resum ida,


está definida así:

public c la ss Thread exten ds O b je ct implements Runnable


I
// A t r i b u t o s
s t a t i c i n t MAX_PRIORITY;
// P r i o r i d a d máxi ma q u e un h i l o p u e d e t e n e r .
s t a t i c i n t MI N P R 1 0 R I T Y ;
// P r i o r i d a d m í n i m a q u e un h i l o p u e d e t e n e r ,
s t a t i c i n t NORM_PR10 R I T Y ;
// P r i o r i d a d a s i g n a d a p o r o m i s i ó n a un h i l o .

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

boolean isDaem onO


// V e r i f i c a s i e s t e h i l o e s un d e m o n i o . S e da e s t e no mb r e a
// un h i l o que s e e j e c u t a en s e g u n d o p l a n o , r e a l i z a n d o una
// o p e r a c i ó n e s p e c í f i c a en t i e m p o s p r e d e f i n i d o s , o b i e n en
// r e s p u e s t a a c i e r t o s e v e n t o s ,
boolean i s I n t e r r u p t e d í )
// V e r i f i c a s i e s t e h i l o ha s i d o i n t e r r u m p i d o ,
v o i d j o i n ( [ m ? /isegundosl. nanosegundos]])
// E s p e r a i n d e f i n i d a m e n t e o e l t i e m p o e s p e c i f i c a d o , a que e s t e
// h i l o t e r m i n e ( a q u e m u e r a ) .
M ___________________
// C o n t i e n e e l c ó d i g o q u e s e e j e c u t a r á c u a n d o e l h i l o p a s e
// al e s t a d o de e j e c u c i ó n . P o r o m i s i ó n n o h a c e n a d a ,
v o i d s e t O a e m o n í b o o l e a n on)
II D e f i n e e s t e h i l o como un d e m o n i o o como un h i l o de u s u a r i o ,
v o i d s e t N a m e ( S t r i n g nombre)
II C am b i a e l no mb r e de e s t e h i l o ,
v o i d s e t P r i o r i t y ( i n t nuevaPrioridad)
II C a m b i a l a p r i o r i d a d d e e s t e h i l o . P o r o m i s i ó n e s n o rm a l
II ( N 0 R M _ P R I 0 R I T Y ) .
s t a t i c v o i d s l e e p d o n g mi 1 i s e g u n d o s [ . i n t n a n o s e g u n d o s ] )
II E n v í a e s t e h i l o a d o r m i r p o r e l t i e m p o e s p e c i f i c a d o .

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

U na clase que im plem ente la interfaz K u n n a b le tiene que sobreescribir el


m étodo ru n aportado por ésta, de ahí que T h r e a d proporcione este m étodo aun­
que no haga nada. El m étodo r u n contendrá el código que debe ejecutar el hilo.

Los m étodos stop, suspend, resu m e y r u n F in a liz e r s O n E x it incluidos en


versiones anteriores al j d k l .2 han sido desaprobados porque son intrínsecam ente
inseguros. Para m ás detalles vea la ayuda sum inistrada por Sun.
640 JA V A : C U R SO D E PROGRAM A CIÓN

Hilo derivado de Thread

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:

public class ContadorAdelante extends Thread


I
p u b l i c C o n t a d o r A d e l a n t e ( S t r i n g nombre) // c o n s t r u c t o r
I
i f ( n o mb r e ! = n u i l ) setName(nombre);
s t a r t O ; // e l h i l o e j e c u t a s u p r o p i o m é t od o r u n
I
public ContadorAdelante() ( th is(nu ll): I // c o n s t r u c t o r

public void run()


I
for (in t i = 1: i <- 1000: i++)
I
System .out.print(getNam e() + " " + i + " \ r ” ):
I
S y s t e m . o u t . p r i n t l n ( ):

La clase ContaclorAdelante es una subclase de T h re a d y sobreescribe el mé­


todo r u n heredado. Lo que hace este m étodo es escribir el nom bre del hilo segui­
do de un contador de 1 a 1000.

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:

public class Test


I
public static void m a in (S trin g [] args)
I
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 + " ) ;
C A PÍTU LO 15: HILOS 6 4 1

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.

Lo que no se debe de hacer es llam ar directam ente al m étodo ru n ; esto eje­


cutaría el código de este m étodo sin que intervenga el planificador. Q uiere esto
d ecir que es el m étodo sta rt el que registra el hilo en el planificador de hilos.

Hilo asociado con una clase

C uando sea necesario que un hilo ejecute el m étodo r u n de un objeto de cualquier


otra clase que no esté derivada de T h re a d . los pasos a seguir son los siguientes:

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 .

Por ejem plo, la siguiente clase im plem enta la interfaz R u n n a b le y sobrees-


cribe el m étodo r u n proporcionado p o r ésta:

public class ContadorAtras implements Runnable


I
p r i v a t e Thread c u e n t a A t r á s ;
p u b l i c C o n t a d o r A t r a s í S t r i n g nombre) // c o n s t r u c t o r
I
c u e n t a A t r á s = new T h r e a d í t h i s ) ; // o b j e t o de l a c l a s e T hr e a d
i f ( n o mb r e ! = n u i l ) c u e n t a A t r á s . s e t N a m e ( n o m b r e ) :
c u e n t a A t r á s . s t a r t ( ) ; // e l h i l o e j e c u t a e l m ét o d o r u n de
1 // C o n t a d o r A t r a s
642 JA V A: C U R SO DE PRO G R A M A CIÓ N

public ContadorAtras() I th is(n u ll); I // c o n s t r u c t o r

pu b lic void run()


I
for (in t i = 1000; i > 0; i - -)
I
S y s t e m . o u t . p r i n t t " \ t \ t " + c u e n t a A t r á s . g e t N a me C ) + " " + i + ” \ r " ) :
I
System .out.pri n t l n ();

L a clase ContadorAtras no se deriva de T h re a d . Sin em bargo, tiene un m éto­


do r u n proporcionado por la interfaz R u n n a b le . Por ello, cualquier objeto Conta­
dorAtras puede ser pasado com o argum ento cuando se invoque al constructor de
la clase T h re a d cuya sintaxis es:

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:

public class Test


I
public static void m a in (S trin g [] args)
I
ContadorAtras o b j C u e n t a A t r á s = new C o n t a d o r A t r a s ( " C o n t a d o r - " );

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.

C om o ejercicio, pruebe a ejecutar la aplicación siguiente y podrá observar


com o los dos hilos, cuentaAdelante y cuentaAtrás, se ejecutan sim ultáneam ente.

public class Test


I
public static void m a in ( S trin g [] args)
I
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 + " ) :
C o n t a d o r A t r a s o b j C u e n t a A t r á s - new C o n t a d o r A t r a s ! " C o n t a d o r - * ) ;

C uando ejecute la aplicación anterior, el sistem a lanza la ejecución del hilo


prim ario (hilo padre) el cual, al ejecutarse, lanza la ejecución del hilo cuentaAde­
lante y la ejecución del hilo cuentaAtrás, finalizando así la ejecución de m a in ; no
obstante, este m étodo no retom ará hasta que no hayan finalizado los hilos hijo;
esto es, el hilo prim ario no term ina m ientras no term inen sus hilos hijo.

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

public void run{)


I
c h a r bi p = ' \u0007 ’ ;
w hile (true)
I
try
I
s l e e p ( l O O O ) ; // 1 s e g u n d o
1
catch ( In te r r u p te d E x c e p tio n e) 11
S y s t e m . o u t .pri n t ( b i p ) :

//////////////////////////////////////////////////////////////////

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 C o n ta d o rA d e la n te tS trin g nombreHilo)


I
setNametnom breHilo):
s t a r t í ):

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

La siguiente aplicación inicia un dem onio de la clase CDemonio expuesta


anteriorm ente y un hilo de la clase ContadorAdelante. M ientras este hilo m uestra
646 JA V A : C U R S O D E P R O G R A M A C IÓ N

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

A un hilo en ejecución se le puede enviar un m ensaje yield para que se m ueva al


estado preparado y ceda así la U C P a otros hilos que estén com pitiendo por ella
(hilos que están en el estado preparado). Si el planificador observa que no hay
ningún hilo esperando por la U C P, perm itirá que el hilo que iba a ceder la UCP
continúe ejecutándose.

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

Si en la entrada estándar hay un byte disponible la sentencia anterior se eje­


cuta satisfactoriam ente y la ejecución del m étodo que la contiene continúa. Sin
em bargo, si no hay un b yte disponible, read tiene que esperar hasta q ue haya uno.
648 JA V A: CU R SO DE PROGRAM A CIÓN

Si el hilo que ejecuta read se m antuviera en el estado de ejecución, la U C P que­


daría ocupada y no se podría realizar nada más. En general, si un m étodo necesita
esperar una cantidad de tiem po indeterm inada hasta que la ocurrencia que lo de­
tiene tenga lugar, el hilo en ejecución debe salir de este estado. T odos los métodos
Jav a que perm iten leer datos se com portan de esta form a. Un hilo que gentilm ente
abandona el estado de ejecución hasta que se dé la ocurrencia que lo detiene se
dice que está bloqueado.

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

preparado. Pasará a ejecución cuando el planificador lo indique. Esto significa


que una llam ada a sleep bloqueará un hilo por un tiem po superior al especificado.

La clase T h re a d proporciona tam bién un m étodo in te rru p t. C uando un hilo


dorm ido recibe este m ensaje, pasa autom áticam ente al estado preparado, y cuan­
do pase a ejecución, ejecutará su m anejador In te rru p te d E x c e p tio n .

Esperando

El m étodo w a it m ueve un hilo en ejecución al estado esperando y el m étodo noti-


fy m ueve un hilo que esté esperando al estado preparado-, n o tify A ll m ueve todos
los hilos q ue estén esperando al estado preparado. Estos m étodos, que verem os
m ás adelante con m ás detalle, se utilizan para sincronizar hilos.

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.

Según lo expuesto, la m áquina Java no reem plazará el hilo actual en ejecución


por otro hilo de la m ism a prioridad. En otras palabras, la m áquina Java no aplica
una planificación por cuantos (tim e-slice — cuanto o rodaja de tiem po —: tiem po
m áxim o que un hilo puede retener la UCP; esta planificación da lugar a un siste­
m a no determ inista), aunque la im plem entación del sistem a de hilos que subyace
en la clase T h re a d puede soportar cuantos. Por lo tanto, no escribir código que
dependa de cuantos porque com o se indica a continuación, en unas m áquinas
virtuales puede funcionar (en W indow s y M acintosh) y en otras no (en Solaris).

Después de lo dicho, sería bueno al program ar que nuestros hilos cedieran


voluntariam ente el control algunas veces. Un hilo dado puede renunciar a su dere­
cho de ejecutarse para ceder el control a otro de la m isma prioridad llam ando al
m étodo yield. Un intento de ceder la U C P a hilos de m enor prioridad se ignorará.

La política de planificación p o r prioridades expuesta, puede verse en algún


m om ento alterada por el planificador. Por ejem plo, el planificador de hilos puede
elegir para su ejecución a un hilo de m enor prioridad para evitar que quede com ­
pletam ente bloqueado porque no pueda progresar p o r falta de los recursos necesa­
rios para ello (puede m orir p o r falta de recursos: inanición - starvation). Por esta
razón, la exactitud de los algoritm os program ados no debe basarse en la prioridad
de los hilos.

¿Qué ocurre con los hilos que tengan igual prioridad?

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:

• un hilo de prioridad m ás alta ha alcanzado el estado de preparado-,


• cede la UCP, o su m étodo r u n finaliza;
• se supera el cuanto (quantum): tiem po m áxim o que un hilo puede retener la
UCP. E sta tercera condición sólo es aplicable en sistem as que soporten la pla­
nificación por cuantos. En este aspecto, la especificación Java da m ucha li­
bertad. C ada m áquina virtual im plem enta com o quiere este agujero en la
definición, cum pliendo perfectam ente el estándar. P or ejem plo, las platafor­
m as W indow s 9x/N T/2000 y M acintosh adm iten planificación por cuantos
para hilos con la m ism a prioridad; en cam bio, Solaris planifica por coopera­
ción, es decir, los hilos deben ceder voluntariam ente la UCP.
C A PÍTU LO 15: H ILOS 6 5 1

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.

Asignar prioridades a los hilos


C uando se crea un hilo Java, éste hereda su prioridad del hilo que lo crea. No
obstante, es posible aum entar o dism inuir esta prioridad. Para m odificar la priori­
dad de un hilo utilice el m étodo setP rio rity:

h i l o . s e t P r i o r i t y (nuevaPrioridad) ;

La siguiente tabla m uestra las constantes correspondientes a las prioridades


definidas en la clase T h re ad :

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

Para obten er la prioridad que tiene un hilo utilice el m étodo ge tP riority:

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.

T hread-1. P-2 2410 T h re a d -2 . P - 3 465771

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:

public c la ss Contador extends Thread


I
p u b lic in t cuenta:
p r i v a t e d o u b l e suma - 0:

public void run()


1
for (cuenta - 0: cuenta < 500000; cuenta++)
I
// R e a l i z a r a l g u n o s c á l c u l o s
suma + “ M a t h . r a n d o m ( ):

// 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 class Cuentas extends i., c a d


I
private sta t ic int nCuentas:
private Contador[] cuenta:

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

publi c void run( )


I
int i :
b o o l e a n h a y a H i 1o s V i v o s :

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

public class Test


I
public static void m a in (S trin g [] args)
I
i n t n C u e n t a s = 2 : // nú me r o de c o n t a d o r e s
// C r e a r y l a n z a r e l h i l o C u e n t a s
C u e n t a s h i l o C u a n t a s = new C u e n t a s ( n C u e n t a s ) ;
h i 1o C u a n t a s . s t a r t ( ):

En este ejem plo que acabam os de realizar, la política de planificación es por


derecho de prioridad.

¿Q ué pasará si elim inam os la sentencia sleep(nM ilisegundos)? Pues que la


política de planificación seguida se ve alterada por el planificador para evitar que
el hilo de m ayor prioridad se apodere de la U C P (hilo egoísta); pruébelo (evitar
starvatiori). Sistem as com o W indow s 9x/N T /2000 pelean contra los “ hilos egoís­
tas” con la estrategia de asignar la U C P p o r cuantos (tim e-slicing).

¿Q ué sucede si asignam os a todos los hilos contador la m ism a prioridad? En


esta situación, el planificador elegirá el siguiente para ejecución según el m odelo
round-robín y en el caso de W indow s asignará, adem ás, la U C P por cuantos.

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.

C uando un hilo se pone a dorm ir (se bloquea), no entra en la planificación del


sistem a; esto es, el planificador no le asigna tiem po de U C P y, por consiguiente,
detiene su ejecución.

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:

• datos: m atriz de tipo dou b le.


• ind: índice del siguiente elem ento vacío.
• tam a ñ o : núm ero de elem entos de la m atriz.

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

• C D atos: es el constructor de la clase. C rea la m atriz con n elem entos, valor


que se pasa com o argum ento, o con 10 si el valor pasado no es válido; tam ­
bién inicia tam año con el núm ero de elem entos.
• obtener, d evuelve el valor de un determ inado elem ento.
• asignar, asigna un valor a un determ inado elem ento.
• cá lcu lo s; obtiene el siguiente valor a alm acenar en la m atriz. R ecibe com o a r­
gum ento el nom bre del hilo en ejecución y devuelve el índice del siguiente
elem ento vacío.

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

p u b lic double obtener(int i)


I
r e t u r n d a t o í i ]:

public void a sig n a ríd o u b le x, int i)


I
da t o C i ] = x ;

public int cálculos(String hilo)


I
i f ( i n d > - t a ma ñ o ) r e t u r n t a ma ñ o :
double x = M a th .ra n d o m ():
S y s t e m . o u t . p r i n t l n í h i l o + " muestra " + ind);
a s ig n a ríx , ind):
ind++:
r e t u r n ind:

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

public class CAdquirirDatos exten ds Thread


I
p r i v a t e C O a t o s m; // o b j e t o p a r a a l m a c e n a r l o s d a t o s
p u b l i c C A d q u i r i r D a t o s ( C D a t o s m d a t o s ) // c o n s t r u c t o r
1
m - mdatos:
I

public void run()


I
int i - 0;

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

En la aplicación anterior observam os que m a in crea el objeto datos donde el


hilo A d q u irí rD atos_0 alm acenará los datos. Si ejecuta esta aplicación el resultado
será el siguiente:

Thread-0 tomó la muestra 0


Thread-0 tomó la muestra l
Thread-0 tomó la muestra 2
Thread-0 tomó la muestra 3
Thread-0 tomó la muestra 4
Thread-0 tomó la muestra 5
Thread-0 tomó la muestra 6
Thread-0 tomó la muestra 7
658 JA VA: C U R S O DB PROGRAM A CIÓN

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:

public class Test


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

A hora el m étodo m a in de la aplicación Test lanza dos hilos: adquirí rD atos_0


y a d q u irirD a to s_l. C uando se lanza un hilo, el retom o al proceso padre es inm e­
diato. P o r eso podem os suponer que la ejecución del hilo A d q u irirD a to s_ l se ini­
cia paralelam ente a la de adquirirD atos_0. Si ahora ejecutam os la aplicación, el
resultado obtenido será sim ilar al siguiente:

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)

A nalicem os los resultados. C uando se ejecutó sólo un hilo, la m atriz se llenó


totalm ente sin problem as; esto es, no faltaron m uestras, tam poco se perdieron por
realizar alm acenam ientos consecutivos en el m ism o elem ento y no hubo accesos a
elem entos fuera de los lím ites establecidos (el núm ero de m uestra coincide con el
CA PÍTU LO 15: HILOS 6 5 9

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

En sistem as que soporten la planificación por cuantos, un hilo en ejecución


puede ser interrum pido después de cualquier línea del m étodo siguiente; por
ejem plo, supongam os según el código siguiente que uno de los hilos se interrum ­
pe después de la línea 6; no se increm entó ind. Si esto ocurre, cuando se ejecute el
otro hilo, alm acenará la m uestra adquirida en el últim o elem ento utilizado; y si
suponem os que este hilo es interrum pido después de la línea 7, se increm enta el
índice, cuando se ejecute de nuevo el hilo que se interrum pió en la línea 6, volve­
rá a increm entar el índice dejando un elem ento vacío.

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.

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

En el ejem plo anterior, la sección de código crítica es el m étodo cálculos.


Esto quiere decir que un hilo no debe acceder a cálculos cuando otro hilo lo está
ejecutando, para lo cual, el m étodo cálculos de la clase C D atos debe ser declarado
sy n ch ro n ized :
660 J A V A : C U R S O D E P R O G R A M A C IÓ N

public class CDatos


I
II...
p u b lic synchronized int cá lc u lo s(S trin g hilo)
I
i f ( i n d > = t a ma ñ o ) r e t u r n ta maño:
d o u b l e 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 ( h i 1 o + " tomó l a m u e s t r a " + ind);
a sig n a r(x , ind);
i nd++;
return ind;
I
I

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.

m o n ito r d isp on ible

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

Las secciones de código sincronizadas, llam adas secciones críticas, denotan


que el acceso a ellas es crítico para el éxito de la ejecución de los hilos del pro­
gram a. Por ello, en ocasiones, nos referim os a las secciones críticas com o opera­
ciones atóm icas, significando que ellas representan para cualquier hilo una
operación que debe ejecutarse de una sola vez.

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

public class CAdquirirDatos extends Thread


I
private C D a t o s m; // o b j e t o p a r a a l m a c e n a r l o s d a t o s

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:

public void run()


I
d o u b l e x:
do

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

while (m .ind < m.tamaño):

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.

Lo que no se debe hacer es lo que se m uestra a continuación, ya que si el b u ­


cle w hile pertenece a la sección crítica, el planificador no podrá bloquear el hilo
hasta que no term ine de ejecutarse y por lo tanto, no podrá asignar tiem po de UCP
al otro hilo. C uando el bucle w hile finalice, la m atriz ya estará llena, lo que supo­
ne que el bucle w hile para el otro hilo nunca se ejecutará.

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

En general es m ejor aplicar la sincronización a nivel del m étodo que a un blo­


que de código. La prim era técnica facilita m ás el diseño orientado a objetos y
proporciona un código m ás fácil de interpretar y p o r lo tanto, m ás fácil de depurar
y de m antener.

M onitor reentrante

Supongam os que en alguna ocasión necesitam os que un m étodo sincronizado ten­


ga que llam ar a otro m étodo tam bién sincronizado de la m ism a clase. P or ejem ­
plo, para facilitar la com prensión de lo que se trata de explicar, vam os a suponer
que el m étodo asig n a r tam bién está sincronizado:

public c la ss CDatos
I
II ...

public synchronized void asignarídouble x, int i)


I
datofi] = x :
)

public synchronized int cálculos(String hilo)


I
i f ( i n d > = t a m a ñ o ) r e t u r n t a ma ñ o :
d o u b l e x = M a t h . r a n d o m ( ):
S y s t e m . o u t . p r i n t l n ( h i l o + " tomó l a muestra " + ind);
asignartx, ind);
i nd++;
re tu rn ind;

L a clase C D atos contiene ahora dos m étodos sincronizados: asignar y cálcu­


los. El segundo llam a al prim ero. C uando un hilo trata de ejecutar el m étodo cál­
culos prim ero tom a el control del m onitor del objeto C D atos. A continuación
ejecuta este m étodo, el cual llam a al m étodo asignar. C om o a signar está también
sincronizado, el hilo intenta adquirir otra vez el control del m onitor del objeto
CD atos. Parece lógico que el hilo debe bloquearse asim ism o puesto que trata de
adquirir un m onitor que él m ism o debe ceder, cosa q u e no puede hacer hasta que
no finalice la ejecución d e cálculos. En cam bio no sucede así ¿ p o r qué?
C A PÍTU LO 15: HILOS 6 6 3

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.

Utilizar wait y notify

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.

Supongam os la clase C M ensaje según se m uestra a continuación:

public class CMensaje


I
private S t r in g textoMensaje;
private i n t númeroMensaje:

public synchronized void alm acenar(int nmsj)


1
númeroMensaje = n m s j :
// 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 :
textoMensaje = "mensaje":
1

public synchronized String obtenerO


1
// C omp on er e l m e n s a j e b a j o un d e t e r m i n a d o f o r m a t o
S t r i n g mensaje;
mensaje = t e x to M e n s a j e + " / / " + númeroMensaje:
r e t u r n mensaje;

Se ha considerado que los m étodos alm acenar y obtener son operaciones


atóm icas, significando que para cualquier hilo deben ejecutarse de una sola vez;
dicho de otra form a, se han definido com o secciones críticas.
664 JA V A : C U R S O D E P R O G R A M A C IÓ N

En el ejem plo expuesto, estam os pensando en un sistem a que tendrá un pro­


ductor de m ensajes (m ensajes de aviso, de error, etc.) para generar y alm acenar
los m ensajes producidos (sólo se recuerda el últim o m ensaje) y un consum idor de
m ensajes que m ostrará al usuario el texto de cada m ensaje que se produzca. T anto
el productor com o el consum idor serán hilos que se suponen están alertas para de­
sem peñar su función cuando sea requerida.

public c la ss Productor extends Thread


I
p r i v a t e CMensaje mensaje: // ú l t i m o m e n s a j e p r o d u c i d o
// C u a n d o s e p r o d u c e un m e n s a j e , e l p r o d u c t o r
// a l m a c e n a e l t e x t o en s u m i e m b r o " m e n s a j e "

public class Consumidor extends Thread


I
p r i v a t e CMensaje mensaje: // m e n s a j e a m o s t r a r
// C ua n d o s e ha p r o d u c i d o un m e n s a j e , e l c o n s u m i d o r
// l o o b t i e n e de s u miembro " m e n s a j e " y l o m u e st ra
)

C om pletem os el código del productor. La clase P roductor tiene un construc­


tor que inicia el atributo m ensaje con el objeto C M ensaje pasado com o argum en­
to; este m ism o objeto será el que utilice el consum idor para m ostrar el últim o
m ensaje producido. C om o se puede observar, ésta es una form a sencilla de hacer
que dos o m ás hilos com partan datos. A sim ism o, sobreescribe el m étodo r u n para
alm acenar el m ensaje que se produzca en el objeto C M ensaje; este m étodo sim ula
que cada m seg s m ilisegundos, valor generado aleatoriam ente para cada m ensaje,
se produce el m ensaje de núm ero núm eroM sj, valor generado tam bién aleatoria­
mente. El hilo perm anece dorm ido y despierta cada vez que se produce un m en­
saje. Según lo expuesto, una aproxim ación a la im plem entación de esta clase
puede ser la siguiente:

public class Productor extends Thread


I
private CMensaje mensaje: // ú l t i m o m e n s a j e p r o d u c i d o

public ProductoríCMensaje c) // c o n s t r u c t o r
I
m e n s a j e = c:

public void run()


I
i n t númeroMsj: // nú mero de m e n s a j e
w hile (true)
CA PÍTU LO 15: H ILOS 6 6 5

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 )

C om pletem os a continuación el código del consum idor. L a clase C onsum idor


tiene un constructor que inicia el atributo m ensaje con el objeto C M ensaje que
com parte con el productor. A sim ism o, sobreescribe el m étodo ru n para obtener el
m ensaje alm acenado en el objeto m ensaje y m ostrarlo. S egún esto, la im plem en-
tación de esta clase puede ser la siguiente:

public class Consum idor exten ds Thread


1
p r i v a t e CMensaje mensaje: // m e n s a j e a m o s t r a r

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:

public void runí)


(
S t r i ng m s j ;

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:

Consumidor Thread-1 obtuvo: n u i l #0


Productor Thread-0 almacena: mensaje #15
Consumidor Thread-1 obtuvo: mensaje #15
Consumidor Thread-1 obtuvo: mensaje #15
Consumidor Thread-1 obtuvo: mensaje #15
Consumidor Thread-1 obtuvo: mensaje #15
Productor Thread-0 almacena: mensaje #19
Consumidor Thread-1 obtuvo: mensaje #19
Consumidor Thread-1 obtuvo: mensaje #19

Un análisis sencillo nos conduce a la conclusión de que independientem ente


de que los m étodos se hayan definido com o secciones críticas, no existe una sin­
cronización entre el productor y el consum idor. El consum idor m uestra el últim o
m ensaje producido cada vez que el planificador le asigna tiem po de U C P, en lugar
de hacerlo única y exclusivam ente cada vez que se produzca un m ensaje.

Para conseguir la sincronización deseada, el m onitor asociado con el objeto


C M ensaje tiene que auxiliarse de los m étodos w a it y notify. La siguiente figura
m uestra las transiciones de estados cuando intervienen estos m étodos:

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

D ijim os q ue en Java, cada “objeto” tiene un m onitor. El m onitor controla el


acceso al código sincronizado del objeto; en otras palabras, a la sección crítica. Y
según hem os visto, un objeto C M ensaje presenta dos secciones críticas. Pues bien,
el m étodo no tify despierta sólo un hilo de los que estén esperando por ese m oni­
tor. Esto es, si hay varios hilos esperando, se elige uno arbitrariam ente. El hilo
despertado com petirá de la m anera habitual con el resto de los hilos que estén en
el estado prep a ra do , p o r adquirir la UCP. S egún lo expuesto, el m étodo notify
sólo puede ser llam ado p o r un hilo que haya adquirido el control del monitor.

Un hilo se pone a esperar p o r el m onitor de un determ inado objeto invocando


al m étodo wait. A dem ás, el hilo cede el control del m onitor.

v o i d w a i t ( O ? / isegu nd os í . n a n o s e g u n d o s ] ] )

El m étodo w ait envía al hilo actualm ente en ejecución al estado de espera,


hasta que otro hilo, el que tiene el control del m onitor, invoque al m étodo notify,
n o tify A Il o in te rru p t, o bien hasta que transcurra el tiem po especificado. C uando
el hilo se pone a dorm ir, cede el control sólo del m onitor q ue controla el acceso al
código sincronizado del objeto que lo ha invocado, lo que perm itirá a otro hilo
que esté esperando por él, adquirirlo; esto es, cualquier otro objeto actualm ente
controlado p o r el hilo que se pone a dorm ir perm anecerá bloqueado m ientras éste
esté dorm ido.

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.

Lo anteriorm ente expuesto conduce a la conclusión de que los m étodos wait,


notify, n o tify A Il e in te rru p t, deben ser invocados desde código sincronizado.

A plicando la teoría expuesta vam os a continuación a sincronizar los hilos


productor y consum idor del ejem plo que estam os desarrollando. Para ello, hay
que tener presente que no se puede m ostrar un m ensaje que aún no se ha generado
(por supuesto, los m ensajes se m uestran una sola vez) y no se puede alm acenar un
m ensaje, si aún no se ha m ostrado el últim o generado.
668 J A V A : C U R S O D E P R O G R A M A C IÓ N

Entonces, añadirem os a la clase C M ensaje un atributo disponible de tipo


b o o lean , que valga false cuando no haya ningún m ensaje que m ostrar, y tr u e en
caso contrario.

public class CMensaje


(
private S trin g textoMensaje;
private in t númeroMensaje;
private boolean d i s p o n i b l e - f a l s e :

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.

public synchronized void almacenar( in t nms j )


I
while ( d isp o n ib le == true)
1
// El ú l t i m o m e n s a j e a ún no ha s i d o m o s t r a d o
try
I
wa i t ( ) : // e l hilo s e p o n e a d o r m i r y c ed e e l monitor
I
catch ( In te rru p te d E x c e p tio n e) I |
I
númeroMensaje = n m s j :
II 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 el m e n s a j e en una t a b l a
II de m e n s a j e s : r e s u l t a d o :
textoMensaje = "mensaje":
d isp o n ib le = true;
n o t i f y A l 1( ):
I

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 synchronized String obtenerO


I
while (disponible == fa ls e )
I
// No h a y m e n s a j e
try
(
w aitO ; // e l hilo s e pone a d o r m i r y cede el monitor
I
catch ( InterruptedException e) I )
I
di spo ni b le = fa 1 s e ;
noti f y A l1();
// C o mp on e r e l m e n s a j e b a j o un d e t e r m i n a d o - f o r ma t o
S t r i n g mensaje;
m e n s a j e = t e x t o M e n s a j e + " II" + n ú m e r o M e n s a j e :
r e t u r n mensaje:
I

U na vez m odificados los m étodos alm acenar y o btener de la clase C M ensaje,


ejecute de nuevo la aplicación Test. O bservará que ahora los resultados sí son los
esperados:

Productor Thread-0 almacena: mensaje 117 4


C o n s um i d o r Thread-1 obtuvo: mensaje #74
Productor Thread-0 almacena: mensaje #17
Consumidor Thread-1 obtuvo: mensaje # 17
Productor Thread-0 almacena: mensaje # 85
Consumidor Thread-1 obtuvo: mensaje #85
Productor Thread-0 almacena: mensaje #3
C o n s um i d o r Thread-1 obtuvo: mensaje #3
Productor Thread-0 almacena: mensaje #91
C o n s um i d o r Thread-1 obtuvo: mensaje #91

¿P or qué los métodos alm acenar y obtener utilizan un bucle?

A ntes de proceder a la explicación, añada m ás consum idores y observar los re­


sultados. Por ejem plo:

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:

1. if (disponible = false) II a n t e s : w hile (disponible = false)


2. I
3. try
4. I
5. w aitO ; // e l hilo s e po ne a d o r m i r y c e d e e l monitor
6 . )
7. catch ( InterruptedException e) ( )
8. 1
9. d i s p o n i b l e = fa 1s e :
10. n o t if y A l1():
11 . S t r i ng m e n s a j e ;
12. mensaje = t e x to M e n s a j e + " # " + númeroMensaje:
13 . r e t u r n mensaje;

C om pile y ejecute de nuevo la aplicación. C om pare los resultados con los


obtenidos anteriorm ente ¿Q ué ha ocurrido?

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

El peligro de utilizar una sentencia if en lugar de w hile es que algunas veces


el hilo que adquiere el control del m onitor, Thread-2, ejecuta la línea 1 y supo­
niendo que la condición es cierta se pone a esperar, adem ás de ced er el monitor.
M ás tarde, otro hilo adquiere el m onitor, Thread-1, suponiendo que la condición
es falsa ejecuta la línea 10 y despierta a los hilos que esperan por este monitor.
Los hilos en el estado preparado com piten por la U C P. S upongam os que el pla­
nificador se la adjudica al hilo original, Thread-2-, éste continuará donde lo dejó, a
p artir de la línea 5, independientem ente del estado del m onitor. El resultado es
que retorna el m ism o m ensaje que Thread-1. U tilizando una sentencia w h ile en
lugar de if, cuando Thread-2 continúe donde lo dejó, a partir de la línea 5, volverá
a ejecu tar la línea 1 y se pondrá de nuevo a esperar p o r ser la condición cierta.

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.

P ara la m ayoría de los program adores Java, la m ejor de evitar el interbloqueo


es prevenirlo, m ejor que probar y detectarlo. En cualquier caso, cualquiera de las
técnicas existentes para m anejar los interbloqueos se sale fuera del objetivo de
este capítulo.

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

P or ejem plo, la siguiente aplicación obtiene una referencia al grupo actual


(grupo predefinido) al cual pertenece el hilo actual (en este caso el hilo prim ario)
y la alm acena en consum idores.

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

¿C óm o se añaden los hilos a un grupo? Pues utilizando alguno d e los cons­


tructores q ue la clase T h re a d proporciona para ello. P or ejem plo:

T h r e a d (T h r e a d G r o u p grupo, S t r in g nom breH ilo)

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:

public c la ss Consumidor extends Thread


I
p r i v a t e CMensaje mensaje: // m e n s a j e a m o s t r a r

p u b l i c Consum ido rtC M e nsa je msj, ThreadGroup grupo, String n o mbr e )


I
s u p e r í g r u p o , nombre):
mensaje = m s j :
)

public void run()


C A P ÍT U L O 15: H IL O S 6 7 3

Se puede observar que el constructor de la clase C onsum idor invoca al cons­


tructor de su clase base (T h r e a d ) pasándole com o argum ento el grupo al cual se
quiere añadir el hilo, el cual está referenciado por this. y el nom bre del hilo.

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.

Hilo re c e p to r IjjH Flujo d e c a ra c te re s H ilo e m is o r

1
lp¡i
P ip e d R e a d e r P ip e d W rite r

Java proporciona las clases P ip e d R e a d e r y P ip e d W r ite r (y sus hom ólogas


para bytes, P ip e d ln p u tS tre a m y P ip e d O u tp u tS tr e a m ) para trabajar con tuberías
a través de las cuales circularán flujos de caracteres. La prim era representa el ex­
trem o de la tubería del cual un hilo obtiene los datos y la segunda el extrem o de la
tubería p o r el cual un hilo envía los datos al otro. Por lo tanto, estas clases traba­
jan conjuntam ente para proporcionar un flujo de datos a través de una tubería de
form a m uy sim ilar a com o una tubería real proporciona un flujo de agua; en ésta.
674 JA V A: C U R S O DE PROGRAM A CIÓN

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.

Para crear la estructura de la figura anterior, prim ero crearíam os un extrem o


de la tubería (extrem o sobre el que trabajará el em isor) y después el otro conecta­
d o al anterior para form ar la tubería (extrem o sobre el que trabajará el receptor).
El código necesario para realizar lo expuesto es el siguiente:

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:

C on tuberías, la salida de un proceso se conecta directam ente a la entrada del


siguiente, según m uestra la figura siguiente:

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

Se puede observar que el m étodo m a in crea una tubería em isor-receptor. A


continuación crea el hilo p ro d u c to rl y le pasa com o argum ento el extrem o de la
tubería por el cual debe de enviar los m ensajes al hilo consum idor. D espués crea
el hilo co n su m id o rl y le pasa com o argum ento el extrem o de la tubería por el cual
debe obten er los m ensajes enviados por el hilo productor. Finalm ente, lanza los
dos hilos para su ejecución.

M ostram os a continuación la clase correspondiente al hilo productor. Esta


clase tiene un atributo em isor, que referenciará el extrem o de la tubería p o r lo que
se enviarán los m ensajes al hilo consum idor. Este atributo será establecido por el
constructor de la clase.

Para en v iar m ensajes al consum idor, el m étodo r u n del productor crea un


flujo (flujoS) de la clase P r in t W r it e r hacia el extrem o em isor. E ste flujo perm iti­
rá utilizar el m étodo p rín tln , que P ip e d W r ite r no tiene, para enviar los m ensajes
por la tubería. L a generación de los m ensajes se sim ula igual que hicim os en la
versión de la aplicación productor consum idor anterior. En este caso, por tratarse
de una tubería, los m ensajes producidos son enviados por la m ism a y puestos en
cola m ientras el consum idor los va recuperando.

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

private PrintW riter flu joS = nuil:

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

public synchronized void almacenarMensajet)


I
i n t númeroMsj: // nú me r o de m e n s a j e
S t r i n g t e x t o M e n s a j e : // t e x t o m e n s a j e

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 :

p u b lic ConsumidoríPipedReader re) // c o n s t r u c t o r


I
r e c e p t o r ■= r e :
f l u j o E - new B u f f e r e d R e a d e r ( r e c e p t o r );
I

public void run()


1
w hile (true)
I
o b t e n e r M e n s a j e ( ):

public synchronized void obtenerMensaje()

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

catch (lOException ignorada) I)

protected void f i n a l i z e (> throws lOException


I
if ( f l u j o E != n u i l ) I f l u j o E . c l o s e ( ): f l u j o E = n u i l ; 1
if ( r e c e p t o r != n u i l ) ( r e c e p t o r . c l o s e ( ): r e c e p t o r = n u i l : I
678 JA V A : C U R S O D E P R O G R A M A C IÓ N

ESPERA ACTIVA Y PASIVA


C uando se diseña un hilo, lógicam ente no sólo pensam os en el trabajo que tiene
que desem peñar, sino en cóm o su trabajo puede verse afectado p o r otros hilos. De
ahí el estudio de la sincronización de hilos. P ero no es m enos im portante pensar
cóm o tiene que com portarse el hilo com o unidad individual de ejecución; esto es,
poniéndonos en el caso de que durante espacios m ás o m enos cortos, su ejecución
no va a ser interferida p o r otros hilos. Si esto es así, los objetos de sincronización
no serán requeridos por otros hilos y puede haber tiem po suficiente para que el
hilo finalice su trabajo a la espera de que se den otros eventos que lo requieran de
nuevo. En un caso com o éste, la espera debe ser pasiva y no activa; es decir, no
debe co n su m ir tiem po de UCP.

E n general un hilo realiza una espera pasiva poniéndose él m ism o a dorm ir


porque cuando está durm iendo, no entra en la planificación del sistem a operativo;
esto es, el sistem a operativo no le asigna tiem po de U C P y, por consiguiente, de­
tiene su ejecución. T anto w a it com o sleep ponen un hilo a dorm ir; en el prim er
caso, para despertado hay que invocar a no tify o notifyA ll, o bien esperar a que
transcurra el tiem po si se especificó, y en el segundo caso despierta cuando trans­
curra el tiem po especificado.

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.

La figura anterior m uestra un esquem a del problem a del p ro d u cto r y el con­


sum idor. Para un correcta sincronización entre los hilos, el productor deberá blo-
C A P ÍT U L O 15: HILOS 6 7 9

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.

El código correspondiente a esta clase se m uestra a continuación:

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

public CMatrizí int n)


I
i f ( n < 1 ) n = 10;
m = new i n t [ n ] :
elem entosVacíos = m.length;
e l e m e n t o s L l e n o s = 0:

public synchronized v o i d a l m a c e n a r í i n t num)


I
// E s p e r a r a q u e h a y a e l e m e n t o s v a c í o s
w h i le ( e le m e n t o s V a c í o s = = 0)
1
try
I
wait(): // e l hilo s e po ne a d o r m i r y c e d e e l monitor
680 J A V A : C U R S O D E P R O G R A M A C IÓ N

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

public synchronized int obtenerO


I
II E s p e r a r a q u e h a y a e l e m e n t o s llenos
w hile (elementosLlenos = 0)
I
try
I
w ait{); // e l hilo s e pone a d o r m i r y cede el monitor
I
catch (In terruptedExce ption e) I )
I
elementosVací os++;
e l e m e n t o s L l e n o s - -;
S yste m .o ut.p r i n t ( " v a c i o s : ” + elem entosVacíos + ", llenos: " +
elementosLlenos + " \r"):
i n t num = m [indCons];
i n d C o n s = ( i n d C o n s + 1) % m . l e n g t h ;
noti f y A l1();
r e t u r n num;

//////////////////////////////////////////////////////////////////

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

vacíos de la m atriz. P or lo tanto, el contador de elem entosLlenos debe valer ini­


cialm ente cero y el de elem entosV acíos debe valer m .length.

C uando un hilo necesita un recurso de un tipo particular, decrem enta el con­


tador del sem áforo correspondiente, y cuando lo libera lo increm enta. Por ejem ­
plo, cuando el productor quiere alm acenar un dato necesita el recurso “elem entos
vacíos” , de tal form a que cada vez q ue lo adquiere lo decrem enta; cuando llegue a
cero im plica sem áforo en rojo indicando que el recurso “elem entos vacíos" está
ocupado. Lógicam ente decrem entar elem entosV acíos im plica increm entar ele­
m entosLlenos.

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

El código anterior, que pertenece al hilo productor, decrem enta el contador


del sem áforo elem entosV acíos, inserta un dato en el siguiente elem ento vacío de
la m atriz y, lógicam ente, increm enta el contador del sem áforo elem entosLlenos.
Si el contador de elem entosV acíos fuera cero, el hilo productor pasaría al estado
bloqueado hasta que el hilo consum idor extraiga uno o m ás datos y, p o r consi­
guiente, increm ente elem entosVacíos. Un razonam iento análogo haríam os para el
consum idor.

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

public void term inar()


I
continuar = false;

//////////////////////////////////////////////////////////////////

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;

public void run()


I
i n t nú me r o :
w hile (continuar)
I
n ú mero - m a t r i z . o b t e n e r ( ) ;
/ / System .out.p r in t ln ( "C o n s u m id o r ” + getNamet) +
// " obtuvo: nú me r o " + n ú m e r o ) :
I
I
CA PÍTU LO 15: HILOS 6 8 3

p u b lic void te rm inarí)


I
continuar = false:
I
I
//////////////////////////////////////////////////////////////////

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

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 ] para c o n t i n u a r y " ) :


S y s t e m . o u t . p r i n t l n ( " v u e l v a a p u l s a r [ E n t r a r ] para 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
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

com o resultado el nom bre de los m étodos de ordenación colocados de m ás rápido


a m enos rápido.

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.

5. G rabar el resultado obtenido en el punto 4 en otro fichero. D espués, visualizar


el fichero para com probar el resultado obtenido.
A PÉN D IC E F
©F.J.Cebalos/RA-MA

Í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

atributos (continuación) m anipular, 173


de la clase, 78 C ardL ayout, 731
iniciar, 255 cast, 51
static, 289 catch, 95; 403
AudioClip, 716 ceil, 115
available, 114 CGI, 703
aw t.7 1 0 ; 723 ciclo de vida, 646
clase, 25. 253
abstracta, 92; 330
B anidada, 381
barra de direcciones, 695 anónim a, 384
barra de estado, mensajes, 718 aplicación, 30
biblioteca de clases, 15 A pplet, 707
bin. 10 A rrays, 233
binarySearch, 233 base. 329
bit, 4 B ufferedlnputStream . 98
bloque, 72 B ufferedReader. 99; 613
bloque de finalización, 405 B ufferedW riter, 613
bloqueado, 637 C alendar. 263
boolean, 27; 43; 103 CArboIBinB, 555
BorderLayout, 731 C A rbolBinE, 559
borrar los elem entos de una lista, 504 C lass. 97
borrar nodo, 553 C olor, 713
borrar un elem ento de una lista, 503 crear, 26
BoxLayout, 731 D atalnputStream , 442
Boyer y M oore, 602 D ataO utputStream , 441
break, 131; 146 Date, 74; 232
BufferedlnputStream , 98 D ateForm at, 232
BufferedReader, 99; 613 de un objeto, 97
B ufferedW riter, 613 D ecim alForm at, 226
burbuja, 592 dentro de un m étodo, 383
buscar nodo, 550 derivada, 329
buscar un elem ento en una lista, 504 derivada de otra ya existente, 429
buscar un elem ento en una m atriz, 299 E O FException, 404
búsqueda binaria, 233; 601 Error, 413
búsqueda de cadenas, 602 E xcepción, 400
búsqueda secuencial, 600 FieldPosition, 228
byte, f , 41; 103 File. 434
FilelnputStream . 433
FileOutputStream , 430
c FileReader, 439
cadenas de caracteres, 175 FileW riter, 438
concatenar, 46 final, 268
leer y escribir, 176 Font. 713
Calendar, 263 Form at, 226
campo, 420 genérica, 509
capacity, 187 G raphics, 708
capas de servicios de la red, 689 G regorianCalendar, 263
carácter siguiente en el flujo de entrada, 427 H ttpServlet, 738
caracteres, 38 InputStream . 93
\r\n , 112
InputStream Reader, 99
disponibles en un flujo, 114 interna, 382
IO Exception, 400
A PÉN D IC E F: ÍN D IC E 7 7 1

clase (continuación) C olor. 713


JA pplet, 734 C ollection, 566
JButton, 727 com entario, 47
JComboBox, 727 com pareTo, 182
JCheckBox. 727 com pareToIgnoreC ase, 183
JFram e, 724 com pilación, 14
JLabel, 727 com pilador, 6
JO ptionPane, 727 com pilador J1T, 8
JRadioB utton. 727 com plejos, 326
JScrolIBar, 727 com ponente S w in g , 726
JTextA rea. 727 com portam iento, 27
JTextField. 727 concat, 182
L eer. 106; 415 concatenar, 46
LinkedList, 519 constante simbólica, 48
lista lineal sim plem ente enlazada, 505 constructor, 34; 74, 269
Lócale. 228 constructor copia, 276
M ath, 114 constructor de la superclase, invocar, 345
M essageForm at. 233 constructor de una subclase, 345
M ethod, 97 constructor por om isión, 270; 272
N umberFormat, 226 consulta dinám ica, 371
O bject, 92; 97; 168; 235; 287; 509; 758 contador, 174
O bjectlnputStream , 451 contenedores, 729
O bjectO utputStream . 450 de nivel interm edio, 730
OutOfMemoryErTor. 413 de nivel superior, 730
OutputStream , 94 continué, 146
PrintStream . 100 control de acceso a una clase, 265
PrintW riter. 102 control el acceso, 336
pública, guardar, 67 conversión, 51
PushbackReader. 425 de cadenas a núm eros, 191
Random , 241 explícita en subclases, 359; 511
Reader. 93 im plícita en subclases, 357
Runtim eException, 400 copiar una matriz num érica, 217
Sim pleD ateForm at, 231 correo electrónico, 692
String. 27; 181 eos, 115
StringBuffer, 186 CR, 112
System . 69 crear un objeto, 73
Thread, 638 crear un paquete, 304
Throw abie. 95; 399 crear una clase, 26
TreeM ap. 567 crear una nueva excepción, 408
TreeSet, 566 CRLF. 112
W riter, 94 cuanto, 650
clases con ficheros, 480 currentTim eM illis, 629
clases Hash..., 567 char, 42
clases para tipos primitivos, 103 Character, 103
class, 26; 97 CharA rrayR eader, 423
CLA SSPA TH , 110; 305 CharA rrayW riter, 423
CListaLinealSE, 512; 523; 534 charA t. 185; 189
clone. 168;218
cióse. 425; 432; 458
códigos de bytes, 7
D
cola, 529 D atalnput, 442
colecciones de objetos, 566 D atalnputStream , 442
colector de basura, 83 DataO utput, 441
772 JA VA : C U R SO DE PROGRAM A CIÓN

D ataOuipulStream . 441 estructuras dinám icas, 495


Date. 74; 232 etiquetas, 147
DateFormat, 232 eventos. 727
DecimalFormat. 226 excepción
declaración, 70 atrapar, 403
declarar las excepciones. 407 declarar. 406
definición de un m étodo, 72 lanzar. 402
definir una subclase, 338 manejar, 401
delete, 188:436 utilizar, 413
dem onio, 643 excepciones, 95; 397
depuración, 16 crear, 408
destroy, 709 explícitas. 400
destructor. 34; 276 im plícitas, 400
destructor en una subclase, 347 Excepción, 400
dirección de Internet, 691 exists, 435
dirección IP. 691 exp, 115
diseño, adm inistradores. 730 expresiones booleanas, 54
dispositivo de salida, 26 expresión, 51
dispositivos estándar. 472 extcheck, 10
DNS, 690 extends, 335; 372
d o ... while, 139 extranet, 689
doG et, 739
dom inio, 690
doPost. 739
F
double. 43; 103 fecha/hora. 263
doubleV alue, 104 fichero, 420
draw lm age, 715 leer líneas de texto, 477
draw String, 721 fichero tem poral, 471
FieldPosition, 228
E File. 434
FilelnputStream , 433
ejecución, 14; 637 FileOutputStream , 430
elim inar un elem ento de una m atriz. 298 FileReader, 439
else if, 126 FileW riter, 438
e-m ail, 692 filtros, 441
encapsulación, 77 fill. 234
encapsulam iento, 33 fin de fichero, 110
endsW ith, 184 final. 48; 268
enfocado, 728 finalize. 237
enlaces, 699 finalize en una subclase, 347
ensam blador, 4 finally, 405
entorno de desarrollo integrado, 11; 16 float. 42; 103
EOFException, 404 floatV alue. 104
equals, 168; 234; 236; 287 floor. 115
err, 96 F low Layout, 731
Error, 400 flujo, 91; 421
escuchadores de eventos, 727 limpiar, 427
estados de un hilo, 636 flujos
estados de un proceso, 634 de bytes, 430
estructura de una aplicación gráfica, 724 de caracteres, 437
estructura else if, 126 d e datos, 440
estructura interna, 255 estándar, 96
estructuras abstractas de datos. 496 flush, 102
A PÉN D IC E F: ÍNDICE 7 7 3

foco, 728 m últiple, 34; 335; 379


FocusListener, 728 simple, 33; 335
Font. 713 hilo. 635
for. 142 ¿está vivo?, 647
Form al, 226 creado, 641
formato, 225 crear, 641
alineación, 228 egoísta. 654
para fechas y horas, 2 3 1 prioridad, 651
para mensajes, 233 term inar, 644
sím bolos, 226 hilos
FTP. 6 9 1 .6 9 3 cooperantes, 655
fuentes. 713 espera activa. 678
espera pasiva, 678
estados, 636
G independientes, 654
garbage col lector, 279 sincronizar, 655
ge, 279 hipertexto, 699
G ET, 739 HTM L, 695
getBytes, 186 etiquetas, 696
getClass, 97 H ttpServlet, 738
getC odeB ase, 715
getContentPane, 730
I
getCrossPlatform LookA ndFeelClassN am e, 731
getC urrencylnstance, 227 identificadores, 46
getD atelnstance, 232 lEEErem ainder, 115
getD ocum entBase, 715 if, 121
getEndlndex, 228 if anidados, 124
getlm age, 715 im agen en un applet, 714
getM essage, 403 im ágenes en un docum ento HTM L, 701
getM ethods, 97 im plem entación de una clase. 259
getN am e, 97 im plem ents, 374
getN um berlnstance, 227 import. 69
getRarameter, 712 in. 96
getPercentlnstance, 227 indexOf, 185
getPriority, 651 iniciador estático, 293
getSystem LookA ndFeelClassN am e, 731 init, 708; 739
getTim e. 242 inorden, 544
getTim elnstance, 232 InputStream , 93
G opher, 691 InputStream Reader, 99
gráficos en un docum ento HTM L. 701 inserción. 595
Graphics, 708 insert, 187
G regorianCalendar, 263 insertar nodo. 5 5 1
G ridBagLayout, 7 3 1 insertar un elem ento en una lista, 500
G ridLayout, 731 instanceof, 530
grupos de noticias, 693 instancia, 25
int. 41
Integer. 103
H
interfaces, 372
Hanoi, 589 gráficas, 723
hash. 616 múltiples, 381
H ash..., 567 interfaz, 34; 256; 269
herencia, 33; 329; 331 A udioClip, 716
D atalnput, 442
774 JA V A . C U R SO DE PROGRAM ACIÓN

interfaz (continuación) JLabel. 727


DataOutput. 441 JO ptionPane. 727
definición. 371 JRadioB utton, 727
para qué sirve, 380 jre, 11; 649
pública. 77 JScrollB ar. 727
Runnable. 639; 641 JSP, 737
Serializable, 449 JSW D K, 742
SingleThreadM odel, 740 JTextA rea, 727
tipo de datos, 378 JTextField. 727
utilizar, 374
vs. clase abstracta, 377
K
intem , 240
Internet. 688 kem el, 636
Internet Explorer. 694 K eyListener, 728
intérprete.6
interrupt, 667
intranet, 689 L
intValue. 104 lang, 69
invocar a un método redefinido. 359 lanzar una excepción, 402
IOException, 95; 400 lastlndexO f. 185
isAlive. 647 leer, 104
ISAPI. 704 leer líneas de texto, 477
isDirectory, 436 leer una línea de texto, 99; 428
isFile. 436 Leer, clase. 415
isNaN. 206 length, 168; 184; 187
ItcmListener, 728 lenguaje m áquina, 5
lenguajes de alto nivel, 5
J LF, 112
lib. 11
JA pplet, 730; 734 lim piar un flujo, 427
jar. 10 LinkedList. 519
Java, 7; 10 List, 566
Java 2D, 723 lista circular, 522
Java Runtime Environm ent, 649 lista circular doblem ente enlazada, 534
java.io. 89; 421 lista doblem ente enlazada, 533
java.lang. 69; 89 lista lineal sim plem ente enlazada, 496
java.tcxt. 226 lista lineal sim plem ente enlazada, 496
java.útil. 74; 228 lista lineal, recorrer, 504
javac. 10 listas lineales. 496
javadoc. 10 literal. 43
javah, 10 de cadena de caracteres. 45
j a v a p .10 de un solo carácter, 45
javax.servlet. 738 entero. 44
JB utton, 727 real, 44
JC om boBox, 727 Lócale, 228
JC heckB ox, 727 log, 115
jdb, 10 long. 41; 103
JD ialog, 730 longitud de una m atriz, 168
JDK, 10; 759 longValue. 104
jerarquía de clases, 329; 336; 350 loop, 716
J F C .723 L P T I.4 7 2
JFratne. 724
JIT. 8; 11
A P É N D IC E F: ÍN D IC E 775

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

a un tipo primitivo, 219 setLayout, 731


final, 268 setLength, 187
referencias a subclases, 356 setLookA ndFeel, 731
referencias y objetos String, 238 setPriority, 651
reflexión, 98 short, 41; 103
registro, 420 show Status, 718
repaint, 708; 719 Sim pleD ateForm at, 231
replace, 185; 188 sin, 115
representación interna, 269 sincronización d e hilos, 655
reset, 613 exclusión mutua, 663
resultado, 72 secciones críticas, 655
return, 73 SingleThreadM odel, 740
reverse, 188 sistem a de nom bres de dom inio, 690
rint, 115 skip, 114
r\n, 112 sleep, 667
round, 115 S M T P . 692
round-robin. 650 sobrecarga de m étodos. 262
run, 640 sobrecarga del operador +, 309
Runnable, 639; 641 sonido en un applet. 716
Runtim eException, 400 sonido en una aplicación, 717
sort, 235
sqrt, 115
s start, 641; 708
saltar n caracteres en un flujo, 114 startsW ith, 184
sección crítica, 659 starvation, 650; 654
secciones criticas, 655 static, 48; 78; 289; 290
secuencia de escape, 39 static iniciador, 293
seguridad en los applets, 722 stop, 708; 716
sentencia stream, 421
break, 146 String, 27; 90; 181
com puesta, 72 String, constructor, 181
continué, 146 StringBuffer, 186
de asignación, 90 subclase, 329; 335
d o ... while, 139 subdom inio, 690
for, 142 subproceso, 635
if, 121 substring, 185; 189
im port, 69 super, 339; 342; 344; 345
return, 73 superclase, 329
sim ple, 7 i directa, 354
sw itch, 129 indirecta. 354
while. 133 Sw ing, 723
seriación, 449 switch, 129
Serializable, 449 synchronized. 659
servidor de nom bres, 691 System, 13; 26; 69
servidor de servlets, 742 System.err, 96
servidor W eb de Java, 742 System .in, 96
servlet, 737 System .out, 96
ejecutar, 743
estructura, 738 T
Set. 566
setColor. 713 tan. 115
setCharAt, 189 TCP/IP, 689
setFont, 713 T elnet, 692
778 J A V A : C U R S O D E P R O G R A M A C IÓ N

this. 266; 344


Thread. 638
u
throw. 402 U IM anager, 731
Throw able. 95; 399 Unicode. 38; 42
throws. 407 unread. 425
tiem po de ejecución. 629 update. 708
tim e-slice, 650 URL. 699
tipo URL de la carpeta, 715
boolean, 43 USENET. 692; 693
byte, 41 UTF-8, 441
char. 42 Util. 74
double, 43
float, 42
int, 41
V
long, 41 valueO f. 104; 186
referenciado, 80 variable. 49
short, 41 CLA SSPA TH . 110
String, 90 iniciar, 50
tipos primitivos, 40 local, 86
tipos referenciados, 43 m iem bro de una clase, 86
toCharA rray, 186 void. 28; 72
toDegrees, 115
toLow erCase, 184
toRadians, 115 w
torres de H anoi, 589
wait. 663; 667
toString, 104; 182; 189; 237 W eb. 693
toU pperCase. 184 w hile. 133
TreeM ap. 567
w hile, do. o for anidados, 136
TreeSet, 566 w indow Closing, 725
trim, 184
W indow Listener, 728
try. 95; 403
W orld W ide W eb. 692; 693
try ... catch, 148
w ritc, 94; 431; 613
W riter, 94
w riteU TF. 463
W W W , 692; 693
N ota del escaneador

E scaneado en ab ril del 2005 en Toledo (España)


D iscu lp en la calidad, ya que el libro está m u y tocado y es de la biblioteca.

E ste libro es m u y bueno, es de Jav ier C eballos, si te gusta, p o r favor, c ó m p ra te lo !.


A hora m ism o cuesta 42 euros.

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.

E spero que este libro os sirva para algo.

También podría gustarte