SlideShare una empresa de Scribd logo
3
Lo más leído
9
Lo más leído
10
Lo más leído
®
I
N
C
LUYE CD-R
O
M
H
o
ssFly
™
™
™
Visítenos en:
www.pearsoneducacion.net
ISBN 978-970-26-1190-5
®
Una introducción completa y autorizada del código activo de DEITEL® a la pro-
gramación orientada a objetos, con la nueva edición Java™ Standard Edition 6,
JDBC™ 4, JavaServer Faces y Servicios Web
¡Java™ es el lenguaje de programación orientada a objetos más popular, con cinco
millones de desarrolladores!
Esta nueva edición del libro de texto sobre Java más utilizado en el mundo emplea un
método anticipado para las clases y objetos. Incluye también una cobertura completa
de la programación orientada a objetos en Java, para lo cual presenta varios ejemplos
prácticos integrados: la clase Tiempo, la clase Empleado, la clase LibroCalificaciones,
un ejemplo práctico opcional de DOO/UML™ 2 con el ATM (capítulos 1 a 8 y 10),
el ejemplo práctico opcional de GUI y gráficos (capítulos 3 a 10), un libro de direc-
ciones controlado por base de datos (capítulo 25) y dos aplicaciones Web multinivel
controladas por bases de datos: una libreta de direcciones que utiliza controles JSF
habilitados para AJAX para mostrar un nombre y una dirección en un Mapa de
Google™ (capítulo 27), y un sistema de reservaciones de una aerolínea que utiliza ser-
vicios Web (capítulo 28).
Los recursos para los usuarios de este libro incluyen los sitios Web (www.deitel.com y
www.pearsoeducacion.net/deitel) con los ejemplos de código del libro e información
para profesores, estudiantes y profesionales.
El CD de este libro incluye material adicional en español y códigos de los ejemplos del
libro.
Para mayor información visite:
www.pearsoneducacion.net/deitel
Deitel Java.qxp 4/29/08 9:47 AM Page 1
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
™
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
P. J. Deitel
Deitel & Associates, Inc.
H. M. Deitel
Deitel & Associates, Inc.
TRADUCCIÓN
Alfonso Vidal Romero Elizondo
Ingeniero en Sistemas Electrónicos
Instituto Tecnológico y de Estudios Superiores de Monterrey
Campus Monterrey
REVISIÓN TÉCNICA
Gabriela Azucena Campos García
Roberto Martínez Román
Departamento de Computación
Instituto Tecnológico y de Estudios Superiores de Monterrey
Campus Estado de México
Jorge Armando Aparicio Lemus
Coordinador del Área de Software
Universidad Tecnológica de El Salvador
™
Authorized translation from the English language edition entitled Java™ How to Program, 7th Edition, by Deitel & Associates (Harvey & Paul),
published by Pearson Education, Inc., publishing as Prentice Hall, Inc., Copyright © 2007. All rights reserved.
ISBN 0-13-222220-5
Traducción autorizada de la edición en idioma inglés titulada Java™ How to Program, 7a Edición, por Deitel & Associates (Harvey & Paul),
publicada por Pearson Education, Inc., publicada como Prentice Hall, Inc., Copyright © 2007. Todos los derechos reservados.
Esta edición en español es la única autorizada.
Edición en español
Editor: Luis Miguel Cruz Castillo
e-mail: luis.cruzpearsoned.com
Editor de desarrollo: Bernardino Gutiérrez Hernández
Supervisor de producción: Enrique Trejo Hernández
Edición en inglés
DEITEL, PAUL J. Y HARVEY M. DEITEL
CÓMO PROGRAMAR EN JAVA. Séptima edición
PEARSON EDUCACIÓN, México 2008
ISBN: 978-970-26-1190-5
Área: Computación
Formato: 20 × 25.5 cm Páginas: 1152
Vice President and Editorial Director, ECS: Marcia J. Horton
Associate Editor: Jennifer Cappello
Assistant Editor: Carole Snyder
Executive Managing Editor: Vince O’Brien
Managing Editor: Bob Engelhardt
Production Editors: Donna M. Crilly, Marta Samsel
Director of Creative Services: Paul Belfanti
A/V Production Editor: Xiaohong Zhu
Art Studio: Artworks, York, PA
Creative Director: Juan López
Art Director: Kristine Carney
Cover Design: Abbey S. Deitel, Harvey M. Deitel, Francesco
Santalucia, Kristine Carney
Interior Design: Harvey M. Deitel, Kristine Carney
Manufacturing Manager: Alexis Heydt-Long
Manufacturing Buyer: Lisa McDowell
Executive Marketing Manager: Robin O’Brien
SÉPTIMA EDICIÓN, 2008
D.R. © 2008 por Pearson Educación de México, S.A. de C.V.
Atlacomulco 500-5o. piso
Col. Industrial Atoto
53519, Naucalpan de Juárez, Estado de México
Cámara Nacional de la Industria Editorial Mexicana. Reg. Núm. 1031.
Prentice Hall es una marca registrada de Pearson Educación de México, S.A. de C.V.
Reservados todos los derechos. Ni la totalidad ni parte de esta publicación pueden reproducirse, registrarse o transmitirse, por un sistema de
recuperación de información, en ninguna forma ni por ningún medio, sea electrónico, mecánico, fotoquímico, magnético o electroóptico,
por fotocopia, grabación o cualquier otro, sin permiso previo por escrito del editor.
El préstamo, alquiler o cualquier otra forma de cesión de uso de este ejemplar requerirá también la autorización del editor o de sus represen-
tantes.
ISBN 10: 970-26-1190-3
ISBN 13: 978-970-26-1190-5
Impreso en México. Printed in Mexico.
1 2 3 4 5 6 7 8 9 0 - 11 10 09 08
®
A Vince O’Brien,
Director de Administración de Proyectos, Prentice Hall.
Es un privilegio para nosotros trabajar con un profesional consumado.
Nuestros mejores deseos para tu éxito continuo.
Paul y Harvey
Marcas registradas
DEITEL, el insecto con dos pulgares hacia arriba y DIVE INTO son marcas registradas de Deitel and Associates, Inc.
Java y todas las marcas basadas en Java son marcas registradas de Sun Microsystems, Inc., en los Estados Unidos y otros países.
Pearson Education es independiente de Sun Microsystems, Inc.
Microsoft, Internet Explorer y el logotipo de Windows son marcas registradas de Microsoft Corporation en los Estados Uni-
dos y/o en otros países
UNIX es una marca registrada de The Open Group.
Contenido
Prefacio xix
Antes de empezar xxx
1 Introducción a las computadoras, Internet y Web 1
1.1 Introducción 2
1.2 ¿Qué es una computadora? 4
1.3 Organización de una computadora 4
1.4 Los primeros sistemas operativos 5
1.5 Computación personal, distribuida y cliente/servidor 5
1.6 Internet y World Wide Web 6
1.7 Lenguajes máquina, ensambladores y de alto nivel 6
1.8 Historia de C y C++ 7
1.9 Historia de Java 8
1.10 Bibliotecas de clases de Java 8
1.11 FORTRAN, COBOL, Pascal y Ada 9
1.12 BASIC, Visual Basic, Visual C++, C# y .NET 10
1.13 Entorno de desarrollo típico en Java 10
1.14 Generalidades acerca de Java y este libro 13
1.15 Prueba de una aplicación en Java 14
1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML 19
1.17 Web 2.0 23
1.18 Tecnologías de software 24
1.19 Conclusión 25
1.20 Recursos Web 25
2 Introducción a las aplicaciones en Java 34
2.1 Introducción 35
2.2 Su primer programa en Java: imprimir una línea de texto 35
2.3 Modificación de nuestro primer programa en Java 41
2.4 Cómo mostrar texto con printf 43
2.5 Otra aplicación en Java: suma de enteros 44
2.6 Conceptos acerca de la memoria 48
2.7 Aritmética 49
2.8 Toma de decisiones: operadores de igualdad y relacionales 52
2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento
de requerimientos de un problema 56
2.10 Conclusión 65
3 Introducción a las clases y los objetos 75
3.1 Introducción 76
3.2 Clases, objetos, métodos y variables de instancia 76
viii Contenido
3.3 Declaración de una clase con un método e instanciamiento de un objeto de una clase 77
3.4 Declaración de un método con un parámetro 81
3.5 Variables de instancia, métodos establecer y métodos obtener 84
3.6 Comparación entre tipos primitivos y tipos por referencia 88
3.7 Inicialización de objetos mediante constructores 89
3.8 Números de punto flotante y el tipo double 91
3.9 (Opcional) Ejemplo práctico de GUI y gráficos: uso de cuadros de diálogo 95
3.10 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un
documento de requerimientos 98
3.11 Conclusión 105
4 Instrucciones de control: parte 1 112
4.1 Introducción 113
4.2 Algoritmos 113
4.3 Seudocódigo 114
4.4 Estructuras de control 114
4.5 Instrucción de selección simple if 116
4.6 Instrucción de selección doble if...else 117
4.7 Instrucción de repetición while 121
4.8 Cómo formular algoritmos: repetición controlada por un contador 123
4.9 Cómo formular algoritmos: repetición controlada por un centinela 127
4.10 Cómo formular algoritmos: instrucciones de control anidadas 134
4.11 Operadores de asignación compuestos 138
4.12 Operadores de incremento y decremento 139
4.13 Tipos primitivos 142
4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples 142
4.15 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de los atributos de las clases 146
4.16 Conclusión 150
5 Instrucciones de control: parte 2 164
5.1 Introducción 165
5.2 Fundamentos de la repetición controlada por contador 165
5.3 Instrucción de repetición for 167
5.4 Ejemplos sobre el uso de la instrucción for 171
5.5 Instrucción de repetición do...while 174
5.6 Instrucción de selección múltiple switch 176
5.7 Instrucciones break y continue 183
5.8 Operadores lógicos 185
5.9 Resumen sobre programación estructurada 190
5.10 (Opcional) Ejemplo práctico de GUI y gráficos: dibujo de rectángulos y óvalos 194
5.11 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo identificar los estados
y actividades de los objetos 197
5.12 Conclusión 200
6 Métodos: un análisis más detallado 211
6.1 Introducción 212
6.2 Módulos de programas en Java 212
6.3 Métodos static, campos static y la clase Math 214
6.4 Declaración de métodos con múltiples parámetros 216
6.5 Notas acerca de cómo declarar y utilizar los métodos 219
6.6 Pila de llamadas a los métodos y registros de activación 221
6.7 Promoción y conversión de argumentos 221
Contenido ix
6.8 Paquetes de la API de Java 222
6.9 Ejemplo práctico: generación de números aleatorios 224
6.9.1 Escalamiento y desplazamiento generalizados de números aleatorios 227
6.9.2 Repetitividad de números aleatorios para prueba y depuración 228
6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones) 228
6.11 Alcance de las declaraciones 232
6.12 Sobrecarga de métodos 235
6.13 (Opcional) Ejemplo práctico de GUI y gráficos: colores y figuras rellenas 238
6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones
de las clases 241
6.15 Conclusión 246
7 Arreglos 260
7.1 Introducción 261
7.2 Arreglos 261
7.3 Declaración y creación de arreglos 262
7.4 Ejemplos acerca del uso de los arreglos 264
7.5 Ejemplo práctico: simulación para barajar y repartir cartas 272
7.6 Instrucción for mejorada 274
7.7 Paso de arreglos a los métodos 276
7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para
almacenar las calificaciones 279
7.9 Arreglos multidimensionales 284
7.10 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo bidimensional 288
7.11 Listas de argumentos de longitud variable 293
7.12 Uso de argumentos de línea de comandos 294
7.13 (Opcional) Ejemplo práctico de GUI y gráficos: cómo dibujar arcos 296
7.14 (Opcional) Ejemplo práctico de Ingeniería de Software: colaboración entre los objetos 299
7.15 Conclusión 305
8 Clases y objetos: un análisis más detallado 325
8.1 Introducción 326
8.2 Ejemplo práctico de la clase Tiempo 327
8.3 Control del acceso a los miembros 330
8.4 Referencias a los miembros del objeto actual mediante this 331
8.5 Ejemplo práctico de la clase Tiempo: constructores sobrecargados 333
8.6 Constructores predeterminados y sin argumentos 338
8.7 Observaciones acerca de los métodos Establecer y Obtener 338
8.8 Composición 340
8.9 Enumeraciones 342
8.10 Recolección de basura y el método finalize 345
8.11 Miembros de clase static 345
8.12 Declaración static import 350
8.13 Variables de instancia final 351
8.14 Reutilización de software 353
8.15 Abstracción de datos y encapsulamiento 354
8.16 Ejemplo práctico de la clase Tiempo: creación de paquetes 355
8.17 Acceso a paquetes 360
8.18 (Opcional) Ejemplo práctico de GUI y gráficos: uso de objetos con gráficos 361
8.19 (Opcional) Ejemplo práctico de Ingeniería de Software: inicio de la programación de las clases
del sistema ATM 364
8.20 Conclusión 369
x Contenido
9 Programación orientada a objetos: herencia 378
9.1 Introducción 379
9.2 Superclases y subclases 380
9.3 Miembros protected 382
9.4 Relación entre las superclases y las subclases 382
9.4.1 Creación y uso de una clase EmpleadoPorComision 383
9.4.2 Creación de una clase EmpleadoBaseMasComision sin usar la herencia 387
9.4.3 Creación de una jerarquía de herencia EmpleadoPorComision-
EmpleadoBaseMasComision 391
9.4.4 La jerarquía de herencia EmpleadoPorComision-EmpleadoBaseMasComision
mediante el uso de variables de instancia protected 394
9.4.5 La jerarquía de herencia EmpleadoPorComision-EmpleadoBaseMasComision
mediante el uso de variables de instancia private 399
9.5 Los constructores en las subclases 404
9.6 Ingeniería de software mediante la herencia 409
9.7 La clase object 410
9.8 (Opcional) Ejemplo práctico de GUI y gráficos: mostar texto e imágenes usando etiquetas 411
9.9 Conclusión 413
10 Programación orientada a objetos: polimorfismo 417
10.1 Introducción 418
10.2 Ejemplos del polimorfismo 419
10.3 Demostración del comportamiento polimórfico 420
10.4 Clases y métodos abstractos 423
10.5 Ejemplo práctico: sistema de nómina utilizando polimorfismo 425
10.5.1 Creación de la superclase abstracta Empleado 426
10.5.2 Creación de la subclase concreta EmpleadoAsalariado 426
10.5.3 Creación de la subclase concreta EmpleadoPorHoras 429
10.5.4 Creación de la subclase concreta EmpleadoPorComision 431
10.5.5 Creación de la subclase concreta indirecta EmpleadoBaseMasComision 432
10.5.6 Demostración del procesamiento polimórfico, el operador instanceof y
la conversión descendente 433
10.5.7 Resumen de las asignaciones permitidas entre variables de la superclase y de la subclase 437
10.6 Métodos y clases final 438
10.7 Ejemplo práctico: creación y uso de interfaces 439
10.7.1 Desarrollo de una jerarquía PorPagar 440
10.7.2 Declaración de la interfaz PorPagar 441
10.7.3 Creación de la clase Factura 441
10.7.4 Modificación de la clase Empleado para implementar la interfaz PorPagar 443
10.7.5 Modificación de la clase EmpleadoAsalariado para usarla en la jerarquía PorPagar 445
10.7.6 Uso de la interfaz PorPagar para procesar objetos Factura y Empleado
mediante el polimorfismo 446
10.7.7 Declaración de constantes con interfaces 448
10.7.8 Interfaces comunes de la API de Java 448
10.8 (Opcional) Ejemplo práctico de GUI y gráficos: realizar dibujos mediante el polimorfismo 449
10.9 (Opcional) Ejemplo práctico de Ingeniería de Software: incorporación de la herencia en
el sistema ATM 451
10.10 Conclusión 457
Contenido xi
11 Componentes de la GUI: parte 1 462
11.1 Introducción 463
11.2 Entrada/salida simple basada en GUI con JOptionPane 464
11.3 Generalidades de los componentes de Swing 467
11.4 Mostrar texto e imágenes en una ventana 469
11.5 Campos de texto y una introducción al manejo de eventos con clases anidadas 474
11.6 Tipos de eventos comunes de la GUI e interfaces de escucha 479
11.7 Cómo funciona el manejo de eventos 481
11.8 JButton 483
11.9 Botones que mantienen el estado 486
11.9.1 JCheckBox 486
11.9.2 JRadioButton 489
11.10 JComboBox y el uso de una clase interna anónima para el manejo de eventos 492
11.11 JList 495
11.12 Listas de selección múltiple 497
11.13 Manejo de eventos de ratón 500
11.14 Clases adaptadoras 504
11.15 Subclase de JPanel para dibujar con el ratón 507
11.16 Manejo de eventos de teclas 510
11.17 Administradores de esquemas 513
11.17.1 FlowLayout 514
11.17.2 BorderLayout 517
11.17.3 GridLayout 520
11.18 Uso de paneles para administrar esquemas más complejos 522
11.19 JTextArea 523
11.20 Conclusión 526
12 Gráficos y Java 2D™ 539
12.1 Introducción 540
12.2 Contextos y objetos de gráficos 542
12.3 Control de colores 542
12.4 Control de tipos de letra 548
12.5 Dibujo de líneas, rectángulos y óvalos 554
12.6 Dibujo de arcos 558
12.7 Dibujo de polígonos y polilíneas 560
12.8 La API Java 2D 563
12.9 Conclusión 569
13 Manejo de excepciones 578
13.1 Introducción 579
13.2 Generalidades acerca del manejo de excepciones 580
13.3 Ejemplo: división entre cero sin manejo de excepciones 580
13.4 Ejemplo: manejo de excepciones tipo ArithmeticException e InputMismatchException 582
13.5 Cuándo utilizar el manejo de excepciones 587
13.6 Jerarquía de excepciones en Java 587
13.7 Bloque finally 590
13.8 Limpieza de la pila 594
13.9 printStackTrace, getStackTrace y getMessage 595
13.10 Excepciones encadenadas 597
13.11 Declaración de nuevos tipos de excepciones 599
13.12 Precondiciones y poscondiciones 600
xii Contenido
13.13 Aserciones 601
13.14 Conclusión 602
14 Archivos y flujos 608
14.1 Introducción 609
14.2 Jerarquía de datos 610
14.3 Archivos y flujos 611
14.4 La clase File 613
14.5 Archivos de texto de acceso secuencial 617
14.5.1 Creación de un archivo de texto de acceso secuencial 617
14.5.2 Cómo leer datos de un archivo de texto de acceso secuencial 623
14.5.3 Ejemplo práctico: un programa de solicitud de crédito 625
14.5.4 Actualización de archivos de acceso secuencial 630
14.6 Serialización de objetos 630
14.6.1 Creación de un archivo de acceso secuencial mediante el uso de la serialización
de objetos 631
14.6.2 Lectura y deserialización de datos de un archivo de acceso secuencial 636
14.7 Clases adicionales de java.io 638
14.8 Abrir archivos con JFileChooser 640
14.9 Conclusión 643
15 Recursividad 653
15.1 Introducción 654
15.2 Conceptos de recursividad 655
15.3 Ejemplo de uso de recursividad: factoriales 655
15.4 Ejemplo de uso de recursividad: serie de Fibonacci 658
15.5 La recursividad y la pila de llamadas a métodos 661
15.6 Comparación entre recursividad e iteración 662
15.7 Las torres de Hanoi 664
15.8 Fractales 666
15.9 “Vuelta atrás” recursiva (backtracking) 676
15.10 Conclusión 676
15.11 Recursos en Internet y Web 676
16 Búsqueda y ordenamiento 685
16.1 Introducción 686
16.2 Algoritmos de búsqueda 687
16.2.1 Búsqueda lineal 687
16.2.2 Búsqueda binaria 690
16.3 Algoritmos de ordenamiento 695
16.3.1 Ordenamiento por selección 695
16.3.2 Ordenamiento por inserción 699
16.3.3 Ordenamiento por combinación 702
16.4 Invariantes 708
16.5 Conclusión 709
17 Estructuras de datos 714
17.1 Introducción 715
17.2 Clases de envoltura de tipos para los tipos primitivos 716
17.3 Autoboxing y autounboxing 716
Contenido xiii
17.4 Clases autorreferenciadas 717
17.5 Asignación dinámica de memoria 717
17.6 Listas enlazadas 718
17.7 Pilas 726
17.8 Colas 730
17.9 Árboles 733
17.10 Conclusión 739
18 Genéricos 761
18.1 Introducción 762
18.2 Motivación para los métodos genéricos 762
18.3 Métodos genéricos: implementación y traducción en tiempo de compilación 764
18.4 Cuestiones adicionales sobre la traducción en tiempo de compilación: métodos que utilizan
un parámetro de tipo como tipo de valor de retorno 767
18.5 Sobrecarga de métodos genéricos 770
18.6 Clases genéricas 770
18.7 Tipos crudos (raw) 779
18.8 Comodines en métodos que aceptan parámetros de tipo 783
18.9 Genéricos y herencia: observaciones 787
18.10 Conclusión 787
18.11 Recursos en Internet y Web 787
19 Colecciones 792
19.1 Introducción 793
19.2 Generalidades acerca de las colecciones 794
19.3 La clase Arrays 794
19.4 La interfaz Collection y la clase Collections 797
19.5 Listas 798
19.5.1 ArrayList e Iterator 799
19.5.2 LinkedList 800
19.5.3 Vector 805
19.6 Algoritmos de las colecciones 808
19.6.1 El algoritmo sort 809
19.6.2 El algoritmo shuffle 812
19.6.3 Los algoritmos reverse, fill, copy, max y min 815
19.6.4 El algoritmo binarySearch 816
19.6.5 Los algoritmos addAll, frequency y disjoint 818
19.7 La clase Stack del paquete java.util 820
19.8 La clase PriorityQueue y la interfaz Queue 822
19.9 Conjuntos 823
19.10 Mapas 826
19.11 La clase Properties 829
19.12 Colecciones sincronizadas 832
19.13 Colecciones no modificables 833
19.14 Implementaciones abstractas 834
19.15 Conclusión 834
20 Introducción a los applets de Java 841
20.1 Introducción 842
20.2 Applets de muestra incluidos en el JDK 842
20.3 Applet simple en Java: cómo dibujar una cadena 846
xiv Contenido
20.3.1 Cómo ejecutar un applet en el appletviewer 848
20.3.2 Ejecución de un applet en un explorador Web 850
20.4 Métodos del ciclo de vida de los applets 850
20.5 Cómo inicializar una variable de instancia con el método int 851
20.6 Modelo de seguridad “caja de arena” 853
20.7 Recursos en Internet y Web 853
20.8 Conclusión 854
21 Multimedia: applets y aplicaciones 858
21.1 Introducción 859
21.2 Cómo cargar, mostrar y escalar imágenes 860
21.3 Animación de una serie de imágenes 862
21.4 Mapas de imágenes 867
21.5 Carga y reproducción de clips de audio 869
21.6 Reproducción de video y otros medios con el Marco de trabajo de medios de Java 872
21.7 Conclusión 876
21.8 Recursos Web 876
22 Componentes de la GUI: parte 2 883
22.1 Introducción 884
22.2 JSlider 884
22.3 Ventanas: observaciones adicionales 888
22.4 Uso de menús con marcos 889
22.5 JPopupMenu 896
22.6 Apariencia visual adaptable 899
22.7 JDesktopPane y JInternalFrame 903
22.8 JTabbedPane 906
22.9 Administradores de esquemas: BoxLayout y GridBagLayout 908
22.10 Conclusión 920
23 Subprocesamiento múltiple 925
23.1 Introducción 926
23.2 Estados de los subprocesos: ciclo de vida de un subproceso 927
23.3 Prioridades y programación de subprocesos 929
23.4 Creación y ejecución de subprocesos 931
23.4.1 Objetos Runnable y la clase Thread 931
23.4.2 Administración de subprocesos con el marco de trabajo Executor 934
23.5 Sincronización de subprocesos 935
23.5.1 Cómo compartir datos sin sincronización 936
23.5.2 Cómo compartir datos con sincronización: hacer las operaciones atómicas 940
23.6 Relación productor/consumidor sin sincronización 943
23.7 Relación productor/consumidor: ArrayBlockingQueue 949
23.8 Relación productor/consumidor con sincronización 952
23.9 Relación productor/consumidor: búferes delimitados 957
23.10 Relación productor/consumidor: las interfaces Lock y Condition 964
23.11 Subprocesamiento múltiple con GUIs 970
23.11.1 Realización de cálculos en un subproceso trabajador 970
23.11.2 Procesamiento de resultados inmediatos con SwingWorker 976
23.12 Otras clases e interfaces en java.util.concurrent 982
23.13 Conclusión 983
Contenido xv
24 Redes 992
24.1 Introducción 993
24.2 Manipulación de URLs 994
24.3 Cómo leer un archivo en un servidor Web 998
24.4 Cómo establecer un servidor simple utilizando sockets de flujo 1001
24.5 Cómo establecer un cliente simple utilizando sockets de flujo 1003
24.6 Interacción entre cliente/servidor mediante conexiones de socket de flujo 1004
24.7 Interacción entre cliente/servidor sin conexión mediante datagramas 1014
24.8 Juego de Tres en raya (Gato) tipo cliente/servidor, utilizando un servidor con
subprocesamiento múltiple 1021
24.9 La seguridad y la red 1034
24.10 [Bono Web] Ejemplo práctico: servidor y cliente DeitelMessenger 1034
24.11 Conclusión 1035
25 Acceso a bases de datos con JDBC 1041
25.1 Introducción 1042
25.2 Bases de datos relacionales 1043
25.3 Generalidades acerca de las bases de datos relacionales: la base de datos libros 1044
25.4 SQL 1047
25.4.1 Consulta básica SELECT 1047
25.4.2 La cláusula WHERE 1048
25.4.3 La cláusula ORDER BY 1050
25.4.4 Cómo fusionar datos de varias tablas: INNER JOIN 1051
25.4.5 La instrucción INSERT 1053
25.4.6 La instrucción UPDATE 1053
25.4.7 La instrucción DELETE 1054
25.5 Instrucciones para instalar MySQL y MySQL Connector/J 1055
25.6 Instrucciones para establecer una cuenta de usuario de MySQL 1056
25.7 Creación de la base de datos libros en MySQL 1057
25.8 Manipulación de bases de datos con JDBC 1057
25.8.1 Cómo conectarse y realizar consultas en una base de datos 1057
25.8.2 Consultas en la base de datos libros 1062
25.9 La interfaz RowSet 1073
25.10 Java DB/Apache Derby 1075
25.11 Objetos PreparedStatement 1076
25.12 Procedimientos almacenados 1090
25.13 Procesamiento de transacciones 1091
25.14 Conclusión 1091
25.15 Recursos Web y lecturas recomendadas 1092
Los capítulos 26 a 30 así como los apéndices, los encontrará en el CD que acompaña este libro.
26 Aplicaciones Web: parte 1 1101
26.1 Introducción 1102
26.2 Transacciones HTTP simples 1103
26.3 Arquitectura de aplicaciones multinivel 1105
26.4 Tecnologías Web de Java 1106
26.4.1 Servlets 1106
26.4.2 JavaServer Pages 1106
26.4.3 JavaServer Faces 1107
26.4.4 Tecnologías Web en Java Studio Creator 2 1108
xvi Contenido
26.5 Creación y ejecución de una aplicación simple en Java Studio Creator 2 1108
26.5.1 Análisis de un archivo JSP 1109
26.5.2 Análisis de un archivo de bean de página 1111
26.5.3 Ciclo de vida del procesamiento de eventos 1115
26.5.4 Relación entre la JSP y los archivos de bean de página 1115
26.5.5 Análisis del XHTML generado por una aplicación Web de Java 1115
26.5.6 Creación de una aplicación Web en Java Studio Creator 2 1117
26.6 Componentes JSF 1123
26.6.1 Componentes de texto y gráficos 1123
26.6.2 Validación mediante los componentes de validación y los validadores personalizados 1128
26.7 Rastreo de sesiones 1137
26.7.1 Cookies 1138
26.7.2 Rastreo de sesiones con el objeto SessionBean 1150
26.8 Conclusión 1162
26.9 Recursos Web 1163
27 Aplicaciones Web: parte 2 1173
27.1 Introducción 1174
27.2 Acceso a bases de datos en las aplicaciones Web 1174
27.2.1 Creación de una aplicación Web que muestra datos de una base de datos 1175
27.2.2 Modificación del archivo de bean de página para la aplicación LibretaDirecciones 1183
27.3 Componentes JSF habilitados para Ajax 1185
27.3.1 Biblioteca de componentes Java BluePrints 1186
27.4 Autocomplete Text Field y formularios virtuales 1187
27.4.1 Configuración de los formularios virtuales 1187
27.4.2 Archivo JSP con formularios virtuales y un AutoComplete Text Field 1189
27.4.3 Cómo proporcionar sugerencias para un AutoComplete Text Field 1192
27.5 Componente Map Viewer de Google Maps 1196
27.5.1 Cómo obtener una clave de la API Google Maps 1196
27.5.2 Cómo agregar un componente y un Map Viewer a una página 1196
27.5.3 Archivo JSP con un componente Map Viewer 1197
27.5.4 Bean de página que muestra un mapa en el componente Map Viewer 1201
27.6 Conclusión 1206
27.7 Recursos Web 1206
28 Servicios Web JAX-WS, Web 2.0 y Mash-ups 1212
28.1 Introducción 1213
28.1.1 Descarga, instalación y configuración de Netbeans 5.5 y Sun Java System
Application Server 1214
28.1.2 Centro de recursos de servicios Web y Centros de recursos sobre Java
en www.deitel.com 1215
28.2 Fundamentos de los servicios Web de Java 1215
28.3 Creación, publicación, prueba y descripción de un servicio Web 1216
28.3.1 Creación de un proyecto de aplicación Web y cómo agregar una clase de servicio
Web en Netbeans 1216
28.3.2 Definición del servicio Web EnteroEnorme en Netbeans 1217
28.3.3 Publicación del servicio Web EnteroEnorme desde Netbeans 1221
28.3.4 Prueba del servicio Web EnteroEnorme con la página Web Tester de
Sun Java System Application Server 1222
28.3.5 Descripción de un servicio Web con el Lenguaje de descripción de servicios
Web (WSDL) 1224
Contenido xvii
28.4 Cómo consumir un servicio Web 1224
28.4.1 Creación de un cliente para consumir el servicio Web EnteroEnorme 1225
28.4.2 Cómo consumir el servicio Web EnteroEnorme 1227
28.5 SOAP 1234
28.6 Rastreo de sesiones en los servicios Web 1234
28.6.1 Creación de un servicio Web Blackjack 1235
28.6.2 Cómo consumir el servicio Web Blackjack 1239
28.7 Cómo consumir un servicio Web controlado por base de datos desde una aplicación Web 1249
28.7.1 Configuración de Java DB en Netbeans y creación de la base de datos Reservacion 1249
28.7.2 Creación de una aplicación Web para interactuar con el servicio Web Reservacion 1253
28.8 Cómo pasar un objeto de un tipo definido por el usuario a un servicio Web 1258
28.9 Conclusión 1266
28.10 Recursos Web 1267
29 Salida con formato 1275
29.1 Introducción 1276
29.2 Flujos 1276
29.3 Aplicación de formato a la salida con printf 1276
29.4 Impresión de enteros 1277
29.5 Impresión de números de punto flotante 1278
29.6 Impresión de cadenas y caracteres 1279
29.7 Impresión de fechas y horas 1280
29.8 Otros caracteres de conversión 1283
29.9 Impresión con anchuras de campo y precisiones 1284
29.10 Uso de banderas en la cadena de formato de printf 1285
29.11 Impresión con índices como argumentos 1289
29.12 Impresión de literales y secuencias de escape 1290
29.13 Aplicación de formato a la salida con la clase Formatter 1290
29.14 Conclusión 1291
30 Cadenas, caracteres y expresiones regulares 1297
30.1 Introducción 1298
30.2 Fundamentos de los caracteres y las cadenas 1298
30.3 La clase String 1299
30.3.1 Constructores de String 1299
30.3.2 Métodos length, charAt y getChars de String 1300
30.3.3 Comparación entre cadenas 1301
30.3.4 Localización de caracteres y subcadenas en las cadenas 1305
30.3.5 Extracción de subcadenas de las cadenas 1307
30.3.6 Concatenación de cadenas 1308
30.3.7 Métodos varios de String 1308
30.3.8 Método valueOf de String 1309
30.4 La clase StringBuilder 1311
30.4.1 Constructores de StringBuilder 1311
30.4.2 Métodos length, capacity, setLength y ensureCapacity de StringBuilder 1312
30.4.3 Métodos charAt, setCharAt, getChars y reverse de StringBuilder 1313
30.4.4 Métodos append de StringBuilder 1314
30.4.5 Métodos de inserción y eliminación de StringBuilder 1316
30.5 La clase Character 1317
30.6 La clase StringTokenizer 1321
30.7 Expresiones regulares, la clase Pattern y la clase Matcher 1322
30.8 Conclusión 1330
xviii Contenido
A Tabla de precedencia de los operadores 1340
B Conjunto de caracteres ASCII 1342
C Palabras clave y palabras reservadas 1343
D Tipos primitivos 1344
E Sistemas numéricos 1345
E.1 Introducción 1346
E.2 Abreviatura de los números binarios como números octales y hexadecimales 1348
E.3 Conversión de números octales y hexadecimales a binarios 1349
E.4 Conversión de un número binario, octal o hexadecimal a decimal 1350
E.5 Conversión de un número decimal a binario, octal o hexadecimal 1351
E.6 Números binarios negativos: notación de complemento a dos 1352
F GroupLayout 1357
F.1 Introducción 1357
F.2 Fundamentos de GroupLayout 1357
F.3 Creación de un objeto SelectorColores 1358
F.4 Recursos Web sobre GroupLayout 1367
G Componentes de integración Java Desktop (JDIC) 1368
G.1 Introducción 1368
G.2 Pantallas de inicio 1368
G.3 La clase Desktop 1370
G.4 Iconos de la bandeja 1371
G.5 Proyectos JDIC Incubator 1373
G.6 Demos de JDIC 1373
H Mashups 1374
Índice 1381
“No vivas más en fragmentos, sólo conéctate”.
—Edgar Morgan Foster
¡Bienvenido a Java y Cómo programar en Java, 7ª edición! En Deitel & Associates escribimos para Prentice Hall
libros de texto sobre lenguajes de programación y libros de nivel profesional, impartimos capacitación a empresas
en todo el mundo y desarrollamos negocios en Internet. Fue un placer escribir esta edición ya que refleja cam-
bios importantes en el lenguaje Java y en las formas de impartir y aprender programación. Se han realizado ajustes
considerables en todos los capítulos.
Características nuevas y mejoradas
He aquí una lista de las actualizaciones que hemos realizado a la 6ª y 7ª ediciones:
Actualizamos todo el libro a la nueva plataforma Java Standard Edition 6 (“Mustang”) y lo revisamos
cuidadosamente, en base a la Especificación del lenguaje Java.
Revisamos la presentación conforme a las recomendaciones del currículum de ACM/IEEE.
Reforzamos nuestra pedagogía anticipada sobre las clases y los objetos, poniendo especial atención a la
orientación de los profesores universitarios en nuestros equipos de revisión, para asegurarnos de obtener
el nivel conceptual correcto. Todo el libro está orientado a objetos, y las explicaciones sobre la POO son
claras y accesibles. En el capítulo 1 presentamos los conceptos básicos y la terminología de la tecnología
de objetos. Los estudiantes desarrollan sus primeras clases y objetos personalizados en el capítulo 3. Al
presentar los objetos y las clases en los primeros capítulos, hacemos que los estudiantes “piensen acerca
de objetos” de inmediato, y que dominen estos conceptos con más profundidad.
La primera presentación de clases y objetos incluye los ejemplos prácticos de las clases Tiempo, Empleado
y LibroCalificaciones, los cuales van haciendo su propio camino a través de varias secciones y capítu-
los, presentando conceptos de OO cada vez más profundos.
Los profesores que imparten cursos introductorios tienen una amplia opción en cuanto a la cantidad
de GUI y gráficos a cubrir; desde cero, a una secuencia introductoria de diez secciones breves, hasta un
tratamiento detallado en los capítulos 11, 12 y 22, y en el apéndice F.
Adaptamos nuestra presentación orientada a objetos para utilizar la versión más reciente de UML™
(Lenguaje Unificado de Modelado™): UML™ 2, el lenguaje gráfico estándar en la industria para modelar
sistemas orientados a objetos.
En los capítulos 1-8 y 10 presentamos y adaptamos el ejemplo práctico opcional del cajero automático
(ATM) de DOO/UML 2. Incluimos un apéndice Web adicional, con la implementación completa del
código. Dé un vistazo a los testimonios que se incluyen en la parte posterior del libro.
Agregamos varios ejemplos prácticos sustanciales sobre programación Web orientada a objetos.
Actualizamos el capítulo 25, Acceso a bases de datos con JDBC, para incluir JDBC 4 y utilizar el nuevo
sistema de administración de bases de datos Java DB/Apache Derby, además de MySQL. Este capítulo
incluye un ejemplo práctico OO sobre el desarrollo de una libreta de direcciones controlada por una
base de datos, la cual demuestra las instrucciones preparadas y el descubrimiento automático de contro-
ladores de JDBC 4.
Agregamos los capítulos 26 y 27, Aplicaciones Web: partes 1 y 2, que introducen la tecnología Java-
Server Faces (JSF) y la utilizan con Sun Java Studio Creador 2 para construir aplicaciones Web de una
manera rápida y sencilla. El capítulo 26 incluye ejemplos sobre la creación de GUIs de aplicaciones Web,
•
•
•
•
•
•
•
•
•
•
Prefacio
el manejo de eventos, la validación de formularios y el rastreo de sesiones. El material de JSF sustituye
los capítulos anteriores sobre servlets y JavaServer Pages (JSP).
Agregamos el capítulo 27, Aplicaciones Web: parte 2, que habla acerca del desarrollo de aplicaciones
Web habilitadas para Ajax, usando las tecnologías JavaServer Faces y Java BluePrints. Este capítulo
incluye una aplicación de libreta de direcciones Web multiniveles, controlada por una base de datos, que
permite a los usuarios agregar y buscar contactos, y mostrar las direcciones de los contactos en mapas
de Google™ Maps. Esta aplicación habilitada para Ajax le proporciona una sensación real del desarrollo
Web 2.0. La aplicación utiliza Componentes JSF habilitados para Ajax para sugerir los nombres de los
contactos, mientras el usuario escribe un nombre para localizar y mostrar una dirección localizada en
un mapa de Google Maps.
Agregamos el capítulo 28, Servicios Web JAX-WS, Web 2.0 y Mash-ups que utiliza un método basado
en herramientas para crear y consumir servicios Web, una capacidad típica de Web 2.0. Los ejemplos
prácticos incluyen el desarrollo de los servicios Web del juego de blackjack y un sistema de reservaciones
de una aerolínea.
Utilizamos el nuevo método basado en herramientas para desarrollar aplicaciones Web con rapidez;
todas las herramientas pueden descargarse sin costo.
Fundamos la Iniciativa Deitel de Negocios por Internet (Deitel Internet Business Initiative) con 60 nue-
vos centros de recursos para apoyar a nuestros lectores académicos y profesionales. Dé un vistazo a nues-
tros nuevos centros de recursos (www.deitel.com/resourcecenters.html), incluyendo: Java SE 6
(Mustang), Java, Evaluación y Certificación de Java, Patrones de Diseño de Java, Java EE 5, Motores
de Búsqueda de Código y Sitios de Código, Programación de Juegos, Proyectos de Programación y
muchos más. Regístrese en el boletín de correo electrónico gratuito Deitel® Buzz Online (www.deitel.
com/newsletter/subscribe.html); cada semana anunciamos nuestro(s) centro(s) de recurso(s) más
reciente(s); además incluimos otros temas de interés para nuestros lectores.
Hablamos sobre los conceptos clave de la comunidad de ingeniería de software, como Web 2.0, Ajax,
SOA, servicios Web, software de código fuente abierto, patrones de diseño, mashups, refabricación,
programación extrema, desarrollo ágil de software, prototipos rápidos y mucho más.
Rediseñamos por completo el capítulo 23, Subprocesamiento múltiple [nuestro agradecimiento especial
a Brian Goetz y Joseph Bowbeer, coautores de Java Concurrency in Practice, Addison-Wesley, 2006].
Hablamos sobre la nueva clase SwingWorker para desarrollar interfaces de usuario con subprocesamien-
to múltiple.
Hablamos sobre los nuevos Componentes de Integración de Escritorio de Java (JDIC), como las panta-
llas de inicio (splash screens) y las interacciones con la bandeja del sistema.
Hablamos sobre el nuevo administrador de esquemas GroupLayout en el contexto de la herramienta de
diseño de GUI NetBeans 5.5 Matisse para crear GUIs portables que se adhieran a los lineamientos
de diseño de GUI de la plataforma subyacente.
Presentamos las nuevas características de ordenamiento y filtrado de JTable, que permiten al usuario
reordenar los datos en un objeto JTable y filtrarlos mediante expresiones regulares.
Presentamos un tratamiento detallado de los genéricos y las colecciones de genéricos.
Introducimos los mashups, aplicaciones que, por lo general, se crean mediante llamadas a servicios Web
(y/o usando fuentes RSS) de dos o más sitios; otra característica típica de Web 2.0.
Hablamos sobre la nueva clase StringBuilder, que tiene un mejor desempeño que StringBuffer en
aplicaciones sin subprocesamiento.
Presentamos las anotaciones, que reducen en gran parte la cantidad de código necesario para crear apli-
caciones.
Las características que se presentan en Cómo programar en Java, 7a edición, incluyen:
Cómo obtener entrada con formato mediante la clase Scanner.
Mostrar salida con formato mediante el método printf del objeto System.out.
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
xx Prefacio
Instrucciones for mejoradas para procesar elementos de arreglos y colecciones.
Declaración de métodos con listas de argumentos de longitud variable (“varargs”).
Uso de clases enum que declaran conjuntos de constantes.
Importación de los miembros static de una clase para usarlos en otra.
Conversión de valores de tipo primitivo a objetos de envolturas de tipo y viceversa, usando autoboxing
y auto-unboxing, respectivamente.
Uso de genéricos para crear modelos generales de métodos y clases que pueden declararse una vez, pero
usarse con muchos tipos de datos distintos.
Uso de las estructuras de datos mejoradas para genéricos de la API Collections.
Uso de la API Concurrency para implementar aplicaciones con subprocesamiento múltiple.
Uso de objetos RowSet de JDBC para acceder a los datos en una base de datos.
Todo esto ha sido revisado cuidadosamente por distinguidos profesores y desarrolladores de la industria, que
trabajaron con nosotros en Cómo programar en Java 6ª y 7ª ediciones.
Creemos que este libro y sus materiales de apoyo proporcionarán a los estudiantes y profesionales una expe-
riencia informativa, interesante, retadora y placentera. El libro incluye una extensa suite de materiales comple-
mentarios para ayudar a los profesores a maximizar la experiencia de aprendizaje de sus estudiantes.
Cómo programar en Java 7ª edición presenta cientos de programas completos y funcionales, y describe sus
entradas y salidas. Éste es nuestro característico método de “código activo” (“live code”); presentamos la mayoría
de los conceptos de programación de Java en el contexto de programas funcionales completos.
Si surge alguna duda o pregunta a medida que lee este libro, envíe un correo electrónico a deitel@deitel.
com; le responderemos a la brevedad. Para obtener actualizaciones sobre este libro y el estado de todo el software
de soporte de Java, además de las noticias más recientes acerca de todas las publicaciones y servicios de Deitel,
visite www.deitel.com.
Regístrese en www.deitel.com/newsletter/subscribe.html para obtener el boletín de correo electrónico
Deitel® Buzz Online y visite la página www.deitel.com/resourcecenters.html para tener acceso a nuestra lista
creciente de centros de recursos.
Uso de UML 2 para desarrollar un diseño orientado a objetos de un ATM. UML 2 se ha convertido
en el lenguaje de modelado gráfico preferido para diseñar sistemas orientados a objetos. Todos los diagramas de
UML en el libro cumplen con la especificación UML 2. Utilizamos los diagramas de actividad de UML para
demostrar el flujo de control en cada una de las instrucciones de control de Java, y usamos los diagramas de
clases de UML para representar las clases y sus relaciones de herencia en forma visual.
Incluimos un ejemplo práctico opcional (pero altamente recomendado) acerca del diseño orientado a objetos
mediante el uso de UML. La revisión del ejemplo práctico estuvo a cargo de un distinguido equipo de profesores
y profesionales de la industria relacionados con DOO/UML, incluyendo líderes en el campo de Rational (los
creadores de UML) y el Grupo de administración de objetos (responsable de la evolución de UML). En el ejem-
plo práctico, diseñamos e implementamos por completo el software para un cajero automático (ATM) simple.
Las secciones Ejemplo práctico de Ingeniería de Software al final de los capítulos 1 a 8 y 10 presentan una
introducción cuidadosamente planeada al diseño orientado a objetos mediante el uso de UML. Presentamos un
subconjunto conciso y simplificado de UML 2, y después lo guiamos a través de su primera experiencia de dise-
ño, ideada para los principiantes. El ejemplo práctico no es un ejercicio, sino una experiencia de aprendizaje de
principio a fin, que concluye con un recorrido detallado a través del código completo en Java. Las secciones del
Ejemplo Práctico de Ingeniería de Software ayudan a los estudiantes a desarrollar un diseño orientado a objetos
para complementar los conceptos de programación orientada a objetos que empiezan a aprender en el capítulo 1,
y que implementan en el capítulo 3. En la primera de estas secciones, al final del capítulo 1, introducimos
los conceptos básicos y la terminología del DOO. En las secciones opcionales Ejemplo Práctico de Ingeniería de
Software al final de los capítulos 2 a 5, consideramos cuestiones más sustanciales al emprender la tarea de resolver
un problema retador con las técnicas del DOO. Analizamos un documento de requerimientos típico que especi-
fica un sistema a construir, determina los objetos necesarios para implementar ese sistema, establece los atributos
que deben tener estos objetos, fija los comportamientos que deben exhibir estos objetos y especifica la forma
en que deben interactuar los objetos entre sí para cumplir con los requerimientos del sistema. En un apéndice
•
•
•
•
•
•
•
•
•
Prefacio xxi
Web adicional presentamos el código completo de una implementación en Java del sistema orientado a objetos
que diseñamos en los primeros capítulos. El ejemplo práctico ayuda a preparar a los estudiantes para los tipos de
proyectos sustanciales que encontrarán en la industria. Empleamos un proceso de diseño orientado a objetos
cuidadosamente desarrollado e incremental para producir un modelo en UML 2 para nuestro sistema ATM.
A partir de este diseño, producimos una implementación sustancial funcional en Java, usando las nociones clave
de la programación orientada a objetos, incluyendo clases, objetos, encapsulamiento, visibilidad, composición,
herencia y polimorfismo.
Gráfico de dependencias
En el gráfico de la siguiente página se muestra las dependencias entre los capítulos, para ayudar a los profesores a pla-
near su programa de estudios. Cómo programar en Java 7ª edición es un libro extenso, apropiado para una variedad de
cursos de programación en distintos niveles. Los capítulos 1-14 forman una secuencia de programación elemental
accesible, con una sólida introducción a la programación orientada a objetos. Los capítulos 11, 12, 20, 21 y 22 for-
man una secuencia sustancial de GUI, gráficos y multimedia. Los capítulos 15 a 19 forman una excelente secuencia
de estructuras de datos. Los capítulos 24 a 28 forman una clara secuencia de desarrollo Web con uso intensivo de
bases de datos.
Método de enseñanza
Cómo programar en Java 7ª edición contiene una extensa colección de ejemplos. El libro se concentra en los
principios de la buena ingeniería de software, haciendo hincapié en la claridad de los programas. Enseñamos
mediante ejemplos. Somos educadores que impartimos temas de vanguardia en salones de clases de la industria
alrededor del mundo. El Dr. Harvey M. Deitel tiene 20 años de experiencia en la enseñanza universitaria y 17, en
la enseñanza en la industria. Paul Deitel tiene 15 años de experiencia en la enseñanza en la industria. Juntos han
impartido cursos, en todos los niveles, a clientes gubernamentales, industriales, militares y académicos de Deitel
& Associates.
Método del código activo. Cómo programar en Java 7ª edición está lleno de ejemplos de “código activo”; esto
significa que cada nuevo concepto se presenta en el contexto de una aplicación en Java completa y funcional, que
es seguido inmediatamente por una o más ejecuciones actuales, que muestran las entradas y salidas del programa.
Este estilo ejemplifica la manera en que enseñamos y escribimos acerca de la programación; a éste le llamamos el
método del “código activo”.
Resaltado de código. Colocamos rectángulos de color gris alrededor de los segmentos de código clave en cada
programa.
Uso de fuentes para dar énfasis. Colocamos los términos clave y la referencia a la página del índice para
cada ocurrencia de definición en texto en negritas para facilitar su referencia. Enfatizamos los componentes en
pantalla en la fuente Helvética en negritas (por ejemplo, el menú Archivo) y enfatizamos el texto del programa
en la fuente Lucida (por ejemplo, int x = 5).
Acceso Web. Todos los ejemplos de código fuente para Cómo programar en Java 7ª edición (y para nuestras otras
publicaciones) se pueden descargar en:
www.deitel.com/books/jhtp7
www.pearsoneducacion.net/deitel
El registro en el sitio es un proceso fácil y rápido. Descargue todos los ejemplos y, a medida que lea las correspon-
dientes discusiones en el libro de texto, después ejecute cada programa. Realizar modificaciones a los ejemplos y
ver los efectos de esos cambios es una excelente manera de mejorar su experiencia de aprendizaje en Java.
Objetivos. Cada capítulo comienza con una declaración de objetivos. Esto le permite saber qué es lo que debe
esperar y le brinda la oportunidad, después de leer el capítulo, de determinar si ha cumplido con ellos.
Frases. Después de los objetivos de aprendizaje aparecen una o más frases. Algunas son graciosas, otras filosóficas y
las demás ofrecen ideas interesantes. Esperamos que disfrute relacionando las frases con el material del capítulo.
xxii Prefacio
Plan general. El plan general de cada capítulo le permite abordar el material de manera ordenada, para poder
anticiparse a lo que está por venir y establecer un ritmo cómodo y efectivo de aprendizaje.
Ilustraciones/Figuras. Incluimos una gran cantidad de gráficas, tablas, dibujos lineales, programas y salidas de
programa. Modelamos el flujo de control en las instrucciones de control mediante diagramas de actividad en
1 Introducción a las computadoras,
Internet y Web
2 Introducción a las
aplicaciones en Java
3 Introducción a las clases y los objetos
4 Instrucciones de control: parte 1
5 Instrucciones de control: parte 2
6 Métodos: Un análisis más detallado
10 Programación orientada
a objetos: polimorfismo
8 Clases y objetos:
un análisis más detallado
9 Programación orientada
a objetos: herencia
11 Componentes de
la GUI: parte 1
12 Gráficos y Java2D™
25 Acceso a
base de datos
con JDBC1
13 Manejo de
excepciones1
15 Recursividad3
18 Genéricos
24 Redes2
14 Archivos
y flujos
23 Subprocesamiento
múltiple4
7 Arreglos
16 Búsqueda y
ordenamiento
21 Multimedia: applets
y aplicaciones
22 Componentes de
la GUI: parte 2
26 Aplicaciones
Web:
parte 1
27 Aplicaciones
Web:
parte 2
19 Colecciones
20 Introducción a los
applets de Java
29 Salida con
formato
(la mayor parte)
30 Cadenas,
caracteres y
expresiones
regulares
3.9 Uso de cuadros de diálogo
(Opcional) Ruta de GUI y gráficos
4.14 Creación de dibujos simples
5.10 Dibujo de rectángulos y óvalos
6.13 Colores y figuras rellenas
7.13 Cómo dibujar arcos
8.18 Uso de objetos
con gráficos
9.8 Mostrar texto e imágenes
usando etiquetas
10.8 Realizar dibujos mediante
el polimorfismo
17 Estructuras de datos
28 Servicios Web
JAX-WS, Web 2.0
y Mash-ups
1. Los capítulos 13 y 25 dependen del capítulo 11 para la GUI que se utiliza en un ejemplo.
2. El capítulo 24 depende del capítulo 20 para un ejemplo que utiliza un applet. El ejemplo práctico extenso
al final de este capítulo depende del capítulo 22 para la GUI y del capítulo 23 para el subprocesamiento múltiple.
3. El capítulo 15 depende de los capítulos 11 y 12 para la GUI y los gráficos que se utilizan en un ejemplo.
4. El capítulo 23 depende del capítulo 11 para la GUI que se utiliza en un ejemplo, y de los capítulos 18-19 para
un ejemplo.
Prefacio xxiii
UML. Los diagramas de clases de UML modelan los campos, constructores y métodos de las clases. En el ejemplo
práctico opcional del ATM de DOO/UML 2 hacemos uso extensivo de seis tipos principales de diagramas en
UML.
Tips de programación. Incluimos tips de programación para ayudarle a enfocarse en los aspectos importantes
del desarrollo de programas. Estos tips y prácticas representan lo mejor que hemos podido recabar a lo largo de
seis décadas combinadas de experiencia en la programación y la enseñanza. Una de nuestras alumnas, estudiante
de matemáticas, recientemente nos comentó que siente que este método es similar al de resaltar axiomas, teoremas
y corolarios en los libros de matemáticas, ya que proporciona una base sólida sobre la cual se puede construir
buen software.
Buena práctica de programación
Las buenas prácticas de programación llaman la atención hacia técnicas que le ayudarán a producir programas
más claros, comprensibles y fáciles de mantener.
Error común de programación
Con frecuencia, los estudiantes tienden a cometer ciertos tipos de errores; al poner atención en estos Errores comunes
de programación se reduce la probabilidad de que usted pueda cometerlos.
Tip para prevenir errores
Estos tips contienen sugerencias para exponer los errores y eliminarlos de sus programas; muchos de ellos describen
aspectos de Java que evitan que los errores entren a los programas.
Tip de rendimiento
A los estudiantes les gusta “turbo cargar” sus programas. Estos tips resaltan las oportunidades para hacer que sus
programas se ejecuten más rápido, o para minimizar la cantidad de memoria que ocupan.
Tip de portabilidad
Incluimos Tips de portabilidad para ayudarle a escribir el código que pueda ejecutarse en una variedad de plata-
formas, y que expliquen cómo es que Java logra su alto grado de portabilidad.
Observación de ingeniería de software
Las Observaciones de ingeniería de software resaltan los asuntos de arquitectura y diseño, lo cual afecta la cons-
trucción de los sistemas de software, especialmente los de gran escala.
Archivo
Nuevo
Abrir...
Cerrar
Observaciones de apariencia visual
Le ofrecemos Observaciones de apariencia visual para resaltar las convenciones de la interfaz gráfica de usuario.
Estas observaciones le ayudan a diseñar interfaces gráficas de usuario atractivas y amigables para el usuario, en con-
formidad con las normas de la industria.
Sección de conclusión. Cada uno de los capítulos termina con una sección breve de “conclusión”, que recapitula
el contenido del capítulo y la transición al siguiente capítulo.
Viñetas de resumen. Cada capítulo termina con estrategias pedagógicas adicionales. Presentamos un resumen
detallado del capítulo, estilo lista con viñetas, sección por sección.
Terminología. Incluimos una lista alfabetizada de los términos importantes definidos en cada capítulo.
Ejercicios de autoevaluación y respuestas. Se incluyen diversos ejercicios de autoevaluación con sus respuestas,
para que los estudiantes practiquen por su cuenta.
xxiv Prefacio
Ejercicios. Cada capítulo concluye con un diverso conjunto de ejercicios, incluyendo recordatorios simples de
terminología y conceptos importantes; identificar los errores en muestras de código, escribir instrucciones in-
dividuales de programas; escribir pequeñas porciones de métodos y clases en Java; escribir métodos, clases y
programas completos; y crear proyectos finales importantes. El extenso número de ejercicios permite a los
instructores adaptar sus cursos a las necesidades únicas de sus estudiantes, y variar las asignaciones de los cur-
sos cada semestre. Los profesores pueden usar estos ejercicios para formar tareas, exámenes cortos, exámenes
regulares y proyectos finales. [NOTA: No nos escriba para solicitarnos acceso al Centro de Recursos para
Instructores. El acceso está limitado estrictamente a profesores universitarios que impartan clases en base
al libro. Los profesores sólo pueden obtener acceso a través de los representantes de Pearson Educación].
Asegúrese de revisar nuestro centro de recursos de proyectos de programación (http://www.deitel.com/
ProgrammingProjects/) para obtener muchos ejercicios adicionales y posibilidades de proyectos.
Miles de entradas en el índice. Hemos incluido un extenso índice, que es útil, en especial, cuando se utiliza el
libro como referencia.
“Doble indexado” de ejemplos de código activo de Java. Para cada programa de código fuente en el libro,
indexamos la leyenda de la figura en forma alfabética y como subíndice, bajo “Ejemplos”. Esto facilita encontrar
los ejemplos usando las características especiales.
Recursos para el estudiante incluidos en
Cómo programar en Java 7ª edición
Hay, disponibles a la venta, una variedad de herramientas de desarrollo, pero ninguna de ellas es necesaria para
comenzar a trabajar con Java. Escribimos Cómo programar en Java 7ª edición utilizando sólo el nuevo Kit de
Desarrollo de Java Standard Edition (JDK), versión 6.0. Puede descargar la versión actual del JDK del sitio Web
de Java de Sun: java.sun.com/javase/downloads/index.jsp. Este sitio también contiene las descargas de la
documentación del JDK.
El CD que se incluye con este libro contienen el Entorno de Desarrollo Integrado (IDE) NetBeans™ 5.5
para desarrollar todo tipo de aplicaciones en Java, y el software Sun Java™ Studio Creator 2 Update 1 para el
desarrollo de aplicaciones Web. Se proporciona también una versión en Windows de MySQL® 5.0 Community
Edition 5.0.27 y MySQL Connector/J 5.0.4, para el procesamiento de datos que se lleva a cabo en los capítulos
25 a 28.
El CD también contiene los ejemplos del libro y una página Web con vínculos al sitio Web de Deitel &
Associates, Inc. Puede cargar esta página Web en un explorador Web para obtener un rápido acceso a todos los
recursos.
Encontrará recursos adicionales y descargas de software en nuestro centro de recursos de Java SE 6 (Mus-
tang), ubicado en:
www.deitel.com/JavaSE6Mustang/
Java Multimedia Cyber Classroom 7ª edición
Cómo programar en Java 7ª edición incluye multimedia interactiva con mucho audio y basada en Web, comple-
mentaria para el libro Java Multimedia Cyber Classroom, 7ª edición, disponible en inglés. Nuestro Ciber salón de
clases (Cyber Classroom) basado en Web incluye recorridos con audio de los ejemplos de código de los capítulos 1
a 14, soluciones a casi la mitad de los ejercicios del libro, un manual de laboratorio y mucho más. Para obtener
más información acerca del Cyber Classroom basado en Web, visite:
www.prenhall.com/deitel/cyberclassroom/
A los estudiantes que utilizan nuestros Ciber salones de clases les gusta su interactividad y capacidades de refe-
rencia. Los profesores nos dicen que sus estudiantes disfrutan al utilizar el Ciber salón de clases y, en consecuencia,
invierten más tiempo en los cursos, dominando un porcentaje mayor del material que en los cursos que sólo
utilizan libros de texto.
Prefacio xxv
Recursos para el instructor de Cómo programar en Java 7ª edición
Cómo programar en Java 7ª edición tiene una gran cantidad de recursos para los profesores. El Centro de Recursos
para Instructores de Prentice Hall contiene el Manual de soluciones, con respuestas para la mayoría de los ejercicios
al final de cada capítulo, un Archivo de elementos de prueba de preguntas de opción múltiple (aproximadamente
dos por cada sección del libro) y diapositivas en PowerPoint® que contienen todo el código y las figuras del texto,
además de los elementos en viñetas que sintetizan los puntos clave del libro. Los profesores pueden personalizar
las diapositivas. Si usted todavía no es un miembro académico registrado, póngase en contacto con su represen-
tante de Pearson Educación. Cabe mencionar que todos estos recursos se encuentran en inglés.
Boletín de correo electrónico gratuito Deitel® Buzz Online
Cada semana, el boletín de correo electrónico Deitel® Buzz Online anuncia nuestro(s) centro(s) de recursos más
reciente(s) e incluye comentarios acerca de las tendencias y desarrollos en la industria, vínculos a artículos y recur-
sos gratuitos de nuestros libros publicados y de las próximas publicaciones, itinerarios de lanzamiento de produc-
tos, fe de erratas, retos, anécdotas, información sobre nuestros cursos de capacitación corporativa impartidos por
instructores y mucho más. También es una buena forma para que usted se mantenga actualizado acerca de todo
lo relacionado con Cómo programar en Java 7ª edición. Para suscribirse, visite la página Web:
www.deitel.com/newsletter/subscribe.html
Novedades en Deitel
Centros de recursos y la iniciativa de negocios por Internet de Deitel. Hemos creado muchos centros
de recursos en línea (en www.deitel.com/resourcecenters.html) para mejorar su experiencia de aprendizaje
en Java. Anunciamos nuevos centros de recursos en cada edición del boletín de correo electrónico Deitel® Buzz
Online. Aquellos de especial interés para los lectores de este libro incluyen: Java, Certificación en Java, Patrones
de Diseño en Java, Java EE 5, Java SE 6, AJAX, Apache, Motores de Búsqueda de Código y Sitios de Código,
Eclipse, Programación de Juegos, Mashups, MySQL, Código Abierto, Proyectos de Programación, Web2.0,
Web 3.0, Servicios Web y XML. Los centros de recursos de Deitel adicionales incluyen: Programas Afiliados,
Servicios de Alerta, ASP.NET, Economía de Atención, Creación de Comunidades Web, C, C++, C#, Juegos
de Computadora, DotNetNuke, FireFox, Gadgets, Google AdSense, Google Analytics, Google Base, Google
Services, Google Video, Google Web Toolkit, IE7, Iniciativa de Negocios por Internet, Publicidad por Internet,
Internet Video, Linux, Microformatos, .NET, Ning, OpenGL, Perl, PHP, Podcasting, Python, Recommender
Systems, RSS, Ruby, Motores de Búsqueda, Optimización de Motores de Búsqueda, Skype, Sudoku, Mundos
Virtuales, Visual Basic, Wikis, Windows Vista, WinFX y muchos más por venir.
Iniciativa de contenido libre. Nos complace ofrecerle artículos de invitados y tutoriales gratuitos, seleccio-
nados de nuestras publicaciones actuales y futuras como parte de nuestra iniciativa de contenido libre. En cada
tema del boletín de correo electrónico Deitel® Buzz Online, anunciamos las adiciones más recientes a nuestra
biblioteca de contenido libre.
Reconocimientos
Uno de los mayores placeres al escribir un libro de texto es el de reconocer el esfuerzo de mucha gente, cuyos nom-
bres quizá no aparezcan en la portada, pero cuyo arduo trabajo, cooperación, amistad y comprensión fue crucial
para la elaboración de este libro. Mucha gente en Deitel & Associates, Inc. dedicó largas horas a este proyecto;
queremos agradecer en especial a Abbey Deitel y Barbara Deitel.
También nos gustaría agradecer a dos participantes de nuestro programa de Pasantía con Honores, que con-
tribuyeron a esta publicación: Megan Shuster, con especialidad en ciencias computacionales en el Swarthmore
College, y Henry Klementowicz, con especialidad en ciencias computacionales en la Universidad de Columbia.
Nos gustaría mencionar nuevamente a nuestros colegas que realizaron contribuciones importantes a Cómo
programar en Java 6ª edición: Andrew B. Goldberg, Jeff Listfield, Su Zhang, Cheryl Yaeger, Jing Hu, Sin Han Lo,
John Paul Casiello y Christi Kelsey.
Somos afortunados al haber trabajado en este proyecto con un talentoso y dedicado equipo de editores pro-
fesionales en Prentice Hall. Apreciamos el extraordinario esfuerzo de Marcia Horton, Directora Editorial de la
División de Ingeniería y Ciencias Computacionales de Prentice Hall. Jennifer Cappello y Dolores Mars hicieron
xxvi Prefacio
un excelente trabajo al reclutar el equipo de revisión del libro y administrar el proceso de revisión. Francesco San-
talucia (un artista independiente) y Kristine Carney de Prentice Hall hicieron un maravilloso trabajo al diseñar la
portada del libro; nosotros proporcionamos el concepto y ellos lo hicieron realidad. Vince O’Brien, Bob Engel-
hardt, Donna Crilly y Marta Samsel hicieron un extraordinario trabajo al administrar la producción del libro.
Deseamos reconocer el esfuerzo de nuestros revisores. Al adherirse a un estrecho itinerario, escrutinizaron
el texto y los programas, proporcionando innumerables sugerencias para mejorar la precisión e integridad de la
presentación.
Apreciamos con sinceridad los esfuerzos de nuestros revisores de post-publicación de la 6ª edición, y nuestros
revisores de la 7ª edición:
Revisores de Cómo programar en Java 7ª edición
(incluyendo los revisores de la post-publicación de la 6ª edición)
Revisores de Sun Microsystems: Lance Andersen (Líder de especificaciones de JDBC/Rowset, Java SE Engi-
neering), Ed Burns, Ludovic Champenois (Servidor de Aplicaciones de Sun para programadores de Java EE
con Sun Application Server y herramientas: NetBeans, Studio Enterprise y Studio Creador), James Davidson,
Vadiraj Deshpande (Grupo de Integración de Sistemas de Java Enterprise, Sun Microsystems India), Sanjay
Dhamankar (Grupo Core Developer Platform), Jesse Glick (Grupo NetBeans), Brian Goetz (autor de Java Con-
currency in Practice, Addison-Wesley, 2006), Doug Kohlert (Grupo Web Technologies and Standards), Sandeep
Konchady (Organización de Ingeniería de Software de Java), John Morrison (Grupo Portal Server Product de
Sun Java System), Winston Prakash, Brandon Taylor (grupo SysNet dentro de la División de Software) y Jayashri
Visvanathan (Equipo de Java Studio Creador de Sun Microsystems). Revisores académicos y de la industria:
Akram Al-Rawi (Universidad King Faisal), Mark Biamonte (DataDiret), Ayad Boudiab (Escuela Internacional de
Choueifat, Líbano), Joe Bowbeer (Mobile App Consulting), Harlan Brewer (Select Engineering Services), Marita
Ellixson (Eglin AFB, Universidad Indiana Wesleyan, Facilitador en Jefe), John Goodson (DataDiret), Anne Hor-
ton (Lockheed Martin), Terrell Regis Hull (Logicalis Integration Solutions), Clark Richey (RABA Technologies,
LLC, Java Sun Champion), Manfred Riem (UTA Interactive, LLC, Java Sun Champion), Karen Tegtmeyer
(Model Technologies, Inc.), David Wolf (Universidad Pacific Lutheran) y Hua Yan (Borough of Manhattan
Community Collage, City University of New York). Revisores de la post-publicación de Cómo programar
en Java 6ª edición: Anne Horton (Lockheed Martin), William Martz (Universidad de Colorado, en Colorado
Springs), Bill O’Farrell (IBM), Jeffry Babb (Universidad Virginia Commonwealth), Jeffrey Six (Universidad de
Delaware, Instalaciones Adjuntas), Jesse Glick (Sun Microsystems), Karen Tegtmeyer (Model Technologies, Inc.),
Kyle Gabhart (L-3 Communications), Marita Ellixson (Eglin AFB, Universidad Indiana Wesleyan, Facilitador en
Jefe) y Sean Santry (Consultor independiente).
Revisores de Cómo programar en Java 6ª edición
(incluyendo a los revisores de la post-publicación de la 5ª edición)
Revisores académicos: Karen Arlien (Colegio Estatal de Bismarck), Ben Blake (Universidad Estatal de Cleve-
land), Walt Bunch (Universidad Chapman), Marita Ellixson (Eglin AFB/Universidad de Arkansas), Ephrem
Eyob (Universidad Estatal de Virginia), Bjorn Foss (Universidad Metropolitana de Florida), Bill Freitas (The
Lawrenceville School), Joe Kasprzyk (Colegio Estatal de Salem), Brian Larson (Modesto Junior College), Roberto
Lopez-Herrejon (Universidad de Texas en Austin), Dean Mellas (Cerritos College), David Messier (Eastern Uni-
versity), Andy Novobilski (Universidad deTennessee, Chattanooga), Richard Ord (Universidad de California, San
Diego), Gavin Osborne (Saskatchewan Institute of Applied Science & Technology), Donna Reese (Universidad
Estatal de Mississippi), Craig Slinkman (Universidad de Texas en Arlington), Sreedhar Thota (Western Iowa Tech
Community Collage), Mahendran Velauthapillai (Universidad de Georgetown), Loran Walter (Universidad
Tecnológica de Lawrence) y Stephen Weiss (Universidad de Carolina del Norte en Chapel Hill). Revisores de la
industria: Butch Anton (Wi-Tech Consulting), Jonathan Bruce (Sun Microsystems, Inc.; Líder de Especifica-
ciones de JCP para JDBC), Gilad Bracha (Sun Microsystems, Inc.; Líder de Especificaciones de JCP para Gené-
ricos), Michael Develle (Consultor independiente), Jonathan Gadzik (Consultor independiente), Brian Goetz
(Quiotix Corporation (Miembro del Grupo de Expertos de Especificaciones de Herramientas de Concurrencia
de JCP), Anne Horton (AT&T Bell Laboratories), James Huddleston (Consultor independiente), Peter Jones
(Sun Microsystems, Inc.), Doug Kohlert (Sun Microsystems, Inc.), Earl LaBatt (Altaworks Corp./Universidad
de New Hampshire), Paul Monday (Sun Microsystems, Inc.), Bill O’Farrell (IBM), Cameron Skinner (Embar-
cadero Technologies, Inc.), Brandon Taylor (Sun Microsystems, Inc.) y Karen Tegtmeyer (Consultor indepen-
Prefacio xxvii
diente). Revisores del ejemplo práctico opcional de DOO/UML: Sinan Si Alhir (Consultor independiente),
Gene Ames (Star HRG), Jan Bergandy (Universidad de Massachussetts en Dartmouth), Marita Ellixson (Eglin
AFB/Universidad de Arkansas), Jonathan Gadzik (Consultor independiente), Thomas Harder (ITT ESI, Inc.),
James Huddleston (Consultor independiente), Terrell Hull (Consultor independiente), Kenneth Hussey (IBM),
Joe Kasprzyk (Colegio Estatal de Salem), Dan McCracken (City College of New York), Paul Monday (Sun
Microsystems, Inc.), Davyd Norris (Rational Software), Cameron Skinner (Embarcadero Technologies, Inc.),
Craig Slinkman (Universidad de Texas en Arlington) y Steve Tockey (Construx Software).
Estos profesionales revisaron cada aspecto del libro y realizaron innumerables sugerencias para mejorar la
precisión e integridad de la presentación.
Bueno ¡ahí lo tiene! Java es un poderoso lenguaje de programación que le ayudará a escribir programas con
rapidez y eficiencia. Escala sin problemas hacia el ámbito del desarrollo de sistemas empresariales, para ayudar a
las organizaciones a crear sus sistemas de información críticos. A medida que lea el libro, apreciaremos con sinceri-
dad sus comentarios, críticas, correcciones y sugerencias para mejorar el texto. Dirija toda su correspondencia a:
deitel@deitel.com
Le responderemos oportunamente y publicaremos las correcciones y aclaraciones en nuestro sitio Web,
www.deitel.com/books/jHTP7/
¡Esperamos que disfrute aprendiendo con este libro tanto como nosotros disfrutamos el escribirlo!
Paul J. Deitel
Dr. Harvey M. Deitel
Maynard, Massachussets
Diciembre del 2006
Acerca de los autores
Paul J. Deitel, CEO y Director Técnico de Deitel & Associates, Inc., es egresado del Sloan School of Manage-
ment del MIT (Massachussets Institute of Technology), en donde estudió Tecnología de la Información. Posee las
certificaciones Programador Certificado en Java (Java Certified Programmer) y Desarrollador Certificado en Java
(Java Certified Developer), y ha sido designado por Sun Microsystems como Java Champion. A través de Deitel
& Associates, Inc., ha impartido cursos en Java, C, C++, C# y Visual Basic a clientes de la industria, incluyendo:
IBM, Sun Microsystems, Dell, Lucent Technologies, Fidelity, NASA en el Centro Espacial Kennedy, el Natio-
nal Severe Storm Laboratory, White Sands Missile Range, Rogue Wave Software, Boeing, Stratus, Cambridge
Technology Partners, Open Environment Corporation, One Wave, Hyperion Software, Adra Systems, Entergy,
CableData Systems, Nortel Networks, Puma, iRobot, Invensys y muchos más. También ha ofrecido conferencias
de Java y C++ para la Boston Chapter of the Association for Computing Machinery. Él y su padre, el Dr. Harvey M.
Deitel, son autores de los libros de programación más vendidos en el mundo.
Dr. Harvey M. Deitel, es Presidente y Consejero de Estrategia de Deitel & Associates, Inc., tiene 45 años
de experiencia en el campo de la computación; lo que incluye un amplio trabajo académico y en la industria. El
Dr. Deitel tiene una licenciatura y una maestría por el MIT y un doctorado de la Universidad de Boston. Tiene
20 años de experiencia como profesor universitario, la cual incluye un puesto vitalicio y el haber sido presidente
del departamento de Ciencias de la computación en el Boston College antes de fundar, con su hijo Paul J. Deitel,
Deitel & Associates, Inc. Él y Paul son coautores de varias docenas de libros y paquetes multimedia, y piensan
escribir muchos más. Los textos de los Deitel se han ganado el reconocimiento internacional y han sido tradu-
cidos al japonés, alemán, ruso, español, chino tradicional, chino simplificado, coreano, francés, polaco, italiano,
portugués, griego, urdú y turco. El Dr. Deitel ha impartido cientos de seminarios profesionales para grandes
empresas, instituciones académicas, organizaciones gubernamentales y diversos sectores del ejército.
Acerca de Deitel & Associates, Inc.
Deitel & Associates, Inc. es una empresa reconocida a nivel mundial, dedicada al entrenamiento corporativo y
la creación de contenido, con especialización en lenguajes de programación, tecnología de software para Inter-
net/World Wide Web, educación de tecnología de objetos y desarrollo de negocios por Internet a través de su
xxviii Prefacio
Iniciativa de Negocios en Internet. La empresa proporciona cursos, que son impartidos por instructores, sobre
la mayoría de los lenguajes y plataformas de programación, como Java, Java Avanzado, C, C++, C#, Visual C++,
Visual Basic, XML, Perl, Python, tecnología de objetos y programación en Internet y World Wide Web. Los fun-
dadores de Deitel & Associates, Inc. son el Dr. Harvey M. Deitel y Paul J. Deitel. Sus clientes incluyen muchas de
las empresas más grandes del mundo, agencias gubernamentales, sectores del ejército e instituciones académicas.
A lo largo de su sociedad editorial de 30 años con Prentice Hall, Deitel & Associates Inc. ha publicado libros
de texto de vanguardia sobre programación, libros profesionales, multimedia interactiva en CD como los Cyber
Classrooms, Cursos Completos de Capacitación, cursos de capacitación basados en Web y contenido electrónico para
los populares sistemas de administración de cursos WebCT, Blackboard y CourseCompass de Pearson. Deitel &
Associates, Inc. y los autores pueden ser contactados mediante correo electrónico en:
deitel@deitel.com
Para conocer más acerca de Deitel & Associates, Inc., sus publicaciones y su currículum mundial de la Serie de
Capacitación Corporativa DIVE INTO®, visite:
www.deitel.com
y suscríbase al boletín gratuito de correo electrónico, Deitel® Buzz Online, en:
www.deitel.com/newsletter/subscribe.html
Puede verificar la lista creciente de Centros de Recursos Deitel en:
www.deitel.com/resourcecenters.html
Quienes deseen comprar publicaciones de Deitel pueden hacerlo en:
www.deitel.com/books/index.html
Las empresas, el gobierno, las instituciones militares y académicas que deseen realizar pedidos en masa deben
hacerlo directamente con Prentice Hall. Para obtener más información, visite:
www.prenhall.com/mischtm/support.html#order
Prefacio xxix
Antes de comenzar a utilizar este libro, debe seguir las instrucciones de esta sección para asegurarse que Java esté
instalado de manera apropiada en su computadora.
Convenciones de fuentes y nomenclatura
Utilizamos varios tipos de letra para diferenciar los componentes en la pantalla (como los nombres de menús y
los elementos de los mismos) y el código o los comandos en Java. Nuestra convención es hacer hincapié en los
componentes en pantalla en una fuente Helvetica sans-serif en negritas (por ejemplo, el menú Archivo) y enfatizar
el código y los comandos de Java en una fuente Lucida sans-serif (por ejemplo, System.out.println()).
Kit de desarrollo de Java Standard Edition (JDK) 6
Los ejemplos en este libro se desarrollaron con el Kit de Desarrollo de Java Standard Edition (JDK) 6. Puede
descargar la versión más reciente y su documentación en:
java.sun.com/javase/6/download.jsp
Si tiene preguntas, envíe un correo electrónico a deitel@deitel.com. Le responderemos en breve.
Requerimientos de software y hardware del sistema
Procesador Pentium III de 500 MHz (mínimo) o de mayor velocidad; Sun® Java™ Studio Creator 2
Update 1 requiere un procesador Intel Pentium 4 de 1 GHz (o equivalente).
Microsoft Windows Server 2003, Windows XP (con Service Pack 2), Windows 2000 Professional (con
Service Pack 4).
Una de las siguientes distribuciones de Linux: Red Hat® Enterprise Linux 3, o Red Hat Fedora Core 3.
Mínimo 512 MB de memoria en RAM; Sun Java Studio Creator 2 Update 1 requiere 1 GB de RAM.
Mínimo 1.5 GB de espacio en disco duro.
Unidad de CD-ROM.
Conexión a Internet.
Explorador Web, Adobe® Acrobat® Reader® y una herramienta para descomprimir archivos zip.
Uso de los CD
Los ejemplos para Cómo programar en Java, 7ª edición se encuentran en los CD (Windows y Linux) que se inclu-
yen en este libro. Siga los pasos de la siguiente sección, Cómo copiar los ejemplos del libro del CD, para copiar el
directorio de ejemplos apropiado del CD a su disco duro. Le sugerimos trabajar desde su disco duro en lugar de
hacerlo desde su unidad de CD por dos razones: 1, los CD son de sólo lectura, por lo que no podrá guardar sus
aplicaciones en ellos; 2 es posible acceder a los archivos con mayor rapidez desde un disco duro que de un CD.
Los ejemplos del libro también están disponibles para descargarse de:
www.deitel.com/books/jhtp7/
www.pearsoneducacion.net/deitel/
La interfaz para el contenido del CD de Microsoft® Windows® está diseñada para iniciarse de manera auto-
mática, a través del archivo AUTORUN.EXE. Si no aparece una pantalla de inicio cuando inserte el CD en su
•
•
•
•
•
•
•
•
Antes de empezar
computadora, haga doble clic en el archivo welcome.htm para iniciar la interfaz del CD para el estudiante,
o consulte el archivo readme.txt en el CD. Para iniciar la interfaz del CD para Linux, haga doble clic en el
archivo welcome.html.
Cómo copiar los ejemplos del libro del CD
Las capturas de pantalla de esta sección pueden diferir un poco de lo que usted verá en su computadora, de acuer-
do con el sistema operativo y el explorador Web de que disponga. Las instrucciones de los siguientes pasos asumen
que está utilizando Microsoft Windows.
1. Insertar el CD. Inserte el CD que se incluye con este libro en la unidad de CD de su computadora.
A continuación deberá aparecer de manera automática la página Web welcome.htm (figura 1) en Win-
dows. También puede utilizar el Explorador de Windows para ver el contenido del CD y hacer doble
clic en welcome.htm para mostrar esta página.
2. Abrir el directorio del CD-ROM. Haga clic en el vínculo Browse CD Contents (Explorar contenido
del CD) (figura 1) para ver el contenido del CD.
Figura 1 | Página de bienvenida para el CD de Cómo programar en Java.
Haga clic en el vínculo Browse
CD Contents para acceder al
contenido del CD
3. Copiar el directorio ejemplos. Haga clic en el directorio ejemplos (figura 2), después seleccione
Copiar. A continuación, use el Explorador de Windows para ver el contenido de su unidad C:. (Tal vez
necesite hacer clic en un vínculo para mostrar el contenido de la unidad). Una vez que se muestre el con-
tenido, haga clic en cualquier parte y seleccione la opción Pegar del menú Editar para copiar el direc-
torio ejemplos del CD a su unidad C:. [Nota: guardamos los ejemplos directamente en la unidad C:
y hacemos referencia a esta unidad a lo largo del texto. Puede optar por guardar sus archivos en una
unidad distinta, con base en la configuración de su computadora, en el laboratorio de su escuela o sus
preferencias personales. Si trabaja en un laboratorio de computadoras, consulte con su profesor para
obtener más información para confirmar en dónde se deben guardar los ejemplos].
Modificación de la propiedad de sólo lectura de los archivos
Los archivos de ejemplo que copió a su computadora desde el CD son de sólo lectura. A continuación eliminará
la propiedad de sólo lectura, para poder modificar y ejecutar los ejemplos.
1. Abrir el cuadro de diálogo Propiedades. Haga clic con el botón derecho del ratón en el directorio
ejemplos y seleccione Propiedades. A continuación aparecerá el cuadro de diálogo Propiedades de
ejemplos (figura 3).
Antes de empezar xxxi
2. Cambiar la propiedad de sólo lectura. En la sección Atributos de este cuadro de diálogo, haga clic en
el botón Sólo lectura para eliminar la marca de verificación (figura 4). Haga clic en Aplicar para aplicar
los cambios.
Figura 3 | Cuadro de diálogo Propiedades de ejemplos.
xxxii Antes de empezar
Figura 2 | Copia del directorio ejemplos.
Haga clic con el botón derecho del
ratón en el directorio ejemplos
Seleccione Copiar
Figura 4 | Desactivar la casilla de verificación Sólo lectura.
Figura 5 | Eliminar la propiedad de sólo lectura para todos los archivos en el directorio ejemplos.
Antes de empezar xxxiii
3. Cambiar la propiedad para todos los archivos. Al hacer clic en Aplicar se mostrará la ventana Confir-
mar cambios de atributos (figura 5). En esta ventana, haga clic en el botón de opción Aplicar cambios
a esta carpeta y a todas las subcarpetas y archivos y haga clic en Aceptar para eliminar la propiedad
de sólo lectura para todos los archivos y directorios en el directorio ejemplos.
Desactive el atributo
Sólo lectura
Instalación del Kit de Desarrollo de Java Standard Edition (JDK)
Antes de ejecutar las aplicaciones de este libro o de crear sus propias aplicaciones, debe instalar el Kit de Desarrollo
de Java Standard Edition (JDK) 6 o una herramienta de desarrollo para Java que soporte a Java SE 6.
Puede descargar el JDK 6 y su documentación de java.sun.com/javase/6/download.jsp. Haga clic en
el botón » DOWNLOAD para JDK 6. Debe aceptar el acuerdo de licencia antes de descargar. Una vez que acepte el
acuerdo, haga clic en el vínculo para el instalador de su plataforma. Guarde el instalador en su disco duro y no
olvide en dónde lo guardó. Antes de instalar, lea con cuidado las instrucciones de instalación del JDK para su
plataforma, que se encuentran en java.sun.com/javase/6/webnotes/install/index.html.
Después de descargar el instalador del JDK, haga doble clic en el programa instalador para empezar a insta-
larlo. Le recomendamos que acepte todas las opciones de instalación predeterminadas. Si modifica el directorio
predeterminado, asegúrese de anotar el nombre y la ubicación exactos que eligió, ya que necesitará esta informa-
ción más adelante en el proceso de instalación. En Windows, el JDK se coloca, de manera predeterminada, en el
siguiente directorio:
C:Archivos de programaJavajdk1.6.0
Haga clic en este botón de opción
para eliminar la propiedad Sólo
lectura para todos los archivos
Establecer la variable de entorno PATH
La variable de entorno PATH en su computadora indica qué directorios debe buscar la computadora cuando inten-
te localizar aplicaciones, como aquellas que le permiten compilar y ejecutar sus aplicaciones en Java (conocidas
como javac.exe y java.exe, respectivamente). Ahora aprenderá a establecer la variable de entorno PATH en su
computadora para indicar en dónde están instaladas las herramientas del JDK.
1. Abrir el cuadro de diálogo Propiedades del sistema. Haga clic en Inicio > Panel de control > Sistema
para mostrar el cuadro de diálogo Propiedades del sistema (figura 6). [Nota: su cuadro de diálogo Pro-
piedades del sistema puede tener una apariencia distinta al que se muestra en la figura 6, dependiendo
de la versión de Microsoft Windows. Este cuadro de diálogo específico es de una computadora que
ejecuta Microsoft Windows XP. Sin embargo, el que aparece en su computadora podría incluir distinta
información].
2. Abrir el cuadro de diálogo Variables de entorno. Seleccione la ficha Opciones avanzadas de la parte
superior del cuadro de diálogo Propiedades del sistema (figura 7). Haga clic en el botón Variables de
entorno para desplegar el cuadro de diálogo Variables de entorno (figura 8).
3. Editar la variable PATH. Desplácese por el cuadro Variables del sistema para seleccionar la variable
PATH. Haga clic en el botón Modificar. Esto hará que se despliegue el cuadro de diálogo Modificar la
variable del sistema (figura 9).
4. Modificar la variable PATH. Coloque el cursor dentro del campo Valor de variable. Use la flecha
izquierda para desplazar el cursor hasta el inicio de la lista. Al principio de la lista, escriba el nombre del
directorio en el que colocó el JDK, seguido de bin; (figura 10). Agregue C:Archivos de progra-
ma Javajdk1.6.0bin; a la variable PATH, si eligió el directorio de instalación predeterminado. No
coloque espacios antes o después de lo que escriba. No se permiten espacios antes o después de cada valor en
una variable de entorno. Haga clic en el botón Aceptar para aplicar sus cambios a la variable PATH.
Si no establece la variable PATH de manera correcta, al utilizar las herramientas del JDK recibirá un mensaje
como éste:
‘java’ no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.
En este caso, regrese al principio de esta sección y vuelva a comprobar sus pasos. Si ha descargado una versión
más reciente del JDK, tal vez necesite modificar el nombre del directorio de instalación del JDK en la variable
PATH.
Figura 6 | Cuadro de diálogo Propiedades del sistema.
xxxiv Antes de empezar
Antes de empezar xxxv
Figura 8 | Cuadro de diálogo Variables de entorno.
Figura 7 | Ficha Opciones avanzadas del cuadro de diálogo Propiedades del sistema.
Seleccione la ficha
Opciones avanzadas
Haga clic en el botón
Variables de entorno
Figura 9 | Cuadro de diálogo Modificar la variable del sistema.
Figura 10 | Modificación de la variable PATH.
Establecer la variable de entorno CLASSPATH
Si trata de ejecutar un programa en Java y recibe un mensaje como:
Exception in thread “main” java.lang.NoClassDefFoundError: SuClase
entonces su sistema tiene una variable de entorno CLASSPATH que debe modificarse. Para corregir el error anterior,
siga los pasos para establecer la variable de entorno PATH, localice la variable CLASSPATH y modifique su valor para
que incluya lo siguiente:
.;
al principio de su valor (sin espacios antes o después de estos caracteres).
Ahora está listo para empezar sus estudios de Java con el libro Cómo programar en Java, 7ª edición. ¡Espera-
mos que lo disfrute!
xxxvi Antes de empezar
Introducción
a las
computadoras,
Internet y Web
OBJETIVOS
En este capítulo aprenderá a:
Comprender los conceptos básicos de hardware y software.
Conocer los conceptos básicos de la tecnología de objetos,
como las clases, objetos, atributos, comportamientos,
encapsulamiento, herencia y polimorfismo.
Familiarizarse con los distintos lenguajes de programación.
Saber qué lenguajes de programación se utilizan más.
Comprender un típico entorno de desarrollo en Java.
Entender el papel de Java en el desarrollo de aplicaciones
cliente/servidor distribuidas para Internet y Web.
Conocer la historia de UML: el lenguaje de diseño orientado
a objetos estándar en la industria.
Conocer la historia de Internet y World Wide Web.
Probar aplicaciones en Java.
Q
Q
Q
Q
Q
Q
Q
Q
Q
Nuestra vida se malgasta
por los detalles…
simplificar, simplificar.
—Henry David Thoreau
La principal cualidad del
lenguaje es la claridad.
—Galen
Mi sublime objetivo deberé
llevarlo a cabo a tiempo.
—W. S. Gilbert
Tenía un maravilloso
talento para empacar
estrechamente el
pensamiento, haciéndolo
portable.
—Thomas B. Macaulay
“¡Caray, creo que de los dos,
el intérprete es el más difícil
de entender!”
—Richard Brinsley Sheridan
El hombre sigue siendo
la computadora más
extraordinaria de todas.
—John F. Kennedy
1
2 Capítulo 1 Introducción a las computadoras, Internet y Web
1.1 Introducción
¡Bienvenido a Java! Hemos trabajado duro para crear lo que pensamos será una experiencia de aprendizaje infor-
mativa, divertida y retadora para usted. Java es un poderoso lenguaje de programación, divertido para los princi-
piantes y apropiado para los programadores experimentados que desarrollan sistemas de información de tamaño
considerable. Cómo programar en Java, 7ª edición es una herramienta efectiva de aprendizaje para cada una de
estas audiencias.
Pedagogía
La parte central del libro se enfoca en la claridad de los programas, a través de las técnicas comprobadas de la pro-
gramación orientada a objetos. Los principiantes aprenderán programación de manera correcta, desde el principio.
La presentación es clara, simple y tiene muchas ilustraciones. Incluye cientos de programas completos y funcio-
nales en Java, y muestra la salida que se obtiene al ejecutar estos programas en una computadora. Enseñamos las
características de Java en un contexto de programas completos y funcionales; a esto le llamamos el método de
código activo (Live-Code™). Los programas de ejemplo están disponibles en el CD que acompaña a este libro.
También puede descargarlos de los sitios Web www.deitel.com/books/jhtp7/ o www.pearsoneducacion.
net.com/deitel.
Fundamentos
Los primeros capítulos presentan los fundamentos de las computadoras, la programación de éstas y el lenguaje de
programación Java, con lo cual se provee una base sólida para un análisis más detallado de Java en los capítulos
posteriores. Los programadores experimentados tienden a leer los primeros capítulos rápidamente, y descubren
que el análisis de Java en los capítulos posteriores es riguroso y retador.
La mayoría de las personas están familiarizadas con las emocionantes tareas que realizan las computado-
ras. Por medio de este libro, usted aprenderá a programar las computadoras para que realicen dichas tareas. El
1.1 Introducción
1.2 ¿Qué es una computadora?
1.3 Organización de una computadora
1.4 Los primeros sistemas operativos
1.5 Computación personal, distribuida y cliente/servidor
1.6 Internet y World Wide Web
1.7 Lenguajes máquina, ensambladores y de alto nivel
1.8 Historia de C y C++
1.9 Historia de Java
1.10 Bibliotecas de clases de Java
1.11 FORTRAN, COBOL, Pascal y Ada
1.12 BASIC, Visual Basic, Visual C++, C# y .NET
1.13 Entorno de desarrollo típico en Java
1.14 Generalidades acerca de Java y este libro
1.15 Prueba de una aplicación en Java
1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML
1.17 Web 2.0
1.18 Tecnologías de software
1.19 Conclusión
1.20 Recursos Web
Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios
Pla
n
g
e
ne
r
a
l
software (las instrucciones que usted escribe para indicar a la computadora que realice acciones y tome decisio-
nes) es quien controla a las computadoras (conocidas comúnmente como hardware). Java, desarrollado por Sun
Microsystems, es uno de los lenguajes para desarrollo de software más populares en la actualidad.
Java Standard Edition 6 (Java SE 6) y el Kit de Desarrollo de Java 6 (JDK 6)
Este libro se basa en la plataforma Java Standard Edition 6 (Java SE 6) de Sun, también conocida como Mus-
tang. Sun ofrece una implementación de Java SE 6, conocida como Kit de Desarrollo de Java (JDK), que
incluye las herramientas necesarias para escribir software en Java. Nosotros utilizamos el JDK versión 6.0 para los
programas en este libro. Por lo regular, Sun actualiza el JDK para corregir errores: para descargar la versión más
reciente del JDK 6, visite java.sun.com/javase/6/download.jsp.
Evolución de la computación y de la programación
El uso de las computadoras se está incrementando en casi cualquier campo de trabajo; los costos de se han redu-
cido en forma dramática, debido al rápido desarrollo en la tecnología de hardware y software. Las computadoras
que ocupaban grandes habitaciones y que costaban millones de dólares, hace algunas décadas, ahora pueden
colocarse en las superficies de chips de silicio más pequeños que una uña, y con un costo de quizá unos cuan-
tos dólares cada uno. Por fortuna, el silicio es uno de los materiales más abundantes en el planeta (es uno de
los ingredientes de la tierra). La tecnología de los chips de silicio ha vuelto tan económica a la tecnología de la
computación que cientos de millones de computadoras de uso general se encuentran actualmente ayudando
a la gente de todo el mundo en: empresas, la industria, el gobierno y en sus vidas. Dicho número podría dupli-
carse fácilmente en unos cuantos años.
A través de los años, muchos programadores aprendieron la metodología conocida como programación
estructurada. Usted aprenderá tanto la programación estructurada como la novedosa y excitante metodología de
la programación orientada a objetos. ¿Por qué enseñamos ambas? La programación orientada a objetos es la
metodología clave utilizada hoy en día por los programadores. Usted creará y trabajará con muchos objetos de
software en este libro. Sin embargo, descubrirá que la estructura interna de estos objetos se construye, a menudo,
utilizando técnicas de programación estructurada. Además, la lógica requerida para manipular objetos se expresa
algunas veces mediante la programación estructurada.
El lenguaje de elección para las aplicaciones en red
Java se ha convertido en el lenguaje de elección para implementar aplicaciones basadas en Internet, y software
para dispositivos que se comunican a través de una red. Ahora, los estéreos y otros dispositivos en los hogares
pueden conectarse entre sí mediante el uso de tecnología Java. ¡En la conferencia JavaOne en mayo del 2006,
Sun anunció que había mil millones de teléfonos móviles y dispositivos portátiles habilitados para Java! Java ha
evolucionado rápidamente en el ámbito de las aplicaciones de gran escala. Es el lenguaje preferido para satisfacer
la mayoría de las necesidades de programación de muchas organizaciones.
Java ha evolucionado tan rápidamente que publicamos esta séptima edición de Cómo programar en Java
justamente 10 años después de publicar la primera edición. Java ha crecido tanto que cuenta con otras dos edi-
ciones. La edición Java Enterprise Edition (Java EE) está orientada hacia el desarrollo de aplicaciones de red
distribuidas, de gran escala, y aplicaciones basadas en Web. La plataforma Java Micro Edition (Java ME) está
orientada hacia el desarrollo de aplicaciones para dispositivos pequeños, con memoria limitada, como los teléfo-
nos celulares, radiolocalizadores y PDAs.
Permanezca en contacto con nosotros
Está a punto de comenzar una ruta de desafíos y recompensas. Mientras tanto, si desea comunicarse con nosotros,
envíenos un correo a deitel@deitel.com o explore nuestro sitio Web en www.deitel.com. Le responderemos
a la brevedad. Para mantenerse al tanto de los desarrollos con Java en Deitel & Associates, regístrese para recibir
nuestro boletín de correo electrónico, Deitel® Buzz Online en
www.deitel.com/newsletter/subscribe.html
Para obtener material adicional sobre Java, visite nuestra creciente lista de centros de recursos en www.deitel.
com/ResourceCenters.html. Esperamos que disfrute aprender con Cómo programar en Java, 7ª edición.
1.1 Introducción 3
4 Capítulo 1 Introducción a las computadoras, Internet y Web
1.2 ¿Qué es una computadora?
Una computadora es un dispositivo capaz de realizar cálculos y tomar decisiones lógicas a velocidades de millones
(incluso de miles de millones) de veces más rápidas que los humanos. Por ejemplo, muchas de las computadoras
personales actuales pueden realizar varios miles de millones de cálculos en un segundo. Una persona con una
calculadora podría requerir toda una vida para completar el mismo número de operaciones. (Puntos a considerar:
¿cómo sabría que la persona sumó los números de manera correcta?, ¿cómo sabría que la computadora sumó los
números de manera correcta?) ¡Las supercomputadoras actuales más rápidas pueden realizar billones de sumas
por segundo!
Las computadoras procesan los datos bajo el control de conjuntos de instrucciones llamadas programas de
cómputo. Estos programas guían a la computadora a través de conjuntos ordenados de acciones especificadas por
gente conocida como programadores de computadoras.
Una computadora está compuesta por diversos dispositivos (como teclado, monitor, ratón, discos, memoria,
DVD, CD-ROM y unidades de procesamiento) conocidos como hardware. A los programas que se ejecutan
en una computadora se les denomina software. Los costos de las piezas de hardware han disminuido de manera
espectacular en años recientes, al punto en que las computadoras personales se han convertido en artículos domés-
ticos. En este libro aprenderá métodos comprobados que pueden reducir los costos de desarrollo del software:
programación orientada a objetos y (en nuestro Ejemplo práctico de Ingeniería de Software en los capítulos 2-8
y 10) diseño orientado a objetos.
1.3 Organización de una computadora
Independientemente de las diferencias en su apariencia física, casi todas las computadoras pueden representarse
mediante seis unidades lógicas o secciones:
1. Unidad de entrada. Esta sección “receptora” obtiene información (datos y programas de cómputo)
desde diversos dispositivos de entrada y pone esta información a disposición de las otras unidades para
que pueda procesarse. La mayoría de la información se introduce a través de los teclados y ratones; tam-
bién puede introducirse de muchas otras formas, como hablar con su computadora, digitalizar imágenes
y desde una red, como Internet.
2. Unidad de salida. Esta sección de “embarque” toma información que ya ha sido procesada por la
computadora y la coloca en los diferentes dispositivos de salida, para que esté disponible fuera de
la computadora. Hoy en día, la mayoría de la información de salida de las computadoras se despliega en
el monitor, se imprime en papel o se utiliza para controlar otros dispositivos. Las computadoras también
pueden dar salida a su información a través de redes como Internet.
3. Unidad de memoria. Esta sección de “almacén” de acceso rápido, pero con relativa baja capacidad,
retiene la información que se introduce a través de la unidad de entrada, para que esté disponible de
manera inmediata para procesarla cuando sea necesario. La unidad de memoria también retiene la infor-
mación procesada hasta que ésta pueda colocarse en los dispositivos de salida por la unidad de salida.
Por lo general, la información en la unidad de memoria se pierde cuando se apaga la computadora. Con
frecuencia, a esta unidad de memoria se le llama memoria o memoria primaria.
4. Unidad aritmética y lógica (ALU). Esta sección de “manufactura” es la responsable de realizar cálcu-
los como suma, resta, multiplicación y división. Contiene los mecanismos de decisión que permiten a
la computadora hacer cosas como, por ejemplo, comparar dos elementos de la unidad de memoria para
determinar si son iguales o no.
5. Unidad central de procesamiento (CPU). Esta sección “administrativa” coordina y supervisa la ope-
ración de las demás secciones. La CPU le indica a la unidad de entrada cuándo debe grabarse la infor-
mación dentro de la de memoria; a la ALU, cuándo debe utilizarse la información de la memoria
para los cálculos; y a la unidad de salida, cuándo enviar la información desde la memoria hasta ciertos
dispositivos de salida. Muchas de las computadoras actuales contienen múltiples CPUs y, por lo tanto,
pueden realizar diversas operaciones de manera simultánea (a estas computadoras se les conoce como
multiprocesadores).
6. Unidad de almacenamiento secundario. Ésta es la sección de “almacén” de alta capacidad y de larga
duración. Los programas o datos que no se encuentran en ejecución por las otras unidades, normalmen-
te se colocan en dispositivos de almacenamiento secundario (por ejemplo, el disco duro) hasta que son
requeridos nuevamente, posiblemente horas, días, meses o incluso años después. El tiempo para acceder
a la información en almacenamiento secundario es mucho mayor que el que se necesita para acceder a la
información de la memoria principal, pero el costo por unidad de memoria secundaria es mucho menor
que el correspondiente a la unidad de memoria principal. Los CDs y DVDs son ejemplos de dispositi-
vos de almacenamiento secundario y pueden contener hasta cientos de millones y miles de millones de
caracteres, respectivamente.
1.4 Los primeros sistemas operativos
Las primeras computadoras eran capaces de realizar solamente una tarea o trabajo a la vez. A esta forma de ope-
ración de la computadora a menudo se le conoce como procesamiento por lotes (batch) de un solo usuario. La
computadora ejecuta un solo programa a la vez, mientras procesa los datos en grupos o lotes. En estos primeros
sistemas, los usuarios generalmente asignaban sus trabajos a un centro de cómputo que los introducía en paquetes
de tarjetas perforadas, y a menudo tenían que esperar horas, o incluso días, antes de que sus resultados impresos
regresaran a sus escritorios.
El software denominado sistema operativo se desarrolló para facilitar el uso de la computadora. Los prime-
ros sistemas operativos administraban la suave transición entre trabajos, incrementando la cantidad de trabajo, o
el flujo de datos, que las computadoras podían procesar.
Conforme las computadoras se volvieron más poderosas, se hizo evidente que un proceso por lotes para un
solo usuario era ineficiente, debido al tiempo que se malgastaba esperando a que los lentos dispositivos de entra-
da/salida completaran sus tareas. Se pensó que era posible realizar muchos trabajos o tareas que podrían compartir
los recursos de la computadora y lograr un uso más eficiente. A esto se le conoce como multiprogramación; que
significa la operación simultánea de muchas tareas que compiten para compartir los recursos de la computadora.
Aun con los primeros sistemas operativos con multiprogramación, los usuarios seguían enviando sus tareas en
paquetes de tarjetas perforadas y esperaban horas, incluso hasta días, por los resultados.
En la década de los sesenta, varios grupos en la industria y en las universidades marcaron la pauta de los sis-
temas operativos de tiempo compartido. El tiempo compartido es un caso especial de la multiprogramación, ya
que los usuarios acceden a la computadora a través de terminales que, por lo general, son dispositivos compuestos
por un teclado y un monitor. Puede haber docenas o incluso cientos de usuarios compartiendo la computadora
al mismo tiempo. La computadora en realidad no ejecuta los procesos de todos los usuarios a la vez. Lo que hace
es ejecutar una pequeña porción del trabajo de un usuario y después procede a dar servicio al siguiente usuario,
con la posibilidad de proporcionar el servicio a cada usuario varias veces por segundo. Así, los programas de los
usuarios aparentemente se ejecutan de manera simultánea. Una ventaja del tiempo compartido es que el usuario
recibe respuestas casi inmediatas a las peticiones.
1.5 Computación personal, distribuida y cliente/servidor
En 1977, Apple Computer popularizó el fenómeno de la computación personal. Las computadoras se volvie-
ron sumamente económicas, de manera que la gente pudo adquirirlas para su uso personal o para negocios. En
1981, IBM, el vendedor de computadoras más grande del mundo, introdujo la Computadora Personal (PC) de
IBM. Con esto se legitimó rápidamente la computación en las empresas, en la industria y en las organizaciones
gubernamentales.
Estas computadoras eran unidades “independientes” (la gente transportaba sus discos de un lado a otro para
compartir información; a esto se le conoce comúnmente como “sneakernet”). Aunque las primeras computadoras
personales no eran lo suficientemente poderosas para compartir el tiempo entre varios usuarios, podían interco-
nectarse mediante redes computacionales, algunas veces a través de líneas telefónicas y otras mediante redes de
área local (LANs) dentro de una empresa. Esto derivó en el fenómeno denominado computación distribuida,
en donde todos los cálculos informáticos de una empresa, en vez de realizarse estrictamente dentro de un centro
de cómputo, se distribuyen mediante redes a los sitios en donde se realiza el trabajo de la empresa. Las compu-
tadoras personales eran lo suficientemente poderosas para manejar los requerimientos de cómputo de usuarios
individuales, y para manejar las tareas básicas de comunicación que involucraban la transferencia de información
entre una computadora y otra, de manera electrónica.
1.5 Computación personal, distribuida y cliente/servidor 5
6 Capítulo 1 Introducción a las computadoras, Internet y Web
Las computadoras personales actuales son tan poderosas como las máquinas de un millón de dólares de hace
apenas unas décadas. Las máquinas de escritorio más poderosas (denominadas estaciones de trabajo) propor-
cionan a cada usuario enormes capacidades. La información se comparte fácilmente a través de redes de compu-
tadoras, en donde algunas computadoras denominadas servidores almacenan datos que pueden ser utilizados por
computadoras cliente distribuidas en toda la red, de ahí el término de computación cliente/servidor. Java se
está utilizando ampliamente para escribir software para redes de computadoras y para aplicaciones cliente/servi-
dor distribuidas. Los sistemas operativos actuales más populares como Linux, Mac OS X y Microsoft Windows
proporcionan el tipo de capacidades que explicamos en esta sección.
1.6 Internet y World Wide Web
Internet (una red global de computadoras) tiene sus raíces en la década de 1960; su patrocinio estuvo a cargo del
Departamento de Defensa de los Estados Unidos. Diseñada originalmente para conectar los sistemas de cómputo
principales de aproximadamente una docena de universidades y organizaciones de investigación, actualmente,
Internet es utilizada por cientos de millones de computadoras y dispositivos controlados por computadora en
todo el mundo.
Con la introducción de World Wide Web (que permite a los usuarios de computadora localizar y ver docu-
mentos basados en multimedia, sobre casi cualquier tema, a través del ciberespacio), Internet se ha convertido
explosivamente en uno de los principales mecanismos de comunicación en todo el mundo.
Internet y World Wide Web se encuentran, sin duda, entre las creaciones más importantes y profundas de la
humanidad. En el pasado, la mayoría de las aplicaciones de computadora se ejecutaban en equipos que no estaban
conectados entre sí. Las aplicaciones de la actualidad pueden diseñarse para intercomunicarse entre computadoras
en todo el mundo. Internet mezcla las tecnologías de la computación y las comunicaciones. Facilita nuestro tra-
bajo. Hace que la información esté accesible en forma instantánea y conveniente para todo el mundo. Hace posi-
ble que los individuos y negocios pequeños locales obtengan una exposición mundial. Está cambiando la forma
en que se hacen los negocios. La gente puede buscar los mejores precios para casi cualquier producto o servicio.
Los miembros de las comunidades con intereses especiales pueden mantenerse en contacto unos con otros. Los
investigadores pueden estar inmediatamente al tanto de los últimos descubrimientos.
Cómo programar en Java, 7ª edición presenta técnicas de programación que permiten a las aplicaciones en Java
utilizar Internet y Web para interactuar con otras aplicaciones. Estas técnicas, junto con otras más, permiten a los
programadores de Java desarrollar el tipo de aplicaciones distribuidas de nivel empresarial que se utilizan actual-
mente en la industria. Se pueden escribir aplicaciones en Java para ejecutarse en cualquier tipo de computadora,
con lo cual se reduce en gran parte el tiempo y el costo de desarrollo de sistemas. Si a usted le interesa desarrollar
aplicaciones que se ejecuten a través de Internet y Web, aprender Java puede ser la clave para que reciba oportu-
nidades retadoras y remuneradoras en su profesión.
1.7 Lenguajes máquina, ensambladores y de alto nivel
Los programadores escriben instrucciones en diversos lenguajes de programación, algunos de los cuales compren-
de directamente la computadora, mientras que otros requieren pasos intermedios de traducción. En la actualidad
se utilizan cientos de lenguajes de computación. Éstos se dividen en tres tipos generales:
1. Lenguajes máquina.
2. Lenguajes ensambladores.
3. Lenguajes de alto nivel.
Cualquier computadora puede entender de manera directa sólo su propio lenguaje máquina; que es su
“lenguaje natural”, y como tal, está definido por el diseño del hardware de dicha computadora. Por lo general,
los lenguajes máquina consisten en cadenas de números (que finalmente se reducen a 1s y 0s) que instruyen a las
computadoras para realizar sus operaciones más elementales, una a la vez. Los lenguajes máquina son dependien-
tes de la máquina (es decir, un lenguaje máquina en particular puede usarse solamente en un tipo de compu-
tadora). Dichos lenguajes son difíciles de comprender para los humanos, el siguiente ejemplo muestra uno de los
primeros programas en lenguaje máquina, el cual suma el pago de las horas extras al sueldo base y almacena el
resultado en el sueldo bruto:
+1300042774
+1400593419
+1200274027
La programación en lenguaje máquina era demasiado lenta y tediosa para la mayoría de los programadores.
En vez de utilizar las cadenas de números que las computadoras podían entender directamente, los programadores
empezaron a utilizar abreviaturas del inglés para representar las operaciones elementales. Estas abreviaturas for-
maron la base de los lenguajes ensambladores. Los programas traductores conocidos como ensambladores
se desarrollaron para convertir los primeros programas en lenguaje ensamblador a lenguaje máquina, a la velo-
cidad de la computadora. A continuación se muestra un ejemplo de un programa en lenguaje ensamblador, que
también suma el pago de las horas extras al sueldo base y almacena el resultado en el sueldo bruto:
load sueldobase
add sueldoextra
store sueldobruto
Aunque este código es más claro para los humanos, las computadoras no lo pueden entender sino hasta que se
traduce en lenguaje máquina.
El uso de las computadoras se incrementó rápidamente con la llegada de los lenguajes ensambladores, pero
los programadores aún requerían de muchas instrucciones para llevar a cabo incluso hasta las tareas más simples.
Para agilizar el proceso de programación se desarrollaron los lenguajes de alto nivel, en donde podían escribirse
instrucciones individuales para realizar tareas importantes. Los programas traductores, denominados compilado-
res, convierten, a lenguaje máquina, los programas que están en lenguaje de alto nivel. Estos últimos permiten a
los programadores escribir instrucciones que son muy similares al inglés común, y contienen la notación matemá-
tica común. Un programa de nómina escrito en un lenguaje de alto nivel podría contener una instrucción como
la siguiente:
sueldoBruto = sueldoBase + sueldoExtra
Obviamente, desde el punto de vista del programador, los lenguajes de alto nivel son mucho más recomen-
dables que los lenguajes máquina o ensamblador. C, C++ y los lenguajes .NET de Microsoft (por ejemplo, Visual
Basic .NET, Visual C++ .NET y C#) son algunos de los lenguajes de programación de alto nivel que más se
utilizan; sin embargo, Java es el más utilizado.
El proceso de compilación de un programa escrito en lenguaje de alto nivel a un lenguaje máquina puede
tardar un tiempo considerable en la computadora. Los programas intérpretes se desarrollaron para ejecutar
programas en lenguaje de alto nivel directamente, aunque con más lentitud. Los intérpretes son populares en los
entornos de desarrollo de programas, en los cuales se agregan nuevas características y se corrigen los errores. Una
vez que se desarrolla un programa por completo, se puede producir una versión compilada para ejecutarse con la
mayor eficiencia.
Actualmente se sabe que existen dos formas de traducir un programa en lenguaje de alto nivel a un formato
que la computadora pueda entender: compilación e interpretación. Como veremos en la sección 1.13, Java utiliza
una mezcla inteligente de estas tecnologías.
1.8 Historia de C y C++
Java evolucionó de C++, el cual evolucionó de C, que a su vez evolucionó de BCPL y B. En 1967, Martin
Richards desarrolló BCPL como un lenguaje para escribir software para sistemas operativos y compiladores. Ken
Thompson modeló muchas características en su lenguaje B a partir del trabajo de sus contrapartes en BCPL, y
utilizó a B para crear las primeras versiones del sistema operativo UNIX, en los laboratorios Bell en 1970.
El lenguaje C evolucionó a partir de B, gracias al trabajo de Dennis Ritchie en los laboratorios Bell, y se
implementó originalmente en 1972. Inicialmente, se hizo muy popular como lenguaje de desarrollo para el sis-
tema operativo UNIX. En la actualidad, la mayoría del código para los sistemas operativos de propósito general
(por ejemplo, los que se encuentran en las computadoras portátiles, de escritorio, estaciones de trabajo y pequeños
servidores) se escribe en C o C++.
A principios de la década de los ochenta, Bjarne Stroustrup desarrolló una extensión de C en los laboratorios
Bell: C++. Este lenguaje proporciona un conjunto de características que “pulen” al lenguaje C pero, lo más impor-
1.8 Historia de C y C++ 7
8 Capítulo 1 Introducción a las computadoras, Internet y Web
tante es que proporciona la capacidad de una programación orientada a objetos (que describiremos con más detalle
en la sección 1.16 y en todo el libro). C++ es un lenguaje híbrido: es posible programar en un estilo parecido a C, en
un estilo orientado a objetos, o en ambos.
Una revolución se está gestando en la comunidad del software. Escribir software de manera rápida, correcta
y económica es aún una meta difícil de alcanzar, en una época en que la demanda de nuevo y más poderoso
software se encuentra a la alza. Los objetos, o dicho en forma más precisa (como veremos en la sección 1.16),
las clases a partir de las cuales se crean los objetos, son en esencia componentes reutilizables de software. Hay
objetos de: fecha, hora, audio, automóvil, personas, etcétera; de hecho, casi cualquier sustantivo puede represen-
tarse como objeto de software en términos de atributos (como el nombre, color y tamaño) y comportamientos
(como calcular, desplazarse y comunicarse). Los desarrolladores de software están descubriendo que utilizar
una metodología de diseño e implementación modular y orientada a objetos puede hacer más productivos a
los grupos de desarrollo de software, que mediante las populares técnicas de programación anteriores, como la
programación estructurada. Los programas orientados a objetos son, a menudo, más fáciles de entender, corregir
y modificar. Java es el lenguaje de programación orientada a objetos que más se utiliza en el mundo.
1.9 Historia de Java
La contribución más importante a la fecha, por parte de la revolución del microprocesador, es que hizo posible el
desarrollo de las computadoras personales, que ahora suman miles de millones a nivel mundial. Las computadoras
personales han tenido un profundo impacto en la vida de las personas, y en la manera en que las empresas realizan
y administran su negocio.
Los microprocesadores están teniendo un profundo impacto en los dispositivos electrónicos inteligentes para
uso doméstico. Al reconocer esto, Sun Microsystems patrocinó en 1991 un proyecto interno de investigación
denominado Green, el cual desembocó en el desarrollo de un lenguaje basado en C++ al que su creador, James
Gosling, llamó Oak debido a un roble que tenía a la vista desde su ventana en las oficinas de Sun. Posteriormente
se descubrió que ya existía un lenguaje de computadora con el mismo nombre. Cuando un grupo de gente de Sun
visitó una cafetería local, sugirieron el nombre Java (una variedad de café) y así se quedó.
Pero el proyecto Green tuvo algunas dificultades. El mercado para los dispositivos electrónicos inteligentes de
uso doméstico no se desarrollaba tan rápido a principios de los noventa como Sun había anticipado. El proyecto
corría el riesgo de cancelarse. Pero para su buena fortuna, la popularidad de World Wide Web explotó en 1993
y la gente de Sun se dio cuenta inmediatamente del potencial de Java para agregar contenido dinámico, como
interactividad y animaciones, a las páginas Web. Esto trajo nueva vida al proyecto.
Sun anunció formalmente a Java en una importante conferencia que tuvo lugar en mayo de 1995. Java
generó la atención de la comunidad de negocios debido al fenomenal interés en World Wide Web. En la actua-
lidad, Java se utiliza para desarrollar aplicaciones empresariales a gran escala, para mejorar la funcionalidad de
los servidores Web (las computadoras que proporcionan el contenido que vemos en nuestros exploradores Web),
para proporcionar aplicaciones para los dispositivos domésticos (como teléfonos celulares, radiolocalizadores y
asistentes digitales personales) y para muchos otros propósitos.
1.10 Bibliotecas de clases de Java
Los programas en Java constan de varias piezas llamadas clases. Estas clases incluyen piezas llamadas métodos,
los cuales realizan tareas y devuelven información cuando completan esas tareas. Los programadores pueden crear
cada una de las piezas que necesitan para formar programas en Java. Sin embargo, la mayoría de los programado-
res en Java aprovechan las ricas colecciones de clases existentes en las bibliotecas de clases de Java, que también se
conocen como APIs (Interfaces de programación de aplicaciones) de Java. Por lo tanto, en realidad existen dos
fundamentos para conocer el “mundo” de Java. El primero es el lenguaje Java en sí, de manera que usted pueda
programar sus propias clases; el segundo son las clases incluidas en las extensas bibliotecas de clases de Java. A lo
largo de este libro hablaremos sobre muchas bibliotecas de clases; que proporcionan principalmente los vendedo-
res de compiladores, pero muchas de ellas las proporcionan vendedores de software independientes (ISVs).
Observación de ingeniería de software 1.1
Utilice un método de construcción en bloques para crear programas. Evite reinventar la rueda: use piezas existentes
siempre que sea posible. Esta reutilización de software es un beneficio clave de la programación orientada a objetos.
Incluimos muchos tips como Observaciones de ingeniería de software a lo largo del texto para explicar
los conceptos que afectan y mejoran la arquitectura y calidad de los sistemas de software. También resaltamos
otras clases de tips, incluyendo las Buenas prácticas de programación (que le ayudarán a escribir programas
más claros, comprensibles, de fácil mantenimiento, y fáciles de probar y depurar; es decir, eliminar errores de
programación), los Errores comunes de programación (problemas de los que tenemos que cuidarnos y evitar),
Tips de rendimiento (que servirán para escribir programas que se ejecuten más rápido y ocupen menos memo-
ria), Tips de portabilidad (técnicas que le ayudarán a escribir programas que se ejecuten, con poca o ninguna
modificación, en una variedad de computadoras; estos tips también incluyen observaciones generales acerca de
cómo logra Java su alto grado de portabilidad), Tips para prevenir errores (que le ayudarán a eliminar errores
de sus programas y, lo que es más importante, técnicas que le ayudarán a escribir programas libres de errores desde
el principio) y Observaciones de apariencia visual (que le ayudarán a diseñar la apariencia visual de las interfaces
gráficas de usuario de sus aplicaciones, además de facilitar su uso). Muchas de estas técnicas y prácticas son sólo
guías. Usted deberá, sin duda, desarrollar su propio estilo de programación.
Observación de ingeniería de software 1.2
Cuando programe en Java, generalmente utilizará los siguientes bloques de construcción: clases y métodos de las
bibliotecas de clases, clases y métodos creados por usted mismo, y clases y métodos creados por otros y puestos a dispo-
sición suya.
La ventaja de crear sus propias clases y métodos es que sabe exactamente cómo funcionan y puede examinar el
código en Java. La desventaja es el tiempo que consumen y el esfuerzo potencialmente complejo que se requiere.
Tip de rendimiento 1.1
Utilizar las clases y métodos de las APIs de Java en vez de escribir sus propias versiones puede mejorar el rendimiento
de sus programas, ya que estas clases y métodos están escritos cuidadosamente para funcionar de manera eficiente. Esta
técnica también reduce el tiempo de desarrollo de los programas.
Tip de portabilidad 1.1
Utilizar las clases y métodos de las APIs de Java en vez de escribir sus propias versiones mejora la portabilidad de sus
programas, ya que estas clases y métodos se incluyen en todas las implementaciones de Java.
Observación de ingeniería de software 1.3
Existen diversas bibliotecas de clases que contienen componentes reutilizables de software, y están disponibles a través
de Internet y Web, muchas de ellas en forma gratuita.
Para descargar la documentación de la API de Java, visite el sitio java.sun.com/javase/6/download.jsp
de Sun para Java.
1.11 FORTRAN, COBOL, Pascal y Ada
Se han desarrollado cientos de lenguajes de alto nivel, pero sólo unos cuantos han logrado una amplia aceptación.
Fortran (FORmula TRANslator, Traductor de fórmulas) fue desarrollado por IBM Corporation a mediados de
la década de los cincuenta para utilizarse en aplicaciones científicas y de ingeniería que requerían cálculos mate-
máticos complejos. En la actualidad, Fortran se utiliza ampliamente en aplicaciones de ingeniería.
COBOL (COmmon Business Oriented Language, Lenguaje común orientado a negocios) fue desa-
rrollado a finales de la década de los cincuenta por fabricantes de computadoras, el gobierno estadounidense y
usuarios de computadoras de la industria. COBOL se utiliza en aplicaciones comerciales que requieren de una
manipulación precisa y eficiente de grandes volúmenes de datos. Gran parte del software de negocios aún se pro-
grama en COBOL.
Durante la década de los sesenta, muchos de los grandes esfuerzos para el desarrollo de software encontraron
severas dificultades. Los itinerarios de software generalmente se retrasaban, los costos rebasaban en gran medida
a los presupuestos y los productos terminados no eran confiables. La gente comenzó a darse cuenta de que el
desarrollo de software era una actividad mucho más compleja de lo que habían imaginado. Las actividades de
1.11 FORTRAN, COBOL, Pascal y Ada 9
10 Capítulo 1 Introducción a las computadoras, Internet y Web
investigación en la década de los sesenta dieron como resultado la evolución de la programación estructurada
(un método disciplinado para escribir programas que fueran más claros, fáciles de probar y depurar, y más fáciles
de modificar que los programas extensos producidos con técnicas anteriores).
Uno de los resultados más tangibles de esta investigación fue el desarrollo del lenguaje de programación
Pascal por el profesor Niklaus Wirth, en 1971. Pascal, cuyo nombre se debe al matemático y filósofo Blaise Pascal
del siglo diecisiete, se diseñó para la enseñanza de la programación estructurada en ambientes académicos, y de
inmediato se convirtió en el lenguaje de programación preferido en la mayoría de las universidades. Pascal carece
de muchas de las características necesarias para poder utilizarse en aplicaciones comerciales, industriales y guber-
namentales, por lo que no ha sido muy aceptado en estos entornos.
El lenguaje de programación Ada se desarrolló bajo el patrocinio del Departamento de Defensa de los Esta-
dos Unidos (DOD) durante la década de los setenta y los primeros años de la década de los ochenta. Cientos de
lenguajes independientes se utilizaron para producir los sistemas de software masivos de comando y control del
departamento de defensa. Éste quería un solo lenguaje que pudiera satisfacer la mayoría de sus necesidades. El
nombre del lenguaje es en honor de Lady Ada Lovelace, hija del poeta Lord Byron. A Lady Lovelace se le atribuye
el haber escrito el primer programa para computadoras en el mundo, a principios de la década de1800 (para la
Máquina Analítica, un dispositivo de cómputo mecánico diseñado por Charles Babbage). Una de las característi-
cas importantes de Ada se conoce como multitarea, la cual permite a los programadores especificar que muchas
actividades ocurran en paralelo. Java, a través de una técnica que se conoce como subprocesamiento múltiple, tam-
bién permite a los programadores escribir programas con actividades paralelas.
1.12 BASIC, Visual Basic, Visual C++, C# y .NET
El lenguaje de programación BASIC (Beginner´s All-Purpose Symbolic Instruction Code, Código de instruc-
ciones simbólicas de uso general para principiantes) fue desarrollado a mediados de la década de los sesenta en el
Dartmouth College, como un medio para escribir programas simples. El propósito principal de BASIC era que
los principiantes se familiarizaran con las técnicas de programación.
El lenguaje Visual Basic de Microsoft se introdujo a principios de la década de los noventa para simplificar el
desarrollo de aplicaciones para Microsoft Windows, y es uno de los lenguajes de programación más populares en
el mundo.
Las herramientas de desarrollo más recientes de Microsoft forman parte de su estrategia a nivel corporativo
para integrar Internet y Web en las aplicaciones de computadora. Esta estrategia se implementa en la plataforma
.NET de Microsoft, la cual proporciona a los desarrolladores las herramientas que necesitan para crear y ejecu-
tar aplicaciones de computadora que puedan ejecutarse en computadoras distribuidas a través de Internet. Los
tres principales lenguajes de programación de Microsoft son Visual Basic .NET (basado en el lenguaje BASIC
original), Visual C++ .NET (basado en C++) y C# (basado en C++ y Java, y desarrollado expresamente para la
plataforma .NET). Los desarrolladores que utilizan .NET pueden escribir componentes de software en el lenguaje
con el que estén más familiarizados y formar aplicaciones al combinar esos componentes con los ya escritos en
cualquier lenguaje .NET.
1.13 Entorno de desarrollo típico en Java
Ahora explicaremos los pasos típicos usados para crear y ejecutar un programa en Java, utilizando un entorno de
desarrollo de Java (el cual se ilustra en la figura 1.1).
Por lo general, los programas en Java pasan a través de cinco fases: edición, compilación, carga, verificación y
ejecución. Hablamos sobre estos conceptos en el contexto del JDK 6.0 de Sun Microsystems, Inc. Puede descargar
el JDK más actualizado y su documentación en java.sun.com/javase/6/download.jsp. Siga cuidadosamente
las instrucciones de instalación para el JDK que se proporcionan en la sección Antes de empezar (o en java.sun.com/
javase/6/webnotes/install/index.htm) para asegurarse de configurar su computadora apropiadamente para
compilar y ejecutar programas en Java. También es conveniente que visite el centro para principiantes en Java (New
to Java Center) de Sun en:
java.sun.com/developer/onlineTraining/new2java/index.html
[Nota: este sitio Web proporciona las instrucciones de instalación para Windows, Linux y MacOS X. Si usted no
utiliza uno de estos sistemas operativos, consulte los manuales del entorno de Java de su sistema, o pregunte a su
Para ejecutar el programa, la
JVM lee los códigos de bytes y
los compila “justo a tiempo”
(JIT); es decir, los traduce en
un lenguaje que la
computadora pueda entender.
A medida que se ejecuta el
programa, existe la posibilidad
de que almacene los valores de
datos en la memoria principal
Disco
Editor
El programa se crea en un
editor y se almacena en disco,
en un archivo con la
terminación .java
Fase 1: edición
Disco
Compilador
El compilador crea los códigos
de bytes y los almacena en
disco, en un archivo con la
terminación .class
Fase 2: compilación
Disco
Cargador de clases
El cargador de clases lee los
archivos .class que
contienen códigos de bytes
del disco y coloca esos
códigos de bytes en la
memoria
Fase 3: carga
...
Verificador de código
de bytes
El verificador de código de
bytes confirma que todos
los códigos de bytes sean
válidos y no violen las
restricciones de seguridad
de Java
Fase 4: verificación
...
Máquina Virtual de
Java (JVM)
Memoria
principal
Memoria
principal
Memoria
principal
Fase 5: ejecución
...
Figura 1.1 | Entorno de desarrollo típico de Java.
1.13 Entorno de desarrollo típico en Java 11
12 Capítulo 1 Introducción a las computadoras, Internet y Web
instructor cómo puede lograr estas tareas con base en el sistema operativo de su computadora. Además, tenga en
cuenta que en ocasiones los vínculos Web se rompen a medida que las compañías evolucionan sus sitios Web. Si
encuentra un problema con este vínculo o con cualquier otro al que se haga referencia en este libro, visite www.
deitel.com para consultar la fe de erratas y notifíquenos su problema al correo electrónico deitel@deitel.
com. Le responderemos a la brevedad].
Fase 1: Creación de un programa
La fase 1 consiste en editar un archivo con un programa de edición (conocido comúnmente como editor). Usted
escribe un programa en Java (conocido, por lo general, como código fuente) utilizando el editor, realiza las correc-
ciones necesarias y guarda el programa en un dispositivo de almacenamiento secundario, como su disco duro. Un
nombre de archivo que termina con la extensión .java indica que éste contiene código fuente en Java. En este
libro asumimos que usted ya sabe cómo editar un archivo.
Dos de los editores que se utilizan ampliamente en sistemas Linux son vi y emacs. En Windows, basta con
usar un programa editor simple, como el Bloc de notas. También hay muchos editores de freeware y shareware
disponibles para descargarlos de Internet, en sitios como www.download.com.
Para las organizaciones que desarrollan sistemas de información extensos, hay entornos de desarrollo inte-
grados (IDEs) disponibles de la mayoría de los proveedores de software, incluyendo Sun Microsystems. Los
IDEs proporcionan herramientas que dan soporte al proceso de desarrollo del software, incluyendo editores para
escribir y editar programas, y depuradores para localizar errores lógicos.
Los IDEs populares son: Eclipse (www.eclipse.org), NetBeans (www.netbeans.org), JBuilder (www.
borland.com), JCreator (www.jcreator.com), BlueJ (www.blueJ.org), jGRASP (www.jgrasp.org) y JEdit
(www.jedit.org). Java Studio Enterprise de Sun Microsystems (developers.sun.com/prodtech/javatools/
jsenterprise/index.jsp) es una versión mejorada de NetBeans. [Nota: la mayoría de nuestros programas de
ejemplo deben operar de manera apropiada con cualquier entorno de desarrollo integrado de Java que cuente con
soporte para el JDK 6].
Fase 2: Compilación de un programa en Java para convertirlo en códigos de bytes
En la fase 2, el programador utiliza el comando javac (el compilador de Java) para compilar un programa. Por
ejemplo, para compilar un programa llamado Bienvenido.java, escriba
javac Bienvenido.java
en la ventana de comandos de su sistema (es decir, el indicador de MS-DOS en Windows 95/98/ME, el Símbolo
del sistema en Windows NT/2000/XP, el indicador de shell en Linux o la aplicación Terminal en Mac OS X).
Si el programa se compila, el compilador produce un archivo .class llamado Bienvenido.class, que contiene
la versión compilada del programa.
El compilador de Java traduce el código fuente en códigos de bytes que representan las tareas a ejecutar en
la fase de ejecución (fase 5). La Máquina Virtual de Java (JVM), una parte del JDK y la base de la plataforma
Java, ejecuta los códigos de bytes. Una máquina virtual (VM) es una aplicación de software que simula a una
computadora, pero oculta el sistema operativo y el hardware subyacentes de los programas que interactúan con
la VM. Si se implementa la misma VM en muchas plataformas computacionales, las aplicaciones que ejecute se
podrán utilizar en todas esas plataformas. La JVM es una de las máquinas virtuales más utilizadas.
A diferencia del lenguaje máquina, que depende del hardware de una computadora específica, los códigos de
bytes son instrucciones independientes de la plataforma; no dependen de una plataforma de hardware en espe-
cial. Entonces, los códigos de bytes de Java son portables (es decir, se pueden ejecutar en cualquier plataforma
que contenga una JVM que comprenda la versión de Java en la que se compilaron). La JVM se invoca mediante
el comando java. Por ejemplo, para ejecutar una aplicación llamada Bienvenido, debe escribir el comando
java Bienvenido
en una ventana de comandos para invocar la JVM, que a su vez inicia los pasos necesarios para ejecutar la aplica-
ción. Esto comienza la fase 3.
Fase 3: Cargar un programa en memoria
En la fase 3, el programa debe colocarse en memoria antes de ejecutarse; a esto se le conoce como cargar. El
cargador de clases toma los archivos .class que contienen los códigos de bytes del programa y los transfiere a
la memoria principal. El cargador de clases también carga cualquiera de los archivos .class que su programa
utilice, y que sean proporcionados por Java. Puede cargar los archivos .class desde un disco en su sistema o a
través de una red (como la de su universidad local o la red de la empresa, o incluso desde Internet).
Fase 4: Verificación del código de bytes
En la fase 4, a medida que se cargan las clases, el verificador de códigos de bytes examina sus códigos de bytes
para asegurar que sean válidos y que no violen las restricciones de seguridad. Java implementa una estrecha segu-
ridad para asegurar que los programas que llegan a través de la red no dañen sus archivos o su sistema (como
podrían hacerlo los virus de computadora y los gusanos).
Fase 5: Ejecución
En la fase 5, la JVM ejecuta los códigos de bytes del programa, realizando así las acciones especificadas por el
mismo. En las primeras versiones de Java, la JVM era tan sólo un intérprete de códigos de bytes de Java. Esto hacía
que la mayoría de los programas se ejecutaran con lentitud, ya que la JVM tenía que interpretar y ejecutar un
código de bytes a la vez. Por lo general, las JVMs actuales ejecutan códigos de bytes usando una combinación de
la interpretación y la denominada compilación justo a tiempo (JIT). En este proceso, la JVM analiza los códigos
de bytes a medida que se interpretan, buscando puntos activos: partes de los códigos de bytes que se ejecutan
con frecuencia. Para estas partes, un compilador justo a tiempo (JIT) (conocido como compilador HotSpot
de Java) traduce los códigos de bytes al lenguaje máquina correspondiente a la computadora. Cuando la JVM
encuentra estas partes compiladas nuevamente, se ejecuta el código en lenguaje máquina, que es más rápido. Por
ende, los programas en Java en realidad pasan por dos fases de compilación: una en la cual el código fuente se
traduce a código de bytes (para tener portabilidad a través de las JVMs en distintas plataformas computacionales)
y otra en la que, durante la ejecución, los códigos de bytes se traducen en lenguaje máquina para la computadora
actual en la que se ejecuta el programa.
Problemas que pueden ocurrir en tiempo de ejecución
Es probable que los programas no funcionen la primera vez. Cada una de las fases anteriores puede fallar, debido a
diversos errores que describiremos en este texto. Por ejemplo, un programa en ejecución podría intentar una divi-
sión entre cero (una operación ilegal para la aritmética con números enteros en Java). Esto haría que el programa
de Java imprimiera un mensaje de error. Si esto ocurre, tendría que regresar a la fase de edición, hacer las correc-
ciones necesarias y proseguir con las fases restantes nuevamente, para determinar que las correcciones resolvieron
el(los) problema(s). [Nota: la mayoría de los programas en Java reciben o producen datos. Cuando decimos que
un programa muestra un mensaje, por lo general, queremos decir que muestra ese mensaje en la pantalla de su
computadora. Los mensajes y otros datos pueden enviarse a otros dispositivos, como los discos y las impresoras,
o incluso a una red para transmitirlos a otras computadoras].
Error común de programación 1.1
Los errores, como la división entre cero, ocurren a medida que se ejecuta un programa, de manera que a estos
errores se les llama errores en tiempo de ejecución. Los errores fatales en tiempo de ejecución hacen que los
programas terminen de inmediato, sin haber realizado correctamente su trabajo. Los errores no fatales en tiempo
de ejecución permiten a los programas ejecutarse hasta terminar su trabajo, lo que a menudo produce resultados
incorrectos.
1.14 Generalidades acerca de Java y este libro
Java es un poderoso lenguaje de programación. En ocasiones, los programadores experimentados se enorgullecen
en poder crear un uso excéntrico, deformado e intrincado de un lenguaje. Ésta es una mala práctica de programa-
ción; ya que hace que: los programas sean más difíciles de leer, se comporten en forma extraña, sean más difíciles
de probar y depurar, y más difíciles de adaptarse a los cambiantes requerimientos. Este libro está enfocado en la
claridad. A continuación se muestra nuestro primer tip de Buena práctica de programación:
Buena práctica de programación 1.1
Escriba sus programas de Java en una manera simple y directa. A esto se le conoce algunas veces como KIS (Keep It
Simple, simplifíquelo). No “extiendas” el lenguaje experimentando con usos excéntricos.
1.14 Generalidades acerca de Java y este libro 13
14 Capítulo 1 Introducción a las computadoras, Internet y Web
Seguramente habrá escuchado que Java es un lenguaje portable y que los programas escritos en él pueden
ejecutarse en diversas computadoras. En general, la portabilidad es una meta elusiva.
Tip de portabilidad 1.2
Aunque es más fácil escribir programas portables en Java que en la mayoría de los demás lenguajes de programación,
las diferencias entre compiladores, JVMs y computadoras pueden hacer que la portabilidad sea difícil de lograr. No
basta con escribir programas en Java para garantizar su portabilidad.
Tip para prevenir errores 1.1
Para asegurarse de que sus programas de Java trabajen correctamente para las audiencias a las que están destinados,
pruébelos siempre en todos los sistemas en los que tenga pensado ejecutarlos.
Comparamos nuestra presentación con la documentación de Java de Sun, para verificar que sea completa
y precisa. Sin embargo, Java es un lenguaje extenso, y ningún libro puede cubrir todos los temas. En la página
java.sun.com/javase/6/docs/api/index.html existe una versión de la documentación de las APIs de Java;
también puede descargar esta documentación en su propia computadora, visitando java.sun.com/javase/6/
download.jsp. Para obtener detalles adicionales sobre muchos aspectos del desarrollo en Java, visite java.sun.
com/reference/docs/index.html.
Buena práctica de programación 1.2
Lea la documentación para la versión de Java que esté utilizando. Consulte esta documentación con frecuencia,
para asegurarse de conocer la vasta colección de herramientas disponibles en Java, y para asegurarse de que las está
utilizando correctamente.
Buena práctica de programación 1.3
Su computadora y su compilador son buenos maestros. Si, después de leer cuidadosamente el manual de documenta-
ción de Java, todavía no está seguro de cómo funciona alguna de sus características, experimente y vea lo que ocurre.
Analice cada error o mensaje de advertencia que obtenga al compilar sus programas (a éstos se les llama errores en
tiempo de compilación o errores de compilación), y corrija los programas para eliminar estos mensajes.
Observación de ingeniería de software 1.4
Algunos programadores gustan de leer el código fuente para las clases de la API de Java, para determinar cómo fun-
cionan las clases y aprender técnicas de programación adicionales.
1.15 Prueba de una aplicación en Java
En esta sección, ejecutará su primera aplicación en Java e interactuará con ella. Para empezar, ejecutará una aplica-
ción de ATM, la cual simula las transacciones que se llevan a cabo al utilizar una máquina de cajero automático,
o ATM (por ejemplo, retirar dinero, realizar depósitos y verificar los saldos de las cuentas). Aprenderá a crear esta
aplicación en el ejemplo práctico opcional orientado a objetos que se incluye en los capítulos 1-8 y 10. La figura
1.10 al final de esta sección sugiere otras aplicaciones interesantes que también puede probar después de terminar
con la prueba del ATM. Para los fines de esta sección supondremos que está utilizando Microsoft Windows.
En los siguientes pasos, ejecutará la aplicación y realizará diversas transacciones. Los elementos y la funcio-
nalidad que podemos ver en esta aplicación son típicos de lo que aprenderá a programar en este libro. [Nota:
utilizamos diversos tipos de letra para diferenciar las características que se ven en una pantalla (por ejemplo, el
Símbolo del sistema) y los elementos que no se relacionan directamente con una pantalla. Nuestra convención es
enfatizar las características de la pantalla como los títulos y menús (por ejemplo, el menú Archivo) en una fuente
Helvetica sans-serif en negritas, y enfatizar los elementos que no son de la pantalla, como los nombres de archivo
o los datos de entrada (como NombrePrograma.java) en una fuente Lucida sans-serif. Como tal vez ya se
haya dado cuenta, cuando se ofrece la definición de algún término ésta aparece en negritas. En las figuras en esta
sección, resaltamos en gris la entrada del usuario requerida por cada paso, y señalamos las partes importantes de la
aplicación con líneas y texto. Para aumentar la visibilidad de estas características, modificamos el color de fondo
de las ventanas del Símbolo del sistema].
1. Revise su configuración. Lea la sección Antes de empezar para verificar si instaló correctamente Java, y
observe si copió los ejemplos del libro en su disco duro.
2 Localice la aplicación completa. Abra una ventana Símbolo del sistema. Para ello, puede seleccionar
Inicio | Todos los programas | Accesorios | Símbolo del sistema. Cambie al directorio de la aplicación
del ATM escribiendo cd C:ejemplosATM, y después oprima Intro (figura 1.2). El comando cd se
utiliza para cambiar de directorio.
3. Ejecute la aplicación del ATM. Escriba el comando java EjemploPracticoATM (figura 1.3) oprima
Intro. Recuerde que el comando java, seguido del nombre del archivo .class (en este caso, Ejemplo-
PracticoATM), ejecuta la aplicación. Si especificamos la extensión .class al usar el comando java se
produce un error. [Nota: los comandos en Java son sensibles a mayúsculas/minúsculas. Es importante
escribir el nombre de esta aplicación con las letras A, T y M mayúsculas en “ATM”, una letra E mayúscu-
la en “Ejemplo” y una letra P mayúscula en “Practico”. De no ser así, la aplicación no se ejecutará]. Si
recibe el mensaje de error "Exception in thread "main" java.lang.NoClassDefFoundError:
EjemploPracticoATM", entonces su sistema tiene un problema con CLASSPATH. Consulte la sección
Antes de empezar para obtener instrucciones acerca de cómo corregir este problema.
4. Escriba un número de cuenta. Cuando la aplicación se ejecuta por primera vez, muestra el mensaje
"Bienvenido!" y le pide un número de cuenta. Escriba 12345 en el indicador "Escriba su numero
de cuenta:" (figura 1.4) y oprima Intro.
Use el comando cd para
cambiar de directorio
Ubicación de los archivos de la aplicación del ATM
Figura 1.2 | Abrir una ventana Símbolo del sistema en Windows XP y cambiar de directorio.
Figura 1.3 | Uso del comando java para ejecutar la aplicación del ATM.
Figura 1.4 | La aplicación pide al usuario un número de cuenta.
1.15 Prueba de una aplicación en Java 15
Mensaje de bienvenida del ATM Indicador que pide el número de cuenta
16 Capítulo 1 Introducción a las computadoras, Internet y Web
5. Escriba un NIP. Una vez que introduzca un número de cuenta válido, la aplicación mostrará el indica-
dor "Escriba su NIP:". Escriba "54321" como su NIP (Número de Identificación Personal) válido y
oprima Intro. A continuación aparecerá el menú principal del ATM, que contiene una lista de opciones
(figura 1.5).
6. Revise el saldo de la cuenta. Seleccione la opción 1, "Ver mi saldo" del menú del ATM (figura 1.6).
A continuación la aplicación mostrará dos números: el Saldo disponible ($1,000.00) y el Saldo
total ($1,200.00). El saldo disponible es la cantidad máxima de dinero en su cuenta, disponible para
retirarla en un momento dado. En algunos casos, ciertos fondos como los depósitos recientes, no están
disponibles de inmediato para que el usuario pueda retirarlos, por lo que el saldo disponible puede ser
menor que el saldo total, como en este caso. Después de mostrar la información de los saldos de la cuen-
ta, se muestra nuevamente el menú principal de la aplicación.
7. Retire dinero de la cuenta. Seleccione la opción 2, "Retirar efectivo", del menú de la aplicación.
A continuación aparecerá (figura 1.7) una lista de montos en dólares (por ejemplo: 20, 40, 60, 100
y 200). También tendrá la oportunidad de cancelar la transacción y regresar al menú principal. Retire
$100 seleccionando la opción 4. La aplicación mostrará el mensaje "Tome su efectivo ahora" y
regresará al menú principal. [Nota: por desgracia, esta aplicación sólo simula el comportamiento de un
verdadero ATM, por lo cual no dispensa efectivo en realidad].
Figura 1.6 | La aplicación del ATM muestra la información del saldo de la cuenta del usuario.
Figura 1.5 | El usuario escribe un número NIP válido y aparece el menú principal de la aplicación del ATM.
Escriba un NIP válido Menú principal del ATM
Información del saldo de la cuenta
8. Confirme que la información de la cuenta se haya actualizado. En el menú principal, seleccione la
opción 1 nuevamente para ver el saldo actual de su cuenta (figura 1.8). Observe que tanto el saldo dis-
ponible como el saldo total se han actualizado para reflejar su transacción de retiro.
9. Finalice la transacción. Para finalizar su sesión actual en el ATM, seleccione, del menú principal, la
opción 4, "Salir" (figura 1.9). El ATM saldrá del sistema y mostrará un mensaje de despedida al usua-
rio. A continuación, la aplicación regresará a su indicador original, pidiendo el número de cuenta del
siguiente usuario.
10. Salga de la aplicación del ATM y cierre la ventana Símbolo del sistema. La mayoría de las aplicacio-
nes cuentan con una opción para salir y regresar al directorio del Símbolo del sistema desde el cual se
ejecutó la aplicación. Un ATM real no proporciona al usuario la opción de apagar la máquina ATM. En
vez de ello, cuando el usuario ha completado todas las transacciones deseadas y elige la opción del menú
para salir, el ATM se reinicia a sí mismo y muestra un indicador para el número de cuenta del siguiente
Figura 1.7 | Se retira el dinero de la cuenta y la aplicación regresa al menú principal.
Figura 1.8 | Verificación del nuevo saldo.
1.15 Prueba de una aplicación en Java 17
Menú de retiro del ATM
Confirmación de la información del saldo de la cuenta
actualizado después de la transacción de retiro
18 Capítulo 1 Introducción a las computadoras, Internet y Web
usuario. Como se muestra en la figura 1.9, la aplicación del ATM se comporta de manera similar. Al
elegir la opción del menú para salir sólo se termina la sesión del usuario actual con el ATM, no toda la
aplicación completa. Para salir realmente de la aplicación del ATM, haga clic en el botón Cerrar (x) en
la esquina superior derecha de la ventana Símbolo del sistema. Al cerrar la ventana, la aplicación ter-
mina su ejecución.
Aplicaciones adicionales incluidas en Cómo programar en Java, 7ª edición
La figura 1.10 lista unas cuantas de los cientos de aplicaciones que se incluyen en los ejemplos y ejercicios del
libro. Estos programas presentan muchas de las poderosas y divertidas características de Java. Ejecute estos progra-
mas para que conozca más acerca de las aplicaciones que aprenderá a construir en este libro de texto. La carpeta
de ejemplos para este capítulo contiene todos los archivos requeridos para ejecutar cada aplicación. Sólo escriba
los comandos que se listan en la figura 1.10 en una ventana de Símbolo del sistema.
Figura 1.9 | Finalización de una sesión de transacciones con el ATM.
Nombre de la aplicación Capítulo(s) en donde se ubica Comandos a ejecutar
Tic-Tac-Toe Capítulos 8 y 24 cd C:ejemploscap01Tic-Tac-Toe
Java PruebaTicTacToe
Juego de adivinanza Capítulo 11 cd C:ejemploscap01JuegoAdivinanza
Java JuegoAdivinanza
Animador de logotipos Capítulo 21 cd C:ejemploscap01AnimadorLogotipos
Java AnimadorLogotipos
Pelota rebotadora Capítulo 23 cd C:ejemploscap01PelotaRebotadora
Java PelotaRebotadora
Figura 1.10 | Ejemplos de aplicaciones de Java adicionales, incluidas en Cómo programar en Java, 7ª edición.
Mensaje de despedida del ATM
Indicador del número de cuenta
para el siguiente usuario
1.16 Ejemplo práctico de Ingeniería de Software: introducción a la
tecnología de objetos y UML
Ahora empezaremos nuestra primera introducción al tema de la orientación a objetos, una manera natural de
pensar acerca del mundo real y de escribir programas de cómputo. Los capítulos 1-8 y 10 terminan con una
sección breve titulada Ejemplo práctico de Ingeniería de Software, en la cual presentamos una introducción cui-
dadosamente guiada al tema de la orientación a objetos. Nuestro objetivo aquí es ayudarle a desarrollar una forma
de pensar orientada a objetos, y de presentarle el Lenguaje Unificado de Modelado™ (UML™), un lenguaje
gráfico que permite a las personas que diseñan sistemas de software utilizar una notación estándar en la industria
para representarlos.
En esta única sección requerida (1.16), presentamos los conceptos y la terminología de la orientación a
objetos. Las secciones opcionales en los capítulos 2-8 y 10 presentan el diseño y la implementación orientados
a objetos de un software para una máquina de cajero automático (ATM) simple. Las secciones tituladas Ejemplo
práctico de Ingeniería de Software al final de los capítulos 2 al 7:
analizan un documento de requerimientos típico que describe un sistema de software (el ATM) que
construirá
determinan los objetos requeridos para implementar ese sistema
establecen los atributos que deben tener estos objetos
fijan los comportamientos que exhibirán estos objetos
especifican la forma en que los objetos deben interactuar entre sí para cumplir con los requerimientos
del sistema
Las secciones tituladas Ejemplo práctico de Ingeniería de Software al final de los capítulos 8 y 10 modifican
y mejoran el diseño presentado en los capítulos 2 al 7. El apéndice M contiene una implementación completa y
funcional en Java del sistema ATM orientado a objetos.
Usted experimentará una concisa pero sólida introducción al diseño orientado a objetos con UML. Además,
afinará sus habilidades para leer código al ver paso a paso la implementación del ATM en Java, cuidadosamente
escrita y bien documentada, en el apéndice M.
Conceptos básicos de la tecnología de objetos
Comenzaremos nuestra introducción al tema de la orientación a objetos con cierta terminología clave. En cual-
quier parte del mundo real puede ver objetos: gente, animales, plantas, automóviles, aviones, edificios, compu-
tadoras, etcétera. Los humanos pensamos en términos de objetos. Los teléfonos, casas, semáforos, hornos de
microondas y enfriadores de agua son sólo unos cuantos objetos más. Los programas de cómputo, como los
programas de Java que leerá en este libro y los que usted mismo escriba, están compuestos por muchos objetos de
software con capacidad de interacción.
En ocasiones dividimos a los objetos en dos categorías: animados e inanimados. Los objetos animados están
“vivos” en cierto sentido; se mueven a su alrededor y hacen cosas. Por otro lado, los objetos inanimados no se
mueven por su propia cuenta. Sin embargo, los objetos de ambos tipos tienen ciertas cosas en común. Todos ellos
tienen atributos (como tamaño, forma, color y peso), y todos exhiben comportamientos (por ejemplo, una
pelota rueda, rebota, se infla y desinfla; un bebé llora, duerme, gatea, camina y parpadea; un automóvil acelera,
frena y da vuelta; una toalla absorbe agua). Estudiaremos los tipos de atributos y comportamientos que tienen los
objetos de software.
Los humanos aprenden acerca de los objetos existentes estudiando sus atributos y observando sus compor-
tamientos. Distintos objetos pueden tener atributos similares y pueden exhibir comportamientos similares. Por
ejemplo, pueden hacerse comparaciones entre los bebés y los adultos, y entre los humanos y los chimpancés.
El diseño orientado a objetos (DOO) modela el software en términos similares a los que utilizan las per-
sonas para describir objetos del mundo real. Este diseño aprovecha las relaciones entre las clases, en donde los
objetos de cierta clase (como una clase de vehículos) tienen las mismas características; los automóviles, camiones,
pequeños vagones rojos y patines tienen mucho en común. El DOO también aprovecha las relaciones de heren-
cia, en donde las nuevas clases de objetos se derivan absorbiendo las características de las clases existentes y agre-
gando sus propias características únicas. Un objeto de la clase “convertible” ciertamente tiene las características
de la clase más general “automóvil” pero, de manera más específica, el techo de un convertible puede ponerse y
quitarse.
•
•
•
•
•
1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML 19
20 Capítulo 1 Introducción a las computadoras, Internet y Web
El diseño orientado a objetos proporciona una manera natural e intuitiva de ver el proceso de diseño de soft-
ware: a saber, modelando los objetos por sus atributos y comportamientos, de igual forma que como describimos
los objetos del mundo real. El DOO también modela la comunicación entre los objetos. Así como las personas
se envían mensajes unas a otras (por ejemplo, un sargento ordenando a un soldado que permanezca firme), los
objetos también se comunican mediante mensajes. Un objeto cuenta de banco puede recibir un mensaje para
reducir su saldo por cierta cantidad, debido a que el cliente ha retirado esa cantidad de dinero.
El DOO encapsula (es decir, envuelve) los atributos y las operaciones (comportamientos) en los objetos;
los atributos y las operaciones de un objeto se enlazan íntimamente entre sí. Los objetos tienen la propiedad de
ocultamiento de información. Esto significa que los objetos pueden saber cómo comunicarse entre sí a través
de interfaces bien definidas, pero por lo general no se les permite saber cómo se implementan otros objetos;
los detalles de la implementación se ocultan dentro de los mismos objetos. Por ejemplo, podemos conducir un
automóvil con efectividad, sin necesidad de saber los detalles acerca de cómo funcionan internamente los moto-
res, las transmisiones y los sistemas de escape; siempre y cuando sepamos cómo usar el pedal del acelerador, el
pedal del freno, el volante, etcétera. Más adelante veremos por qué el ocultamiento de información es tan impres-
cindible para la buena ingeniería de software.
Los lenguajes como Java son orientados a objetos. La programación en dichos lenguajes se llama progra-
mación orientada a objetos (POO), y permite a los programadores de computadoras implementar un diseño
orientado a objetos como un sistema funcional. Por otra parte, los lenguajes como C son por procedimientos, de
manera que la programación tiende a ser orientada a la acción. En C, la unidad de programación es la función.
Los grupos de acciones que realizan cierta tarea común se forman en funciones, y las funciones se agrupan para
formar programas. En Java, la unidad de programación es la clase a partir de la cual se instancian (crean) los obje-
tos en un momento dado. Las clases en Java contienen métodos (que implementan operaciones y son similares a
las funciones en C) y campos (que implementan atributos).
Los programadores de Java se concentran en crear clases. Cada clase contiene campos, además del conjunto
de métodos que manipulan esos campos y proporcionan servicios a clientes (es decir, otras clases que utilizan esa
clase). El programador utiliza las clases existentes como bloques de construcción para crear nuevas clases.
Las clases son para los objetos lo que los planos de construcción, para las casas. Así como podemos construir
muchas casas a partir de un plano, podemos instanciar (crear) muchos objetos a partir de una clase. No puede
cocinar alimentos en la cocina de un plano de construcción; puede cocinarlos en la cocina de una casa.
Las clases pueden tener relaciones con otras clases. Por ejemplo, en un diseño orientado a objetos de un
banco, la clase “cajero” necesita relacionarse con las clases “cliente”, “cajón de efectivo”, “bóveda”, etcétera. A estas
relaciones se les llama asociaciones.
Al empaquetar el software en forma de clases, los sistemas de software posteriores pueden reutilizar esas
clases. Los grupos de clases relacionadas se empaquetan comúnmente como componentes reutilizables. Así como
los corredores de bienes raíces dicen a menudo que los tres factores más importantes que afectan el precio de los
bienes raíces son “la ubicación, la ubicación y la ubicación”, las personas en la comunidad de software dicen a
menudo que los tres factores más importantes que afectan el futuro del desarrollo de software son “la reutiliza-
ción, la reutilización y la reutilización”. Reutilizar las clases existentes cuando se crean nuevas clases y programas
es un proceso que ahorra tiempo y esfuerzo; también ayuda a los programadores a crear sistemas más confiables
y efectivos, ya que las clases y componentes existentes a menudo han pasado por un proceso extenso de prueba,
depuración y optimización del rendimiento.
Evidentemente, con la tecnología de objetos podemos crear la mayoría del software que necesitaremos median-
te la combinación de clases, así como los fabricantes de automóviles combinan las piezas intercambiables. Cada
nueva clase que usted cree tendrá el potencial de convertirse en una valiosa pieza de software, que usted y otros
programadores podrán usar para agilizar y mejorar la calidad de los futuros esfuerzos de desarrollo de software.
Introducción al análisis y diseño orientados a objetos (A/DOO)
Pronto estará escribiendo programas en Java. ¿Cómo creará el código para sus programas? Tal vez, como muchos
programadores principiantes, simplemente encenderá su computadora y empezará a teclear. Esta metodología
puede funcionar para programas pequeños (como los que presentamos en los primeros capítulos del libro) pero
¿qué haría usted si se le pidiera crear un sistema de software para controlar miles de máquinas de cajero automá-
tico para un importante banco? O suponga que le pidieron trabajar en un equipo de 1,000 desarrolladores de
software para construir el nuevo sistema de control de tráfico aéreo de Estados Unidos. Para proyectos tan grandes
y complejos, no podría simplemente sentarse y empezar a escribir programas.
Para crear las mejores soluciones, debe seguir un proceso detallado para analizar los requerimientos de su
proyecto (es decir, determinar qué es lo que se supone debe hacer el sistema) y desarrollar un diseño que cumpla
con esos requerimientos (es decir, decidir cómo debe hacerlo el sistema). Idealmente usted pasaría por este proceso
y revisaría cuidadosamente el diseño (o haría que otros profesionales de software lo revisaran) antes de escribir
cualquier código. Si este proceso implica analizar y diseñar su sistema desde un punto de vista orientado a objetos,
lo llamamos un proceso de análisis y diseño orientado a objetos (A/DOO). Los programadores experimentados
saben que el análisis y el diseño pueden ahorrar innumerables horas, ya que les ayudan a evitar un método de de-
sarrollo de un sistema mal planeado, que tiene que abandonarse en plena implementación, con la posibilidad de
desperdiciar una cantidad considerable de tiempo, dinero y esfuerzo.
A/DOO es el término genérico para el proceso de analizar un problema y desarrollar un método para resol-
verlo. Los pequeños problemas como los que se describen en los primeros capítulos de este libro no requieren de
un proceso exhaustivo de A/DOO. Podría ser suficiente con escribir pseudocódigo antes de empezar a escribir el
código en Java; el pseudocódigo es un medio informal para expresar la lógica de un programa. En realidad no es
un lenguaje de programación, pero podemos usarlo como un tipo de bosquejo para guiarnos mientras escribimos
nuestro código. En el capítulo 4 presentamos el pseudocódigo.
A medida que los problemas y los grupos de personas que los resuelven aumentan en tamaño, los métodos
de A/DOO se vuelven más apropiados que el pseudocódigo. Idealmente, un grupo debería acordar un proce-
so estrictamente definido para resolver su problema, y establecer también una manera uniforme para que los
miembros del grupo se comuniquen los resultados de ese proceso entre sí. Aunque existen diversos procesos de
A/DOO, hay un lenguaje gráfico para comunicar los resultados de cualquier proceso A/DOO que se ha vuelto
muy popular. Este lenguaje, conocido como Lenguaje Unificado de Modelado (UML), se desarrolló a mediados
de la década de los noventa, bajo la dirección inicial de tres metodologistas de software: Grady Booch, James
Rumbaugh e Ivar Jacobson.
Historia de UML
En la década de los ochenta, un creciente número de empresas comenzó a utilizar la POO para crear sus aplica-
ciones, lo cual generó la necesidad de un proceso estándar de A/DOO. Muchos metodologistas (incluyendo a
Booch, Rumbaugh y Jacobson) produjeron y promocionaron, por su cuenta, procesos separados para satisfacer
esta necesidad. Cada uno de estos procesos tenía su propia notación, o “lenguaje” (en forma de diagramas gráfi-
cos), para transmitir los resultados del análisis y el diseño.
A principios de la década de los noventa, diversas compañías (e inclusive diferentes divisiones dentro de la
misma compañía) utilizaban sus propios procesos y notaciones únicos. Al mismo tiempo, estas compañías que-
rían utilizar herramientas de software que tuvieran soporte para sus procesos particulares. Con tantos procesos,
se les dificultó a los distribuidores de software proporcionar dichas herramientas. Evidentemente era necesario
contar con una notación y procesos estándar.
En 1994, James Rumbaugh se unió con Grady Booch en Rational Software Corporation (ahora una división
de IBM), y comenzaron a trabajar para unificar sus populares procesos. Pronto se unió a ellos Ivar Jacobson. En
1996, el grupo liberó las primeras versiones de UML para la comunidad de ingeniería de software, solicitan-
do retroalimentación. Casi al mismo tiempo, una organización conocida como Object Management Group™
(OMG™, Grupo de administración de objetos) hizo una invitación para participar en la creación de un lenguaje
común de modelado. El OMG (www.omg.org) es una organización sin fines de lucro que promueve la estanda-
rización de las tecnologías orientadas a objetos, emitiendo lineamientos y especificaciones como UML. Varias
empresas (entre ellas HP, IBM, Microsoft, Oracle y Rational Software) habían reconocido ya la necesidad de
un lenguaje común de modelado. Estas compañías formaron el consorcio UML Partners (Socios de UML) en
respuesta a la solicitud de proposiciones por parte del OMG (el consorcio que desarrolló la versión 1.1 de UML y
la envió al OMG). La propuesta fue aceptada y, en 1997, el OMG asumió la responsabilidad del mantenimiento
y revisión de UML en forma continua. La versión 2 que está ahora disponible marca la primera modificación
importante al UML desde el estándar de la versión 1.1 de 1997. A lo largo de este libro, presentaremos la termi-
nología y notación de UML 2.
¿Qué es UML?
UML es ahora el esquema de representación gráfica más utilizado para modelar sistemas orientados a objetos. Evi-
dentemente ha unificado los diversos esquemas de notación populares. Aquellos quienes diseñan sistemas utilizan
el lenguaje (en forma de diagramas) para modelar sus sistemas.
1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML 21
22 Capítulo 1 Introducción a las computadoras, Internet y Web
Una característica atractiva es su flexibilidad. UML es extensible (es decir, capaz de mejorarse con nuevas
características) e independiente de cualquier proceso de A/DOO específico. Los modeladores de UML tienen la
libertad de diseñar sistemas utilizando varios procesos, pero todos los desarrolladores pueden ahora expresar esos
diseños con un conjunto de notaciones gráficas estándar.
UML es un lenguaje gráfico complejo, con muchas características. En nuestras secciones del Ejemplo prácti-
co de Ingeniería de Software, presentamos un subconjunto conciso y simplificado de estas características. Luego
utilizamos este subconjunto para guiarlo a través de la experiencia de su primer diseño con UML, la cual está
dirigida a los programadores principiantes orientados a objetos en cursos de programación de primer o segundo
semestre.
Recursos Web de UML
Para obtener más información acerca de UML, consulte los siguientes sitios Web:
www.uml.org
Esta página de recursos de UML del Grupo de Administración de Objetos (OMG) proporciona documentos de la
especificación para UML y otras tecnologías orientadas a objetos.
www.ibm.com/software/rational/uml
Ésta es la página de recursos de UML para IBM Rational, sucesor de Rational Software Corporation (la compañía que
creó a UML).
en.wikipedia.org/wiki/UML
La definición de Wikipedia de UML. Este sitio también ofrece vínculos a muchos recursos adicionales de UML.
es.wikipedia.org/wiki/UML
La definición de Wikipedia del UML en español.
Lecturas recomendadas
Los siguientes libros proporcionan información acerca del diseño orientado a objetos con UML:
Ambler, S. The Object Primer: Agile Model-Driven Development with UML 2.0, Third Edition. Nueva York: Cambridge
University Press, 2005.
Arlow, J. e I. Neustadt. UML and the Unified Process: Practical Object-Oriented Analysis and Design, Second Edition.
Boston: Addison-Wesley Professional, 2006.
Fowler, M. UML Distilled, Third Edition: A Brief Guide to the Standard Object Modeling Language. Boston: Addison-
Wesley Professional, 2004.
Rumbaugh, J., I. Jacobson y G. Booch. The Unified Modeling Language User Guide, Second Edition. Boston: Addison-
Wesley Professional, 2006.
Ejercicios de autorrepaso de la sección 1.16
1.1 Liste tres ejemplos de objetos reales que no mencionamos. Para cada objeto, liste varios atributos y compor-
tamientos.
1.2 El pesudocódigo es __________.
a) otro término para el A/DOO
b) un lenguaje de programación utilizado para visualizar diagramas de UML
c) un medio informal para expresar la lógica de un programa
d) un esquema de representación gráfica para modelar sistemas orientados a objetos
1.3 El UML se utiliza principalmente para __________.
a) probar sistemas orientados a objetos
b) diseñar sistemas orientados a objetos
c) implementar sistemas orientados a objetos
d) a y b
Respuestas a los ejercicios de autorrepaso de la sección 1.16
1.1 [Nota: las respuestas pueden variar]. a) Los atributos de una televisión incluyen el tamaño de la pantalla, el
número de colores que puede mostrar, su canal actual y su volumen actual. Una televisión se enciende y se apaga, cam-
bia de canales, muestra video y reproduce sonidos. b) Los atributos de una cafetera incluyen el volumen máximo de
agua que puede contener, el tiempo requerido para preparar una jarra de café y la temperatura del plato calentador bajo
la jarra de café. Una cafetera se enciende y se apaga, prepara café y lo calienta. c) Los atributos de una tortuga incluyen
su edad, el tamaño de su caparazón y su peso. Una tortuga camina, se mete en su caparazón, emerge del mismo y come
vegetación.
1.2 c.
1.3 b.
1.17 Web 2.0
Literalmente, la Web explotó a mediados de la década de los noventa, pero surgieron tiempos difíciles a principios
del año 2000, debido al desplome económico de punto com. Al resurgimiento que empezó aproximadamente en el
2004, se le conoce como Web 2.0. La primera Conferencia sobre Web 2.0 se realizó en el 2004. Un año después,
el término “Web 2.0” obtuvo aproximadamente 10 millones de coincidencias en el motor de búsqueda Google,
para crecer hasta 60 millones al año siguiente. A Google se le considera en muchas partes como la compañía carac-
terística de Web 2.0. Algunas otras son Craigslist (listados gratuitos de anuncios clasificados), Flickr (sitio para
compartir fotos), del.icio.us (sitios favoritos de carácter social), YouTube (sitio para compartir videos), MySpace y
FaceBook (redes sociales), Salesforce (software de negocios que se ofrece como servicio en línea), Second Life (un
mundo virtual), Skype (telefonía por Internet) y Wikipedia (una enciclopedia en línea gratuita).
En Deitel & Associates, inauguramos nuestra Iniciativa de Negocios por Internet basada en Web 2.0 en el
año 2005. Estamos investigando las tecnologías clave de Web 2.0 y las utilizamos para crear negocios en Internet.
Compartimos nuestra investigación en forma de Centros de recursos en www.deitel.com/resourcecenters.
html. Cada semana anunciamos los Centros de recursos más recientes en nuestro boletín de correo electrónico
Deitel® Buzz Online (www.deitel.com/newsletter/subscribe.html). Cada uno de estos centros lista muchos
vínculos a contenido y software gratuito en Internet.
En este libro incluimos un tratamiento detallado sobre los servicios Web (capítulo 28) y presentamos la nue-
va metodología de desarrollo de aplicaciones, conocida como mashups (apéndice H), en la que puede desarrollar
rápidamente aplicaciones poderosas e intrigantes, al combinar servicios Web complementarios y otras fuentes de
información provenientes de dos o más organizaciones. Un mashup popular es www.housingmaps.com, el cual
combina los listados de bienes raíces de www.craigslist.org con las capacidades de los mapas de Google Maps
para mostrar las ubicaciones de los apartamentos para renta en un área dada.
Ajax es una de las tecnologías más importantes de Web 2.0. Aunque el uso del término explotó en el 2005,
es sólo un término que nombra a un grupo de tecnologías y técnicas de programación que han estado en uso
desde finales de la década de los noventa. Ajax ayuda a las aplicaciones basadas en Internet a funcionar como las
aplicaciones de escritorio; una tarea difícil, dado que dichas aplicaciones sufren de retrasos en la transmisión, a
medida que los datos se intercambian entre su computadora y las demás computadoras en Internet. Mediante el
uso de Ajax, las aplicaciones como Google Maps han logrado un desempeño excelente, además de la apariencia
visual de las aplicaciones de escritorio. Aunque no hablaremos sobre la programación “pura” con Ajax en este libro
(que es bastante compleja), en el capítulo 27 mostraremos cómo crear aplicaciones habilitadas para Ajax mediante
el uso de los componentes de JavaServer Faces (JSF) habilitados para Ajax.
Los blogs son sitios Web (actualmente hay como 60 millones de ellos) similares a un diario en línea, en
donde las entradas más recientes aparecen primero. Los “bloggers” publican rápidamente sus opiniones acerca
de las noticias, lanzamientos de productos, candidatos políticos, temas controversiales, y de casi todo lo demás.
A la colección de todos los blogs y de la comunidad de “blogging” se le conoce como blogósfera, y cada vez está
teniendo más influencia. Technorati es el líder en motores de búsqueda de blogs.
Las fuentes RSS permiten a los sitios enviar información a sus suscriptores. Un uso común de las fuentes
RSS es enviar las publicaciones más recientes de los blogs, a las personas que se suscriben a éstos. Los flujos de
información RSS en Internet están creciendo de manera exponencial.
Web 3.0 es otro nombre para la siguiente generación de la Web, que también se le conoce como Web
Semántica. Casi todo el contenido de Web 1.0 estaba basado en HTML. Web 2.0 está utilizando cada vez más el
XML, en especial en tecnologías como las fuentes RSS. Web 3.0 utilizará aún más el XML, creando una “Web de
significado”. Si usted es un estudiante que busca un excelente artículo de presentación o un tema para una tesis,
o si es un emprendedor que busca oportunidades de negocios, dé un vistazo a nuestro Centro de recursos sobre
Web 3.0.
1.17 Web 2.0 23
24 Capítulo 1 Introducción a las computadoras, Internet y Web
Para seguir los últimos desarrollos en Web 2.0, visite www.techcrunch.com y www.slashdot.org, y revise la
lista creciente de Centros de recursos relacionados con Web 2.0 en www.deitel.com/resourcecenters.html.
1.18 Tecnologías de software
En esta sección hablaremos sobre varias “palabras de moda” que escuchará en la comunidad de desarrollo de soft-
ware. Creamos Centros de recursos sobre la mayoría de estos temas, y hay muchos por venir.
Agile Software Development (Desarrollo Ágil de Software) es un conjunto de metodologías que tratan de
implementar software rápidamente, con menos recursos que las metodologías anteriores. Visite los sitios de Agile
Alliance (www.agilealliance.org) y Agile Manifesto (www.agilemanifesto.org). También puede visitar el
sitio en español www.agile-spain.com.
Extreme programming (XP) (Programación extrema (PX)) es una de las diversas tecnologías de desarrollo
ágil. Trata de desarrollar software con rapidez. El software se libera con frecuencia en pequeños incrementos, para
alentar la rápida retroalimentación de los usuarios. PX reconoce que los requerimientos de los usuarios cambian
a menudo, y que el software debe cumplir con esos requerimientos rápidamente. Los programadores trabajan
en pares en una máquina, de manera que la revisión del código se realiza de inmediato, a medida que se crea el
código. Todos en el equipo deben poder trabajar con cualquier parte del código.
Refactoring (Refabricación) implica la reformulación del código para hacerlo más claro y fácil de mantener,
al tiempo que se preserva su funcionalidad. Se emplea ampliamente con las metodologías de desarrollo ágil. Hay
muchas herramientas de refabricación disponibles para realizar las porciones principales de la reformulación de
manera automática.
Los patrones de diseño son arquitecturas probadas para construir software orientado a objetos flexible y que
pueda mantenerse (vea el apéndice P Web adicional). El campo de los patrones de diseño trata de enumerar a los
patrones recurrentes, y de alentar a los diseñadores de software para que los reutilicen y puedan desarrollar un
software de mejor calidad con menos tiempo, dinero y esfuerzo.
Programación de juegos. El negocio de los juegos de computadora es más grande que el negocio de las
películas de estreno. Ahora hay cursos universitarios, e incluso maestrías, dedicados a las técnicas sofisticadas de
software que se utilizan en la programación de juegos. Vea nuestros Centros de recursos sobre Programación
de juegos y Proyectos de programación.
El software de código fuente abierto es un estilo de desarrollo de software que contrasta con el desarrollo
propietario, que dominó los primeros años del software. Con el desarrollo de código fuente abierto, individuos
y compañías contribuyen sus esfuerzos en el desarrollo, mantenimiento y evolución del software, a cambio del
derecho de usar ese software para sus propios fines, comúnmente sin costo. Por lo general, el código fuente abierto
se examina a detalle por una audiencia más grande que el software propietario, por lo cual los errores se eliminan
con más rapidez. El código fuente abierto también promueve más innovación. Sun anunció recientemente que
piensa abrir el código fuente de Java. Algunas de las organizaciones de las que se habla mucho en la comunidad
de código fuente abierto son Eclipse Foundation (el IDE de Eclipse es popular para el desarrollo de software en
Java), Mozilla Foundation (creadores del explorador Web Firefox), Apache Software Foundation (creadores del
servidor Web Apache) y SourceForge (que proporciona las herramientas para administrar proyectos de código
fuente abierto y en la actualidad cuenta con más de 100,000 proyectos en desarrollo).
Linux es un sistema operativo de código fuente abierto, y uno de los más grandes éxitos de la iniciativa de
código fuente abierto. MySQL es un sistema de administración de bases de datos con código fuente abierto. PHP
es el lenguaje de secuencias de comandos del lado servidor para Internet de código fuente abierto más popular,
para el desarrollo de aplicaciones basadas en Internet. LAMP es un acrónimo para el conjunto de tecnologías
de código fuente abierto que utilizaron muchos desarrolladores para crear aplicaciones Web: representa a Linux,
Apache, MySQL y PHP (o Perl, o Python; otros dos lenguajes que se utilizan para propósitos similares).
Ruby on Rails combina el lenguaje de secuencias de comandos Ruby con el marco de trabajo para aplicacio-
nes Web Rails, desarrollado por la compañía 37Signals. Su libro, Getting Real, es una lectura obligatoria para los
desarrolladores de aplicaciones Web de la actualidad; puede leerlo sin costo en gettingreal.37signals.com/
toc.php. Muchos desarrolladores de Ruby on Rails han reportado un considerable aumento en la productividad,
en comparación con otros lenguajes al desarrollar aplicaciones Web con uso intensivo de bases de datos.
Por lo general, el software siempre se ha visto como un producto; la mayoría del software aún se ofrece de esta
manera. Si desea ejecutar una aplicación, compra un paquete de software de un distribuidor. Después instala ese
software en su computadora y lo ejecuta según sea necesario. Al aparecer nuevas versiones del software, usted lo
actualiza, a menudo con un costo considerable. Este proceso puede volverse incómodo para empresas con decenas
de miles de sistemas que deben mantenerse en una extensa colección de equipo de cómputo. Con Software as a
Service (SAAS), el software se ejecuta en servidores ubicados en cualquier parte de Internet. Cuando se actualiza
ese servidor, todos los clientes a nivel mundial ven las nuevas características; no se necesita instalación local. Usted
accede al servidor a través de un explorador Web; éstos son bastante portables, por lo que puede ejecutar las mis-
mas aplicaciones en distintos tipos de computadoras, desde cualquier parte del mundo. Salesforce.com, Google,
Microsoft Office Live y Windows Live ofrecen SAAS.
1.19 Conclusión
Este capítulo presentó los conceptos básicos de hardware y software, y los conceptos de la tecnología básica
de objetos, incluyendo clases, objetos, atributos, comportamientos, encapsulamiento, herencia y polimorfismo.
Hablamos sobre los distintos tipos de lenguajes de programación y cuáles son los más utilizados. Conoció los
pasos para crear y ejecutar una aplicación de Java mediante el uso del JDK 6 de Sun. El capítulo exploró la historia
de Internet y World Wide Web, y la función de Java en cuanto al desarrollo de aplicaciones cliente/servidor dis-
tribuidas para Internet y Web. También aprendió acerca de la historia y el propósito de UML: el lenguaje gráfico
estándar en la industria para modelar sistemas de software. Por último, realizó pruebas de una o más aplicaciones
de Java, similares a los tipos de aplicaciones que aprenderá a programar en este libro.
En el capítulo 2 creará sus primeras aplicaciones en Java. Verá ejemplos que muestran cómo los programas
imprimen mensajes en pantalla y obtienen información del usuario para procesarla. Analizaremos y explicaremos
cada ejemplo, para facilitarle el proceso de aprender a programar en Java.
1.20 Recursos Web
Esta sección proporciona muchos recursos que le serán de utilidad a medida que aprenda Java. Los sitios incluyen
recursos de Java, herramientas de desarrollo de Java para estudiantes y profesionales, y nuestros propios sitios
Web, en donde podrá encontrar descargas y recursos asociados con este libro. También le proporcionaremos un
vínculo, en donde podrá suscribirse a nuestro boletín de correo electrónico Deitel® Buzz Online sin costo.
Sitios Web de Deitel & Associates
www.deitel.com
Contiene actualizaciones, correcciones y recursos adicionales para todas las publicaciones Deitel.
www.deitel.com/newsletter/subscribe.html
Suscríbase al boletín de correo electrónico gratuito Deitel® Buzz Online, para seguir el programa de publicaciones de
Deitel & Associates, incluyendo actualizaciones y fe de erratas para este libro.
www.prenhall.com/deitel
La página de inicio de Prentice Hall para las publicaciones Deitel. Aquí encontrará información detallada sobre los
productos, capítulos de ejemplo y Sitios Web complementarios con recursos para estudiantes e instructores.
www.deitel.com/books/jhtp7/
La página de inicio de Deitel & Associates para Cómo programar en Java, 7a edición. Aquí encontrará vínculos a los
ejemplos del libro (que también se incluyen en el CD que viene con el libro) y otros recursos.
Centros de recursos de Deitel sobre Java
www.deitel.com/Java/
Nuestro Centro de recursos sobre Java se enfoca en la enorme cantidad de contenido gratuito sobre Java, disponible en
línea. Empiece aquí su búsqueda de recursos, descargas, tutoriales, documentación, libros, libros electrónicos, diarios,
artículos, blogs y más, que le ayudarán a crear aplicaciones en Java.
www.deitel.com/JavaSE6Mustang/
Nuestro Centro de recursos sobre Java SE 6 (Mustang) es su guía para la última versión de Java. Este sitio incluye los
mejores recursos que encontramos en línea, para ayudarle a empezar con el desarrollo en Java SE 6.
www.deitel.com/JavaEE5/
Nuestro Centro de recursos sobre Java Enterprise Edition 5 (Java EE 5).
www.deitel.com/JavaCertification/
Nuestro Centro de recursos de evaluación de certificación y valoración.
1.20 Recursos Web 25
26 Capítulo 1 Introducción a las computadoras, Internet y Web
www.deitel.com/JavaDesignPatterns/
Nuestro Centro de recursos sobre los patrones de diseño de Java. En su libro, Design Patterns: Elements of Reusable
Object-Oriented Software (Boston: Addison-Wesley Professional, 1995), la “Banda de los cuatro” (E. Gamma, R. Helm,
R. Jonson y J. Vlissides) describen 23 patrones de diseño que proporcionan arquitecturas demostradas para construir
sistemas de software orientados a objetos. En este centro de recursos, encontrará discusiones sobre muchos de éstos y
otros patrones de diseño.
www.deitel.com/CodeSearchEngines/
Nuestro Centro de recursos sobre Motores de Búsqueda de Código y Sitios de Código incluye recursos que los desarro-
lladores utilizan para buscar código fuente en línea.
www.deitel.com/ProgrammingProjects/
Nuestro Centro de recursos sobre Proyectos de Programación es su guía para proyectos de programación estudiantiles
en línea.
Sitios Web de Sun Microsystems
java.sun.com/developer/onlineTraining/new2java/index.html
El centro “New to Java Center” (Centro para principiantes en Java) en el sitio Web de Sun Microsystems ofrece recursos
de capacitación en línea para ayudarle a empezar con la programación en Java.
java.sun.com/javase/6/download.jsp
La página de descarga para el Kit de Desarrollo de Java 6 (JDK 6) y su documentación. El JDK incluye todo lo necesario
para compilar y ejecutar sus aplicaciones en Java SE 6 (Mustang).
java.sun.com/javase/6/webnotes/install/index.html
Instrucciones para instalar el JDK 6 en plataformas Solaris, Windows y Linux.
java.sun.com/javase/6/docs/api/index.html
El sitio en línea para la documentación de la API de Java SE 6.
java.sun.com/javase
La página de inicio para la plataforma Java Standard Edition.
java.sun.com
La página de inicio de la tecnología Java de Sun ofrece descargas, referencias, foros, tutoriales en línea y mucho más.
java.sun.com/reference/docs/index.html
El sitio de documentación de Sun para todas las tecnologías de Java.
developers.sun.com
La página de inicio de Sun para los desarrolladores de Java proporciona descargas, APIs, ejemplos de código, artículos
con asesoría técnica y otros recursos sobre las mejores prácticas de desarrollo en Java.
Editores y Entornos de Desarrollo Integrados
www.eclipse.org
El entorno de desarrollo Eclipse puede usarse para desarrollar código en cualquier lenguaje de programación. Puede
descargar el entorno y varios complementos (plug-ins) de Java para desarrollar sus programas en Java.
www.netbeans.org
El IDE NetBeans. Una de las herramientas de desarrollo para Java más populares, de distribución gratuita.
borland.com/products/downloads/download_jbuilder.html
Borland ofrece una versión Foundation Edition gratuita de su popular IDE JBuilder para Java. Este sitio también ofrece
versiones de prueba de 30 días de las ediciones Enterprise y Developer.
www.blueJ.org
BlueJ: una herramienta gratuita diseñada para ayudar a enseñar Java orientado a objetos a los programadores novatos.
www.jgrasp.org
Descargas, documentación y tutoriales sobre jGRASP. Esta herramienta muestra representaciones visuales de programas
en Java, para ayudar a su comprensión.
www.jedit.org
jEdit: un editor de texto escrito en Java.
developers.sun.com/prodtech/javatools/jsenterprise/index.jsp
El IDE Sun Java Studio Enterprise: la versión mejorada de NetBeans de Sun Microsystems.
www.jcreator.com
JCreator: un IDE popular para Java. JCreator Lite Edition está disponible como descarga gratuita. También está dispo-
nible una versión de prueba de 30 días de JCreator Pro Edition.
www.textpad.com
TextPad: compile, edite y ejecute sus programas en Java desde este editor, que proporciona coloreo de sintaxis y una
interfaz fácil de usar.
www.download.com
Un sitio que contiene descargas de aplicaciones de freeware y shareware, incluyendo programas editores.
Sitios de recursos adicionales sobre Java
www.javalobby.org
Proporciona noticias actualizadas sobre Java, foros en donde los desarrolladores pueden intercambiar tips y consejos, y
una base de conocimiento de Java extensa, que organiza artículos y descargas en toda la Web.
www.jguru.com
Ofrece foros, descargas, artículos, cursos en línea y una extensa colección de FAQs (Preguntas frecuentes) sobre Java.
www.javaworld.com
Ofrece recursos para desarrolladores de Java, como artículos, índices de libros populares sobre Java, tips y FAQs.
www.ftponline.com/javapro
La revista JavaPro contiene artículos mensuales, tips de programación, reseñas de libros y mucho más.
sys-con.com/java/
El Diario de Desarrolladores de Java de Sys-Con Media ofrece artículos, libros electrónicos y otros recursos sobre Java.
Resumen
Sección 1.1 Introducción
• Java se ha convertido en el lenguaje de elección para implementar aplicaciones basadas en Internet y software para
dispositivos que se comunican a través de una red.
• Java Enterprise Edition (Java EE) está orientada hacia el desarrollo de aplicaciones de redes distribuidas de gran
escala, y aplicaciones basadas en Web.
• Java Micro Edition (Java ME) está orientada hacia el desarrollo de aplicaciones para dispositivos pequeños, con
memoria limitada, como teléfonos celulares, radiolocalizadotes y PDAs.
Sección 1.2 ¿Qué es una computadora?
• Una computadora es un dispositivo capaz de realizar cálculos y tomar decisiones lógicas a velocidades de millones
(incluso de miles de millones) de veces más rápidas que los humanos.
• Las computadoras procesan los datos bajo el control de conjuntos de instrucciones llamadas programas de cómputo.
Los programas guían a las computadoras a través de acciones especificadas por gente llamada programadores de
computadoras.
• Una computadora está compuesta por varios dispositivos conocidos como hardware. A los programas que se ejecu-
tan en una computadora se les denomina software.
Sección 1.3 Organización de una computadora
• Casi todas las computadoras pueden representarse mediante seis unidades lógicas o secciones.
• La unidad de entrada obtiene información desde los dispositivos de entrada y pone esta información a disposición
de las otras unidades para que pueda procesarse.
• La unidad de salida toma información que ya ha sido procesada por la computadora y la coloca en los diferentes
dispositivos de salida, para que esté disponible fuera de la computadora.
• La unidad de memoria es la sección de “almacén” de acceso rápido, pero con relativa baja capacidad, de la compu-
tadora. Retiene la información que se introduce a través de la unidad de entrada, para que la información pueda estar
disponible de manera inmediata para procesarla cuando sea necesario. También retiene la información procesada
hasta que ésta pueda ser colocada en los dispositivos de salida por la unidad de salida.
• La unidad aritmética y lógica (ALU) es la responsable de realizar cálculos (como suma, resta, multiplicación y divi-
sión) y tomar decisiones.
Resumen 27
• La unidad central de procesamiento (CPU) coordina y supervisa la operación de las demás secciones. La CPU le
indica a la unidad de entrada cuándo debe grabarse la información dentro de la unidad de memoria, a la ALU cuán-
do debe utilizarse la información de la unidad de memoria para los cálculos, y a la unidad de salida cuándo enviar
la información desde la unidad de memoria hasta ciertos dispositivos de salida.
• Los multiprocesadores contienen múltiples CPUs y, por lo tanto, pueden realizar muchas operaciones de manera
simultánea.
• La unidad de almacenamiento secundario es la sección de “almacén” de alta capacidad y de larga duración de la
computadora. Los programas o datos que no se encuentran en ejecución por las otras unidades, normalmente se
colocan en dispositivos de almacenamiento secundario hasta que son requeridos de nuevo.
Sección 1.4 Los primeros sistemas operativos
• Las primeras computadoras eran capaces de realizar solamente una tarea o trabajo a la vez.
• Los sistemas operativos se desarrollaron para facilitar el uso de la computadora.
• La multiprogramación significa la operación simultánea de muchas tareas.
• Con el tiempo compartido, la computadora ejecuta una pequeña porción del trabajo de un usuario y después pro-
cede a dar servicio al siguiente usuario, con la posibilidad de proporcionar el servicio a cada usuario varias veces por
segundo.
Sección 1.5 Computación personal, distribuida y cliente/servidor
• En 1977, Apple Computer popularizó el fenómeno de la computación personal.
• En 1981, IBM, el vendedor de computadoras más grande del mundo, introdujo la Computadora Personal (PC) de
IBM, que legitimó rápidamente la computación en las empresas, en la industria y en las organizaciones guberna-
mentales.
• En la computación distribuida, en vez de que la computación se realice sólo en una computadora central, se distri-
buye mediante redes a los sitios en donde se realiza el trabajo de la empresa.
• Los servidores almacenan datos que pueden utilizar las computadoras cliente distribuidas a través de la red, de ahí el
término de computación cliente/servidor.
• Java se está utilizando ampliamente para escribir software para redes de computadoras y para aplicaciones cliente/
servidor distribuidas.
Sección 1.6 Internet y World Wide Web
• Internet es accesible por más de mil millones de computadoras y dispositivos controlados por computadora.
• Con la introducción de World Wide Web, Internet se ha convertido explosivamente en uno de los principales meca-
nismos de comunicación en todo el mundo.
Sección 1.7 Lenguajes máquina, lenguajes ensambladores y lenguajes de alto nivel
• Cualquier computadora puede entender de manera directa sólo su propio lenguaje máquina.
• El lenguaje máquina es el “lenguaje natural” de una computadora.
• Por lo general, los lenguajes máquina consisten en cadenas de números (que finalmente se reducen a 1s y 0s) que
instruyen a las computadoras para realizar sus operaciones más elementales, una a la vez.
• Los lenguajes máquina son dependientes de la máquina.
• Los programadores empezaron a utilizar abreviaturas del inglés para representar las operaciones elementales. Estas
abreviaturas formaron la base de los lenguajes ensambladores.
• Los programas traductores conocidos como ensambladores se desarrollaron para convertir los primeros programas
en lenguaje ensamblador a lenguaje máquina, a la velocidad de la computadora.
• Los lenguajes de alto nivel permiten a los programadores escribir instrucciones parecidas al lenguaje inglés cotidiano,
y contienen notaciones matemáticas de uso común.
• Java es el lenguaje de programación de alto nivel más utilizado en todo el mundo.
• Los programas intérpretes ejecutan los programas en lenguajes de alto nivel directamente.
Sección 1.8 Historia de C y C++
• Java evolucionó de C++, el cual evolucionó de C, que a su vez evolucionó de BCPL y B.
• El lenguaje C evolucionó a partir de B, gracias al trabajo de Dennis Ritchie en los laboratorios Bell. Inicialmente, se
hizo muy popular como lenguaje de desarrollo para el sistema operativo UNIX.
• A principios de la década de los ochenta, Bjarne Stroustrup desarrolló una extensión de C en los laboratorios Bell:
C++. Este lenguaje proporciona un conjunto de características que “pulen” al lenguaje C, además de la capacidad de
una programación orientada a objetos.
28 Capítulo 1 Introducción a las computadoras, Internet y Web
Sección 1.9 Historia de Java
• Java se utiliza para desarrollar aplicaciones empresariales a gran escala, para mejorar la funcionalidad de los servidores
Web, para proporcionar aplicaciones para los dispositivos domésticos y para muchos otros propósitos.
• Los programas en Java consisten en piezas llamadas clases. Las clases incluyen piezas llamadas métodos, los cuales
realizan tareas y devuelven información cuando se completan estas tareas.
Sección 1.10 Bibliotecas de clases de Java
• La mayoría de los programadores en Java aprovechan las ricas colecciones de clases existentes en las bibliotecas de
clases de Java, que también se conocen como APIs (Interfaces de programación de aplicaciones) de Java.
• La ventaja de crear sus propias clases y métodos es que sabe cómo funcionan y puede examinar el código. La desven-
taja es que se requiere una cantidad considerable de tiempo y un esfuerzo potencialmente complejo.
Sección 1.11 FORTRAN, COBOL, Pascal y Ada
• Fortran (FORmula TRANslator, Traductor de fórmulas) fue desarrollado por IBM Corporation a mediados de la
década de los cincuenta para utilizarse en aplicaciones científicas y de ingeniería que requerían cálculos matemáticos
complejos.
• COBOL (COmmon Business Oriented Language, Lenguaje común orientado a negocios) se utiliza en aplicaciones
comerciales que requieren de una manipulación precisa y eficiente de grandes volúmenes de datos.
• Las actividades de investigación en la década de los sesenta dieron como resultado la evolución de la programación
estructurada (un método disciplinado para escribir programas que sean más claros, fáciles de probar y depurar, y más
fáciles de modificar que los programas extensos producidos con técnicas anteriores).
• Pascal se diseñó para la enseñanza de la programación estructurada en ambientes académicos, y de inmediato se
convirtió en el lenguaje de programación preferido en la mayoría de las universidades.
• El lenguaje de programación Ada se desarrolló bajo el patrocinio del Departamento de Defensa de los Estados
Unidos (DOD) para satisfacer la mayoría de sus necesidades. Una característica de Ada conocida como multitarea
permite a los programadores especificar que muchas actividades ocurrirán en paralelo. Java, a través de una técnica
que se conoce como subprocesamiento múltiple, también permite a los programadores escribir programas con activi-
dades paralelas.
Sección 1.12 BASIC, Visual Basic, Visual C++, C# y .NET
• BASIC fue desarrollado a mediados de la década de los sesenta para escribir programas simples.
• El lenguaje Visual Basic de Microsoft simplifica el desarrollo de aplicaciones para Windows.
• La plataforma .NET de Microsoft integra Internet y Web en las aplicaciones de computadora.
Sección 1.13 Entorno de desarrollo típico en Java
• Por lo general, los programas en Java pasan a través de cinco fases: edición, compilación, carga, verificación y ejecu-
ción.
• La fase 1 consiste en editar un archivo con un editor. Usted escribe un programa utilizando el editor, realiza las
correcciones necesarias y guarda el programa en un dispositivo de almacenamiento secundario, tal como su disco
duro.
• Un nombre de archivo que termina con la extensión .java indica que éste contiene código fuente en Java.
• Los entornos de desarrollo integrados (IDEs) proporcionan herramientas que dan soporte al proceso de desarrollo
del software, incluyendo editores para escribir y editar programas, y depuradores para localizar errores lógicos.
• En la fase 2, el programador utiliza el comando javac para compilar un programa.
• Si un programa se compila, el compilador produce un archivo .class que contiene el programa compilado.
• El compilador de Java traduce el código fuente de Java en códigos de bytes que representan las tareas a ejecutar. La
Máquina Virtual de Java (JVM) ejecuta los códigos de bytes.
• En la fase 3, de carga, el cargador de clases toma los archivos .class que contienen los códigos de bytes del progra-
ma y los transfiere a la memoria principal.
• En la fase 4, a medida que se cargan las clases, el verificador de códigos de bytes examina sus códigos de bytes para
asegurar que sean válidos y que no violen las restricciones de seguridad de Java.
• En la fase 5, la JVM ejecuta los códigos de bytes del programa.
Sección 1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos
y UML
• El Lenguaje Unificado de Modelado (UML) es un lenguaje gráfico que permite a las personas que crean sistemas
representar sus diseños orientados a objetos en una notación común.
Resumen 29
• El diseño orientado a objetos (DOO) modela los componentes de software en términos de objetos reales.
• Los objetos tienen la propiedad de ocultamiento de la información: por lo general, no se permite a los objetos de
una clase saber cómo se implementan los objetos de otras clases.
• La programación orientada a objetos (POO) implementa diseños orientados a objetos.
• Los programadores de Java se concentran en crear sus propios tipos definidos por el usuario, conocidos como clases.
Cada clase contiene datos y métodos que manipulan a esos datos y proporcionan servicios a los clientes.
• Los componentes de datos de una clase son los atributos o campos; los componentes de operación son los
métodos.
• Las clases pueden tener relaciones con otras clases; a estas relaciones se les llama asociaciones.
• El proceso de empaquetar software en forma de clases hace posible que los sistemas de software posteriores reutilicen
esas clases.
• A una instancia de una clase se le llama objeto.
• El proceso de analizar y diseñar un sistema desde un punto de vista orientado a objetos se llama análisis y diseño
orientados a objetos (A/DOO).
Terminología
30 Capítulo 1 Introducción a las computadoras, Internet y Web
Ada
ALU (unidad aritmética y lógica)
ANSI C
API de Java (Interfaz de Programación de Aplicaciones)
atributo
BASIC
bibliotecas de clases
C
C#
C++
cargador de clases
clase
.class, archivo
COBOL
código de bytes
compilador
compilador HotSpot™
compilador JIT (justo a tiempo)
componente reutilizable
comportamiento
computación cliente/servidor
computación distribuida
computación personal
computadora
contenido dinámico
CPU (unidad central de procesamiento)
disco
diseño orientado a objetos (DOO)
dispositivo de entrada
dispositivo de salida
documento de requerimientos
editor
encapsulamiento
ensamblador
entrada/salida (E/S)
enunciado del problema
error en tiempo de compilación
error en tiempo de ejecución
error fatal en tiempo de ejecución
error no fatal en tiempo de ejecución
fase de carga
fase de compilación
fase de edición
fase de ejecución
fase de verificación
flujo de datos
Fortran
Hardware
herencia
HTML (Lenguaje de Marcado de Hipertexto)
IDE (Entorno Integrado de Desarrollo)
Internet
Intérprete
Java
Java Enterprise Edition (Java EE)
.java, extensión de nombre de archivo
Java Micro Edition (Java ME)
Java Standard Edition (Java SE)
java, intérprete
javac, compilador
KIS (simplifíquelo)
Kit de Desarrollo de Java (JDK)
LAN (red de área local)
lenguaje de alto nivel
lenguaje ensamblador
lenguaje máquina
Lenguaje Unificado de Modelado (UML)
Máquina virtual de Java (JVM)
memoria principal
método
método de código activo (live-code)
Microsoft Internet Explorer, navegador Web
modelado
multiprocesador
multiprogramación
.NET
objeto
ocultamiento de información
Pascal
plataforma
portabilidad
programa de cómputo
programa traductor
programación estructurada
programación orientada a objetos (POO)
programación por procedimientos
programador de computadoras
pseudocódigo
reutilización de software
servidor de archivos
sistema heredado
sistema operativo
software
subprocesamiento múltiple
Sun Microsystems
tiempo compartido
tipo definido por el usuario
traducción
unidad aritmética y lógica (ALU)
unidad central de procesamiento (CPU)
unidad de almacenamiento secundario
unidad de entrada
unidad de memoria
unidad de salida
verificador de código de bytes
Visual Basic .NET
Visual C++ .NET
World Wide Web
Ejercicios de autoevaluación 31
Ejercicios de autoevaluación
1.1 Complete las siguientes oraciones:
a) La compañía que popularizó la computación personal fue ______________.
b) La computadora que legitimó la computación personal en los negocios y la industria fue _____________.
c) Las computadoras procesan los datos bajo el control de conjuntos de instrucciones llamadas ___________.
d) Las seis unidades lógicas clave de la computadora son _____________, _____________, _____________,
_____________, _____________ y _____________.
e) Los tres tipos de lenguajes descritos en este capítulo son _____________, _____________ y __________
____________________________.
f) Los programas que traducen programas en lenguaje de alto nivel a lenguaje máquina se denominan _____
_____________.
g) La __________ permite a los usuarios de computadora localizar y ver documentos basados en multimedia
sobre casi cualquier tema, a través de Internet.
h) _____________, permite a un programa en Java realizar varias actividades en paralelo.
1.2 Complete cada una de las siguientes oraciones relacionadas con el entorno de Java:
a) El comando _____________ del JDK ejecuta una aplicación en Java.
b) El comando _____________ del JDK compila un programa en Java.
c) El archivo de un programa en Java debe terminar con la extensión de archivo _____________.
d) Cuando se compila un programa en Java, el archivo producido por el compilador termina con la exten-
sión _____________.
e) El archivo producido por el compilador de Java contiene _____________ que se ejecutan mediante la
Máquina Virtual de Java.
1.3 Complete cada una de las siguientes oraciones (basándose en la sección 1.16):
a) Los objetos tienen una propiedad que se conoce como _____________; aunque éstos pueden saber cómo
comunicarse con los demás objetos a través de interfaces bien definidas, generalmente no se les permite
saber cómo están implementados los otros objetos.
b) Los programadores de Java se concentran en crear _____________, que contienen campos y el conjunto
de métodos que manipulan a esos campos y proporcionan servicios a los clientes.
c) Las clases pueden tener relaciones con otras clases; a éstas relaciones se les llama _____________.
d) El proceso de analizar y diseñar un sistema desde un punto de vista orientado a objetos se conoce como
_____________ .
e) El DOO aprovecha las relaciones _____________, en donde se derivan nuevas clases de objetos al absorber
las características de las clases existentes y después agregar sus propias características únicas.
f) _____________ es un lenguaje gráfico que permite a las personas que diseñan sistemas de software utilizar
una notación estándar en la industria para representarlos.
g) El tamaño, forma, color y peso de un objeto se consideran _____________ del mismo.
32 Capítulo 1 Introducción a las computadoras, Internet y Web
Respuestas a los ejercicios de autoevaluación
1.1 a) Apple. b) PC de IBM. c) programas. d) unidad de entrada, unidad de salida, unidad de memoria,
unidad aritmética y lógica, unidad central de procesamiento, unidad de almacenamiento secundario. e) lenguajes
máquina, lenguajes ensambladores, lenguajes de alto nivel. f) compiladores. g) World Wide Web. h) Subprocesa-
miento múltiple.
1.2 a) java. b) javac. c) .java. d) .class. e) códigos de bytes.
1.3 a) ocultamiento de información. b) clases. c) asociaciones. d) análisis y diseño orientados a objetos (A/
DOO). e) herencia. f) El Lenguaje Unificado de Modelado (UML). g) atributos.
Ejercicios
1.4 Clasifique cada uno de los siguientes elementos como hardware o software:
a) CPU
b) compilador de Java
c) JVM
d) unidad de entrada
e) editor
1.5 Complete cada una de las siguientes oraciones:
a) La unidad lógica de la computadora que recibe información desde el exterior de la computadora para que
ésta la utilice se llama _____________.
b) El proceso de indicar a la computadora cómo resolver problemas específicos se llama _____________.
c) _____________ es un tipo de lenguaje computacional que utiliza abreviaturas del inglés para las instruc-
ciones de lenguaje máquina.
d) _____________ es una unidad lógica de la computadora que envía información, que ya ha sido procesada
por la computadora, a varios dispositivos, de manera que la información pueda utilizarse fuera de la compu-
tadora.
e) _____________ y _____________ son unidades lógicas de la computadora que retienen información.
f) _____________ es una unidad lógica de la computadora que realiza cálculos.
g) _____________ es una unidad lógica de la computadora que toma decisiones lógicas.
h) Los lenguajes _____________ son los más convenientes para que el programador pueda escribir programas
rápida y fácilmente.
i) Al único lenguaje que una computadora puede entender directamente se le conoce como el __________
de esa computadora.
j) _____________ es una unidad lógica de la computadora que coordina las actividades de todas las demás
unidades lógicas.
1.6 Indique la diferencia entre los términos error fatal y error no fatal. ¿Por qué sería preferible experimentar un
error fatal, en vez de un error no fatal?
1.7 Complete cada una de las siguientes oraciones:
a) _____________ se utiliza ahora para desarrollar aplicaciones empresariales de gran escala, para mejorar la
funcionalidad de los servidores Web, para proporcionar aplicaciones para dispositivos domésticos y para
muchos otros fines más.
b) _____________ se diseñó específicamente para la plataforma .NET, de manera que los programadores
pudieran migrar fácilmente a .NET.
c) Inicialmente, _____________ se hizo muy popular como lenguaje de desarrollo para el sistema operativo
UNIX.
d) _____________ fue desarrollado a mediados de la década de los sesenta en el Dartmouth College, como
un medio para escribir programas simples.
e) _____________ fue desarrollado por IBM Corporation a mediados de la década de los cincuenta para
utilizarse en aplicaciones científicas y de ingeniería que requerían cálculos matemáticos complejos.
f) _____________ se utiliza para aplicaciones comerciales que requieren la manipulación precisa y eficiente
de grandes cantidades de datos.
g) El lenguaje de programación _____________ fue desarrollado por Bjarne Stroustrup a principios de la
década de los ochenta, en los laboratorios Bell.
1.8 Complete cada una de las siguientes oraciones (basándose en la sección 1.13):
a) Por lo general, los programas de Java pasan a través de cinco fases: _____________, _____________,
_____________, _____________ y _____________.
b) Un _____________ proporciona muchas herramientas que dan soporte al proceso de desarrollo de soft-
ware, como los editores para escribir y editar programas, los depuradores para localizar los errores lógicos en
los programas, y muchas otras características más.
c) El comando java invoca al _____________, que ejecuta los programas de Java.
d) Un(a) _____________ es una aplicación de software que simula a una computadora, pero oculta el sistema
operativo subyacente y el hardware de los programas que interactúan con la VM.
e) Un programa _____________ puede ejecutarse en múltiples plataformas.
f) El _____________ toma los archivos .class que contienen los códigos de bytes del programa y los trans-
fiere a la memoria principal.
g) El _____________ examina los códigos de bytes para asegurar que sean válidos.
1.9 Explique las dos fases de compilación de los programas de Java.
Ejercicios 33
Introducción a
las aplicaciones
en Java
OBJETIVO S
En este capítulo aprenderá a:
Escribir aplicaciones simples en Java.
Utilizar las instrucciones de entrada y salida.
Familiarizarse con los tipos primitivos de Java.
Comprender los conceptos básicos de la memoria.
Utilizar los operadores aritméticos.
Comprender la precedencia de los operadores aritméticos.
Escribir instrucciones para tomar decisiones.
Utilizar los operadores relacionales y de igualdad.
Q
Q
Q
Q
Q
Q
Q
Q
¿Qué hay en un nombre?
A eso a lo que llamamos
rosa, si le diéramos otro
nombre conservaría su
misma fragancia dulce.
— William Shakespeare
Al hacer frente a una
decisión, siempre me
pregunto, “¿Cuál
será la solución más
divertida?”
—Peggy Walker
“Toma un poco más de
té”, dijo el conejo blanco a
Alicia, con gran seriedad.
“No he tomado nada
todavía.” Contestó Alicia
en tono ofendido, “Entonces
no puedo tomar más”.
“Querrás decir que no
puedes tomar menos”, dijo
el sombrerero loco, “es muy
fácil tomar más que nada”.
—Lewis Carroll
2
2.1 Introducción
Ahora presentaremos la programación de aplicaciones en Java, que facilita una metodología disciplinada para el
diseño de programas. La mayoría de los programas en Java que estudiará en este libro procesan información y
muestran resultados. Le presentaremos seis ejemplos que demuestran cómo sus programas pueden mostrar men-
sajes y cómo pueden obtener información del usuario para procesarla. Comenzaremos con varios ejemplos que
simplemente muestran mensajes en la pantalla. Después demostraremos un programa que obtiene dos números
de un usuario, calcula su suma y muestra el resultado. Usted aprenderá a realizar varios cálculos aritméticos y a
guardar sus resultados para usarlos más adelante. El último ejemplo en este capítulo demuestra los fundamentos
de toma de decisiones, al mostrarle cómo comparar números y después mostrar mensajes con base en los resul-
tados de la comparación. Por ejemplo, el programa muestra un mensaje que indica que dos números son iguales
sólo si tienen el mismo valor. Analizaremos cada ejemplo, una línea a la vez, para ayudarle a aprender a programar
en Java. En los ejercicios del capítulo proporcionamos muchos problemas retadores y divertidos, para ayudarle a
aplicar las habilidades que aprenderá aquí.
2.2 Su primer programa en Java: imprimir una línea de texto
Cada vez que utiliza una computadora, ejecuta diversas aplicaciones que realizan tareas por usted. Por ejemplo,
su aplicación de correo electrónico le permite enviar y recibir mensajes de correo, y su navegador Web le permite
ver páginas de sitios Web en todo el mundo. Los programadores de computadoras crean dichas aplicaciones,
escribiendo programas de cómputo.
Una aplicación en Java es un programa de computadora que se ejecuta cuando usted utiliza el comando
java para iniciar la Máquina Virtual de Java (JVM). Consideremos una aplicación simple que muestra una línea
de texto. (Más adelante en esta sección hablaremos sobre cómo compilar y ejecutar una aplicación). El programa
y su salida se muestran en la figura 2.1. La salida aparece en el recuadro al final del programa. El programa ilustra
varias características importantes del lenguaje. Java utiliza notaciones que pueden parecer extrañas a los no pro-
gramadores. Además, cada uno de los programas que presentamos en este libro tiene números de línea incluidos
para su conveniencia; los números de línea no son parte de los programas en Java. Pronto veremos que la línea 9
se encarga del verdadero trabajo del programa; a saber, mostrar la frase Bienvenido a la programacion en
Java!en la pantalla. Ahora consideremos cada línea del programa en orden.
La línea 1
// Fig. 2.1: Bienvenido1.java
empieza con //, indicando que el resto de la línea es un comentario. Los programadores insertan comentarios
para documentar los programas y mejorar su legibilidad. Los comentarios también ayudan a otras personas a
leer y comprender un programa. El compilador de Java ignora estos comentarios, de manera que la computadora
no hace nada cuando el programa se ejecuta. Por convención, comenzamos cada uno de los programas con un
comentario, el cual indica el número de figura y el nombre del archivo.
2.1 Introducción
2.2 Su primer programa en Java: imprimir una línea de texto
2.3 Modificación de nuestro primer programa en Java
2.4 Cómo mostrar texto con printf
2.5 Otra aplicación en Java: suma de enteros
2.6 Conceptos acerca de la memoria
2.7 Aritmética
2.8 Toma de decisiones: operadores de igualdad y relacionales
2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de requerimientos
2.10 Conclusión
Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios
Pla
n
g
e
ne
r
a
l
2.2 Su primer programa en Java: imprimir una línea de texto 35
36 Capítulo 2 Introducción a las aplicaciones en Java
Un comentario que comienza con // se llama comentario de fin de línea (o de una sola línea), ya que
termina al final de la línea en la que aparece. Un comentario que se especifica con // puede empezar también en
medio de una línea, y continuar solamente hasta el final de esa línea (como en las líneas 11 y 13).
Los comentarios tradicionales (también conocidos como comentarios de múltiples líneas), como el que
se muestra a continuación
/* Éste es un comentario
Tradicional. Puede
dividirse en muchas líneas */
se distribuyen en varias líneas. Este tipo de comentario comienza con el delimitador /* y termina con */. El com-
pilador ignora todo el texto que esté entre los delimitadores. Java incorporó los comentarios tradicionales y los
comentarios de fin de línea de los lenguajes de programación C y C++, respectivamente. En este libro utilizamos co-
mentarios de fin de línea.
Java también cuenta con comentarios Javadoc, que están delimitados por /** y */. Al igual que con los
comentarios tradicionales, el compilador ignora todo el texto entre los delimitadores de los comentarios Javadoc.
Estos comentarios permiten a los programadores incrustar la documentación del programa directamente en éste.
Dichos comentarios son el formato preferido en la industria. El programa de utilería javadoc (parte del Kit de
Desarrollo de Java SE) lee esos comentarios y los utiliza para preparar la documentación de su programa, en for-
mato HTML. Hay algunas sutilezas en cuanto al uso apropiado de los comentarios estilo Java. En el apéndice K,
Creación de documentación con javadoc, demostramos el uso de los comentarios Javadoc y la herramienta
javadoc. Para obtener información completa, visite la página de herramientas de javadoc de Sun en java.sun.
com/javase/6/docs/technotes/guides/javadoc/index.html.
Error común de programación 2.1
Olvidar uno de los delimitadores de un comentario tradicional o Javadoc es un error de sintaxis. La sintaxis de un
lenguaje de programación que especifica las reglas para crear un programa apropiado en ese lenguaje. Un error de
sintaxis ocurre cuando el compilador encuentra código que viola las reglas del lenguaje Java (es decir, su sintaxis).
En este caso, el compilador muestra un mensaje de error para ayudar al programador a identificar y corregir el código
incorrecto. Los errores de sintaxis se conocen también como errores del compilador, errores en tiempo de com-
pilación o errores de compilación, ya que el compilador los detecta durante la fase de compilación. Usted no podrá
ejecutar su programa sino hasta que corrija todos los errores de sintaxis que éste contenga.
La línea 2
// Programa para imprimir texto.
es un comentario de fin de línea que describe el propósito del programa.
1 // Fig. 2.1: Bienvenido1.java
2 // Programa para imprimir texto.
3
4 public class Bienvenido1
5 {
6 // el método main empieza la ejecución de la aplicación en Java
7 public static void main( String args[] )
8 {
9 System.out.println( "Bienvenido a la programacion en Java!" );
10
11 } // fin del método main
12
13 } // fin de la clase Bienvenido1
Figura 2.1 | Programa para imprimir texto.
Bienvenido a la programacion en Java!
2.2 Su primer programa en Java: imprimir una línea de texto 37
Buena práctica de programación 2.1
Es conveniente que todo programa comience con un comentario que explique su propósito, el autor, la fecha y la hora
de la última modificación del mismo. (No mostraremos el autor, la fecha y la hora en los programas de este libro, ya
que sería redundante).
La línea 3 es una línea en blanco. Los programadores usan líneas en blanco y espacios para facilitar la lectura
de los programas. En conjunto, las líneas en blanco, los espacios y los tabuladores se conocen como espacio en
blanco. (Los espacios y tabuladores se conocen específicamente como caracteres de espacio en blanco). El com-
pilador ignora el espacio en blanco. En éste y en los siguientes capítulos, hablaremos sobre las convenciones para
utilizar espacios en blanco para mejorar la legibilidad de los programas.
Buena práctica de programación 2.2
Utilice líneas en blanco y espacios para mejorar la legibilidad del programa.
La línea 4
public class Bienvenido1
comienza una declaración de clase para la clase Bienvenido1. Todo programa en Java consiste de, cuando
menos, una declaración de clase que usted, el programador, debe definir. Estas clases se conocen como clases defi-
nidas por el programador o clases definidas por el usuario. La palabra clave class introduce una declaración
de clase en Java, la cual debe ir seguida inmediatamente por el nombre de la clase (Bienvenido1). Las palabras
clave (algunas veces conocidas como palabras reservadas) se reservan para uso exclusivo de Java (hablaremos
sobre las diversas palabras clave a lo largo de este texto) y siempre se escriben en minúscula. En el apéndice C se
muestra la lista completa de palabras clave de Java.
Por convención, todos los nombres de clases en Java comienzan con una letra mayúscula, y la primera letra de
cada palabra en el nombre de la clase debe ir en mayúscula (por ejemplo, EjemploDeNombreDeClase). En Java, el
nombre de una clase se conoce como identificador: una serie de caracteres que pueden ser letras, dígitos, guiones
bajos (_) y signos de moneda ($), que no comience con un dígito ni tenga espacios. Algunos identificadores váli-
dos son Bienvenido1, $valor, _valor, m_campoEntrada1 y boton7. El nombre 7boton no es un identificador
válido, ya que comienza con un dígito, y el nombre campo entrada tampoco lo es debido a que contiene un
espacio. Por lo general, un identificador que no empieza con una letra mayúscula no es el nombre de una clase.
Java es sensible a mayúsculas y minúsculas; es decir, las letras mayúsculas y minúsculas son distintas, por lo que
a1 y A1 son distintos identificadores (pero ambos son válidos).
Buena práctica de programación 2.3
Por convención, el identificador del nombre de una clase siempre debe comenzar con una letra mayúscula, y la pri-
mera letra de cada palabra subsiguiente del identificador también debe ir en mayúscula. Los programadores de Java
saben que, por lo general, dichos identificadores representan clases de Java, por lo que si usted nombra a sus clases de
esta forma, sus programas serán más legibles.
Error común de programación 2.2
Java es sensible a mayúsculas y minúsculas. No utilizar la combinación apropiada de letras minúsculas y mayúsculas
para un identificador, generalmente produce un error de compilación.
En los capítulos 2 al 7, cada una de las clases que definimos comienza con la palabra clave public. Cuando
usted guarda su declaración de clase public en un archivo, el nombre del mismo debe ser el nombre de la clase,
seguido de la extensión de nombre de archivo .java. Para nuestra aplicación, el nombre del archivo es Bien-
venido1.java. En el capítulo 8 aprenderá más acerca de las clases public y las que no son public.
Error común de programación 2.3
Una clase public debe colocarse en un archivo que tenga el mismo nombre que la clase (en términos de ortografía y
uso de mayúsculas) y la extensión .java; en caso contrario, ocurre un error de compilación.
38 Capítulo 2 Introducción a las aplicaciones en Java
Error común de programación 2.4
Es un error que un archivo que contiene la declaración de una clase, no finalice con la extensión .java. El compi-
lador de Java sólo compila archivos con la extensión .java.
Una llave izquierda (en la línea 5 de este programa), {, comienza el cuerpo de todas las declaraciones de
clases. Su correspondiente llave derecha (en la línea 13), }, debe terminar cada declaración de una clase. Observe
que las líneas de la 6 a la 11 tienen sangría; ésta es una de las convenciones de espaciado que se mencionaron ante-
riormente. Definimos cada una de las convenciones de espaciado como una Buena práctica de programación.
Buena práctica de programación 2.4
Siempre que escriba una llave izquierda de apertura ({) en su programa, escriba inmediatamente la llave derecha de
cierre (}) y luego vuelva a colocar el cursor entre las llaves y utilice sangría para comenzar a escribir el cuerpo. Esta
práctica ayuda a evitar errores debido a la omisión de una de las llaves.
Buena práctica de programación 2.5
Aplique sangría a todo el cuerpo de la declaración de cada clase, usando un “nivel” de sangría entre la llave izquierda
({) y la llave derecha (}), las cuales delimitan el cuerpo de la clase. Este formato enfatiza la estructura de la decla-
ración de la clase, y facilita su lectura.
Buena práctica de programación 2.6
Establezca una convención para el tamaño de sangría que usted prefiera, y después aplique uniformemente esta con-
vención. La tecla Tab puede utilizarse para crear sangrías, pero las posiciones de los tabuladores pueden variar entre
los diversos editores de texto. Le recomendamos utilizar tres espacios para formar un nivel de sangría.
Error común de programación 2.5
Es un error de sintaxis no utilizar las llaves por pares.
La línea 6
// el método main empieza la ejecución de la aplicación en Java
es un comentario de fin de línea que indica el propósito de las líneas 7 a 11 del programa. La línea 7
public static void main( String args[] )
es el punto de inicio de toda aplicación en Java. Los paréntesis después del identificador main indican que éste es
un bloque de construcción del programa, al cual se le llama método. Las declaraciones de clases en Java general-
mente contienen uno o más métodos. En una aplicación en Java, sólo uno de esos métodos debe llamarse main
y debe definirse como se muestra en la línea 7; de no ser así, la JVM no ejecutará la aplicación. Los métodos
pueden realizar tareas y devolver información una vez que las hayan concluido. La palabra clave void indica que
este método realizará una tarea, pero no devolverá ningún tipo de información cuando complete su tarea. Más
adelante veremos que muchos métodos devuelven información cuando finalizan sus tareas. Aprenderá más acerca
de los métodos en los capítulos 3 y 6. Por ahora, simplemente copie la primera línea de main en sus aplicaciones
en Java. En la línea 7, las palabras String args[] entre paréntesis son una parte requerida de la declaración del
método main. Hablaremos sobre esto en el capítulo 7, Arreglos.
La llave izquierda ({) en la línea 8 comienza el cuerpo de la declaración del método; su correspondiente
llave derecha (}) debe terminar el cuerpo de esa declaración (línea 11 del programa). Observe que la línea 9, entre
las llaves, tiene sangría.
Buena práctica de programación 2.7
Aplique un “nivel” de sangría a todo el cuerpo de la declaración de cada método, entre la llave izquierda ({) y la
llave derecha (}), las cuales delimitan el cuerpo del método. Este formato resalta la estructura del método y ayuda a
que su declaración sea más fácil de leer.
2.2 Su primer programa en Java: imprimir una línea de texto 39
La línea 9
System.out.println( "Bienvenido a la programacion en Java!" );
indica a la computadora que realice una acción; es decir, que imprima la cadena de caracteres contenida entre
los caracteres de comillas dobles (sin incluirlas). A una cadena también se le denomina cadena de caracteres,
mensaje o literal de cadena. Genéricamente, nos referimos a los caracteres entre comillas dobles como cadenas.
El compilador no ignora los caracteres de espacio en blanco dentro de las cadenas.
System.out se conoce como el objeto de salida estándar. System.out permite a las aplicaciones en Java
mostrar conjuntos de caracteres en la ventana de comandos, desde la cual se ejecuta la aplicación en Java. En
Microsoft Windows 95/98/ME, la ventana de comandos es el símbolo de MS-DOS. En versiones más recientes
de Microsoft Windows, la ventana de comandos es el Símbolo del sistema. En UNIX/Linux/Mac OS X, la
ventana de comandos se llama ventana de terminal o shell. Muchos programadores se refieren a la ventana de
comandos simplemente como la línea de comandos.
El método System.out.println muestra (o imprime) una línea de texto en la ventana de comandos. La
cadena dentro de los paréntesis en la línea 9 es el argumento para el método. El método System.out.println
realiza su tarea, mostrando (o enviando) su argumento en la ventana de comandos. Cuando System.out.
println completa su tarea, posiciona el cursor de salida (la ubicación en donde se mostrará el siguiente carácter)
al principio de la siguiente línea en la ventana de comandos. [Este desplazamiento del cursor es similar a cuando
un usuario oprime la tecla Intro, al escribir en un editor de texto (el cursor aparece al principio de la siguiente
línea en el archivo)].
Toda la línea 9, incluyendo System.out.println, el argumento "Bienvenido a la programacion en
Java!" entre paréntesis y el punto y coma (;), se conoce como una instrucción; y siempre debe terminar con
un punto y coma. Cuando se ejecuta la instrucción de la línea 9 de nuestro programa, ésta muestra el mensaje
Bienvenido a la programacion en Java! en la ventana de comandos. Por lo general, un método está com-
puesto por una o más instrucciones que realizan la tarea, como veremos en los siguientes programas.
Error común de programación 2.6
Omitir el punto y coma al final de una instrucción es un error de sintaxis.
Tip para prevenir errores 2.1
Al aprender a programar, es conveniente, en ocasiones, “descomponer” un programa funcional, para poder familiari-
zarse con los mensajes de error de sintaxis del compilador; ya que este tipo de mensajes no siempre indican el problema
exacto en el código. Y de esta manera, cuando se encuentren dichos mensajes de error de sintaxis, tendrá una idea de
qué fue lo que ocasionó el error. Trate de quitar un punto y coma o una llave del programa de la figura 2.1, y vuelva
a compilarlo de manera que pueda ver los mensajes de error generados por esta omisión.
Tip para prevenir errores 2.2
Cuando el compilador reporta un error de sintaxis, éste tal vez no se encuentre en el número de línea indicado por el
mensaje. Primero verifique la línea en la que se reportó el error; si esa línea no contiene errores de sintaxis, verifique
las líneas anteriores.
A algunos programadores se les dificulta, cuando leen o escriben un programa, relacionar las llaves izquierda
y derecha ({ y }) que delimitan el cuerpo de la declaración de una clase o de un método. Por esta razón, incluyen
un comentario de fin de línea después de una llave derecha de cierre (}) que termina la declaración de un método
y que termina la declaración de una clase. Por ejemplo, la línea 11
} // fin del método main
especifica la llave derecha de cierre (}) del método main, y la línea 13
} // fin de la clase Bienvenido1
especifica la llave derecha de cierre (}) de la clase Bienvenido1. Cada comentario indica el método o la clase que
termina con esa llave derecha.
40 Capítulo 2 Introducción a las aplicaciones en Java
Buena práctica de programación 2.8
Para mejorar la legibilidad de los programas, agregue un comentario de fin de línea después de la llave derecha de
cierre (}), que indique a qué método o clase pertenece.
Cómo compilar y ejecutar su primera aplicación en Java
Ahora estamos listos para compilar y ejecutar nuestro programa. Para este propósito, supondremos que usted
utiliza el Kit de Desarrollo 6.0 (JDK 6.0) de Java SE de Sun Microsystems. En nuestros centros de recursos en
www.deitel.com/ResourceCenters.html proporcionamos vínculos a tutoriales que le ayudarán a empezar a
trabajar con varias herramientas de desarrollo populares de Java.
Para compilar el programa, abra una ventana de comandos y cambie al directorio en donde está guardado el
programa. La mayoría de los sistemas operativos utilizan el comando cd para cambiar directorios. Por ejemplo,
cd c:ejemploscap02fig02_01
cambia al directorio fig02_01 en Windows. El comando
cd ~/ejemplos/cap02/fig02_01
cambia al directorio fig02_01 en UNIX/Linux/Mac OS X.
Para compilar el programa, escriba
javac Bienvenido1.java
Si el programa no contiene errores de sintaxis, el comando anterior crea un nuevo archivo llamado Bienvenido1.
class (conocido como el archivo de clase para Bienvenido1), el cual contiene los códigos de bytes de Java que
representan nuestra aplicación. Cuando utilicemos el comando java para ejecutar la aplicación, la JVM ejecutará
estos códigos de bytes.
Tip para prevenir errores 2.3
Cuando trate de compilar un programa, si recibe un mensaje como “comando o nombre de archivo inco-
rrecto”, “javac: comando no encontrado” o “'javac ' no se reconoce como un comando interno
o externo, programa o archivo por lotes ejecutable”, entonces su instalación del software Java no
se completó apropiadamente. Con el JDK, esto indica que la variable de entorno PATH del sistema no se estableció
apropiadamente. Consulte cuidadosamente las instrucciones de instalación en la sección Antes de empezar de este
libro. En algunos sistemas, después de corregir la variable PATH, es probable que necesite reiniciar su equipo o abrir
una nueva ventana de comandos para que estos ajustes tengan efecto.
Tip para prevenir errores 2.4
Cuando la sintaxis de un programa es incorrecta, el compilador de Java genera mensajes de error de sintaxis; éstos
contienen el nombre de archivo y el número de línea en donde ocurrió el error. Por ejemplo, Bienvenido1.java:6
indica que ocurrió un error en el archivo Bienvenido1.java en la línea 6. El resto del mensaje proporciona infor-
mación acerca del error de sintaxis.
Tip para prevenir errores 2.5
El mensaje de error del compilador “Public class NombreClase must be defined in a file called
NombreClase.java” indica que el nombre del archivo no coincide exactamente con el nombre de la clase public
en el archivo, o que escribió el nombre de la clase en forma incorrecta al momento de compilarla.
La figura 2.2 muestra el programa de la figura 2.1 ejecutándose en una ventana Símbolo del sistema de
Microsoft® Windows® XP. Para ejecutar el programa, escriba java Bienvenido1; posteriormente se iniciará la
JVM, que cargará el archivo “.class” para la clase Bienvenido1. Observe que la extensión “.class” del nombre
de archivo se omite en el comando anterior; de no ser así, la JVM no ejecutaría el programa. La JVM llama al
método main. A continuación, la instrucción de la línea 9 de main muestra "Bienvenido a la programacion
en Java!" [Nota: muchos entornos muestran los símbolos del sistema con fondos negros y texto blanco. En
nuestro entorno, ajustamos esta configuración para que nuestras capturas de pantalla fueran más legibles].
2.3 Modificación de nuestro primer programa en Java 41
Tip para prevenir errores 2.6
Al tratar de ejecutar un programa en Java, si recibe el mensaje “Exception in thread "main" java.1ang.
NoC1assDefFoundError: Bienvenido1”, quiere decir que su variable de entorno CLASSPATH no se ha configu-
rado apropiadamente. Consulte cuidadosamente las instrucciones de instalación en la sección Antes de empezar de
este libro. En algunos sistemas, tal vez necesite reiniciar su equipo o abrir una nueva ventana de comandos para que
estos ajustes tengan efecto.
2.3 Modificación de nuestro primer programa en Java
Esta sección continúa con nuestra introducción a la programación en Java, con dos ejemplos que modifican el
ejemplo de la figura 2.1 para imprimir texto en una línea utilizando varias instrucciones, y para imprimir texto en
varias líneas utilizando una sola instrucción.
Cómo mostrar una sola línea de texto con varias instrucciones
Bienvenido a la programacion en Java! puede mostrarse en varias formas. La clase Bienvenido2, que se
muestra en la figura 2.3, utiliza dos instrucciones para producir el mismo resultado que el de la figura 2.1. De
aquí en adelante, resaltaremos las características nuevas y las características clave en cada listado de código, como
se muestra en las línea 9 a 10 de este programa.
El programa imprime en la pantalla
Bienvenido a la programacion en Java!
Figura 2.2 | Ejecución de Bienvenido1 en una ventana Símbolo del sistema de Microsoft Windows XP.
1 // Fig. 2.3: Bienvenido2.java
2 // Imprimir una línea de texto con varias instrucciones.
3
4 public class Bienvenido2
5 {
6 // el método main empieza la ejecución de la aplicación en Java
7 public static void main( String args[] )
8 {
9 System.out.print( "Bienvenido a " );
10 System.out.println( "la programacion en Java!" );
11
12 } // fin del método main
13
14 } // fin de la clase Bienvenido2
Figura 2.3 | Impresión de una línea de texto con varias instrucciones.
Bienvenido a la programacion en Java!
Usted escribe este comando
para ejecutar la aplicación
42 Capítulo 2 Introducción a las aplicaciones en Java
El programa es similar al de la figura 2.1, por lo que aquí sólo hablaremos de los cambios. La línea 2
// Imprimir una línea de texto con varias instrucciones.
es un comentario de fin de línea que describe el propósito de este programa. La línea 4 comienza la declaración
de la clase Bienvenido2.
Las líneas 9 y 10 del método main
System.out.print( "Bienvenido a " );
System.out.println( "la programacion en Java!" );
muestran una línea de texto en la ventana de comandos. La primera instrucción utiliza el método print de
System.out para mostrar una cadena. A diferencia de println, después de mostrar su argumento, print no
posiciona el cursor de salida al inicio de la siguiente línea en la ventana de comandos; sino que el siguiente carác-
ter aparecerá inmediatamente después del último que muestre print. Por lo tanto, la línea 10 coloca el primer
carácter de su argumento (la letra “l”) inmediatamente después del último que muestra la línea 9 (el carácter de
espacio antes del carácter de comilla doble de cierre de la cadena). Cada instrucción print o println continúa
mostrando caracteres a partir de donde la última instrucción print o println dejó de mostrar caracteres.
Cómo mostrar varias líneas de texto con una sola instrucción
Una sola instrucción puede mostrar varias líneas, utilizando caracteres de nueva línea, los cuales indican a los
métodos print y println de System.out cuándo deben colocar el cursor de salida al inicio de la siguiente línea
en la ventana de comandos. Al igual que las líneas en blanco, los espacios y los tabuladores, los caracteres de nueva
línea son caracteres de espacio en blanco. La figura 2.4 muestra cuatro líneas de texto, utilizando caracteres de
nueva línea para determinar cuándo empezar cada nueva línea. La mayor parte del programa es idéntico a los
de las figuras 2.1 y 2.3, por lo que aquí sólo veremos los cambios.
La línea 2
// Imprimir varias líneas de texto con una sola instrucción.
es un comentario que describe el propósito de este programa. La línea 4 comienza la declaración de la clase Bien-
venido3.
La línea 9
System.out.println( "Bienvenidonanla programacionnenJava!" );
muestra cuatro líneas separadas de texto en la ventana de comandos. Por lo general, los caracteres en una cadena
se muestran exactamente como aparecen en las comillas dobles. Sin embargo, observe que los dos caracteres
1 // Fig. 2.4: Bienvenido3.java
2 // Imprimir varias líneas de texto con una sola instrucción.
3
4 public class Bienvenido3
5 {
6 // el método main empieza la ejecución de la aplicación en Java
7 public static void main( String args[] )
8 {
9 System.out.println( "Bienvenidonanla programacionnen Java!" );
10
11 } // fin del método main
12
13 } // fin de la clase Bienvenido3
Figura 2.4 | Impresión de varias líneas de texto con una sola instrucción.
Bienvenido
a
la programacion
en Java!
2.4 Cómo mostrar texto con printf 43
 y n (que se repiten tres veces en la instrucción) no aparecen en la pantalla. La barra diagonal inversa () se
conoce como carácter de escape. Este carácter indica a los métodos print y println de System.out que se va
a imprimir un “carácter especial”. Cuando aparece una barra diagonal inversa en una cadena de caracteres, Java
combina el siguiente carácter con la barra diagonal inversa para formar una secuencia de escape. La secuencia
de escape n representa el carácter de nueva línea. Cuando aparece un carácter de nueva línea en una cadena que
se va a imprimir con System.out, el carácter de nueva línea hace que el cursor de salida de la pantalla se despla-
ce al inicio de la siguiente línea en la ventana de comandos. En la figura 2.5 se enlistan varias secuencias de escape
comunes, con descripciones de cómo afectan la manera de mostrar caracteres en la ventana de comandos. Para
obtener una lista completa de secuencias de escape, visite java.sun.com/docs/books/jls/third_edition/
html/lexical.html#3.10.6.
2.4 Cómo mostrar texto con printf
Java SE 5.0 agregó el método System.out.printf para mostrar datos con formato; la f en el nombre printf
representa la palabra “formato”. La figura 2.6 muestra las cadenas "Bienvenido a" y "la programacion en
Java!" con System.out.printf.
Las líneas 9 y 10
System.out.printf( "%sn%sn",
"Bienvenido a", "la programacion en Java!" );
llaman al método System.out.printf para mostrar la salida del programa. La llamada al método especifica tres
argumentos. Cuando un método requiere varios argumentos, éstos se separan con comas (,); a esto se le conoce
como lista separada por comas.
Buena práctica de programación 2.9
Coloque un espacio después de cada coma (,) en una lista de argumentos, para que sus programas sean más legi-
bles.
Recuerde que todas las instrucciones en Java terminan con un punto y coma (;). Por lo tanto, las líneas 9
y 10 sólo representan una instrucción. Java permite que las instrucciones largas se dividan en varias líneas. Sin
embargo, no puede dividir una instrucción a la mitad de un identificador, o de una cadena.
Error común de programación 2.7
Dividir una instrucción a la mitad de un identificador o de una cadena es un error de sintaxis.
Secuencia
de escape Descripción
n Nueva línea. Coloca el cursor de la pantalla al inicio de la siguiente línea.
t Tabulador horizontal. Desplaza el cursor de la pantalla hasta la siguiente posición de tabulación.
r
Retorno de carro. Coloca el cursor de la pantalla al inicio de la línea actual; no avanza a la siguiente línea.
Cualquier carácter que se imprima después del retorno de carro sobrescribe los caracteres previamente
impresos en esa línea.
 Barra diagonal inversa. Se usa para imprimir un carácter de barra diagonal inversa.
” Doble comilla. Se usa para imprimir un carácter de doble comilla. Por ejemplo,
System.out.println( ""entre comillas"" );
muestra
"entre comillas"
Figura 2.5 | Algunas secuencias de escape comunes.
44 Capítulo 2 Introducción a las aplicaciones en Java
El primer argumento del método printf es una cadena de formato que puede consistir en texto fijo y espe-
cificadores de formato. El método printf imprime el texto fijo de igual forma que print o println. Cada es-
pecificador de formato es un receptáculo para un valor, y especifica el tipo de datos a imprimir. Los especificadores
de formato también pueden incluir información de formato opcional.
Los especificadores de formato empiezan con un signo porcentual (%) y van seguidos de un carácter que
representa el tipo de datos. Por ejemplo, el especificador de formato %s es un receptáculo para una cadena. La
cadena de formato en la línea 9 especifica que printf debe imprimir dos cadenas, y que a cada cadena le debe
seguir un carácter de nueva línea. En la posición del primer especificador de formato, printf sustituye el valor
del primer argumento después de la cadena de formato. En cada posición posterior de los especificadores de
formato, printf sustituye el valor del siguiente argumento en la lista. Así, este ejemplo sustituye "Bienvenido
a" por el primer %s y "la programacion en Java!" por el segundo %s. La salida muestra que se imprimieron
dos líneas de texto.
En nuestros ejemplos, presentaremos las diversas características de formato a medida que se vayan necesitan-
do. El capítulo 29 presenta los detalles de cómo dar formato a la salida con printf.
2.5 Otra aplicación en Java: suma de enteros
Nuestra siguiente aplicación lee (o recibe como entrada) dos enteros (números completos, como –22, 7, 0 y
1024) introducidos por el usuario mediante el teclado, calcula la suma de los valores y muestra el resultado.
Este programa debe llevar la cuenta de los números que suministra el usuario para los cálculos que el programa
realiza posteriormente. Los programas recuerdan números y otros datos en la memoria de la computadora, y
acceden a esos datos a través de elementos del programa, conocidos como variables. El programa de la figura 2.7
demuestra estos conceptos. En los resultados de ejemplo, resaltamos las diferencias entre la entrada del usuario
y la salida del programa.
1 // Fig. 2.6: Bienvenido4.java
2 // Imprimir varias líneas en un cuadro de diálogo.
3
4 public class Bienvenido4
5 {
6 // el método main empieza la ejecución de la aplicación de Java
7 public static void main( String args[] )
8 {
9 System.out.printf( "%sn%sn",
10 "Bienvenido a", "la programacion en Java!" );
11
12 } // fin del método main
13
14 } // fin de la clase Bienvenido4
Figura 2.6 | Imprimir varias líneas de texto con el método System.out.printf.
Bienvenido a
la programacion en Java!
1 // Fig. 2.7: Suma.java
2 // Programa que muestra la suma de dos enteros.
3 import java.util.Scanner; // el programa usa la clase Scanner
4
5 public class Suma
6 {
7 // el método main empieza la ejecución de la aplicación en Java
Figura 2.7 | Programa que muestra la suma de dos enteros. (Parte 1 de 2).
2.5 Otra aplicación en Java: suma de enteros 45
Las líneas 1 y 2
// Fig. 2.7: Suma.java
// Programa que muestra la suma de dos enteros.
indican el número de la figura, el nombre del archivo y el propósito del programa. La línea 3
import java.util.Scanner; // el programa usa la clase Scanner
es una declaración import que ayuda al compilador a localizar una clase que se utiliza en este programa. Una
gran fortaleza de Java es su extenso conjunto de clases predefinidas que podemos utilizar, en vez de “reinventar
la rueda”. Estas clases se agrupan en paquetes (colecciones con nombre de clases relacionadas) y se conocen en
conjunto como la biblioteca de clases de Java, o Interfaz de Programación de Aplicaciones de Java (API de
Java). Los programadores utilizan declaraciones import para identificar las clases predefinidas que se utilizan en
un programa de Java. La declaración import en la línea 3 indica que este ejemplo utiliza la clase Scanner pre-
definida de Java (que veremos en breve) del paquete java.util. Después, el compilador trata de asegurarse que
utilicemos la clase Scanner de manera apropiada.
Error común de programación 2.8
Todas las declaraciones import deben aparecer antes de la primera declaración de clase en el archivo. Colocar una
declaración import dentro del cuerpo de la declaración de una clase, o después de la declaración de la misma, es un
error de sintaxis.
Tip para prevenir errores 2.7
Por lo general, si olvida incluir una declaración import para una clase que utilice en su programa, se produce un
error de compilación que contiene el mensaje: “cannot resolve symbol”. Cuando esto ocurra, verifique que haya
proporcionado las declaraciones import apropiadas y que los nombres en las mismas estén escritos correctamente,
incluyendo el uso apropiado de las letras mayúsculas y minúsculas.
Figura 2.7 | Programa que muestra la suma de dos enteros. (Parte 2 de 2).
8 public static void main( String args[] )
9 {
10 // crea objeto Scanner para obtener la entrada de la ventana de comandos
11 Scanner entrada = new Scanner( System.in );
12
13 int numero1; // primer número a sumar
14 int numero2; // segundo número a sumar
15 int suma; // suma de numero1 y numero2
16
17 System.out.print( "Escriba el primer entero: " ); // indicador
18 numero1 = entrada.nextInt(); // lee el primer número del usuario
19
20 System.out.print( "Escriba el segundo entero: " ); // indicador
21 numero2 = entrada.nextInt(); // lee el segundo número del usuario
22
23 suma = numero1 + numero2; // suma los números
24
25 System.out.printf( "La suma es %dn", suma ); // muestra la suma
26
27 } // fin del método main
28
29 } // fin de la clase Suma
Escriba el primer entero: 45
Escriba el segundo entero: 72
La suma es 117
46 Capítulo 2 Introducción a las aplicaciones en Java
La línea 5
public class Suma
empieza la declaración de la clase Suma. El nombre de archivo para esta clase public debe ser Suma.java. Recuer-
de que el cuerpo de cada declaración de clase empieza con una llave izquierda de apertura (línea 6) ({) y termina
con una llave derecha de cierre (línea 29) (}).
La aplicación empieza a ejecutarse con el método main (líneas 8 a la 27). La llave izquierda (línea 9) marca
el inicio del cuerpo de main, y la correspondiente llave derecha (línea 27) marca el final de main. Observe que al
método main se le aplica un nivel de sangría en el cuerpo de la clase Suma, y que al código en el cuerpo de main
se le aplica otro nivel para mejorar la legibilidad.
La línea 11
Scanner entrada = new Scanner( System.in );
es una instrucción de declaración de variable (también conocida como declaración), la cual especifica el nom-
bre (entrada) y tipo (Scanner) de una variable utilizada en este programa. Una variable es una ubicación en la
memoria de la computadora, en donde se puede guardar un valor para utilizarlo posteriormente en un programa.
Todas las variables deben declararse con un nombre y un tipo antes de poder usarse; este nombre permite al
programa acceder al valor de la variable en memoria; y puede ser cualquier identificador válido. (Consulte en la
sección 2.2 los requerimientos para nombrar identificadores). El tipo de una variable especifica el tipo de infor-
mación que se guarda en esa ubicación de memoria. Al igual que las demás instrucciones, las instrucciones de
declaración terminan con punto y coma (;).
La declaración en la línea 11 especifica que la variable llamada entrada es de tipo Scanner. Un objeto
Scanner permite a un programa leer datos (como números) para usarlos. Los datos pueden provenir de muchas
fuentes, como un archivo en disco, o desde el teclado. Antes de usar un objeto Scanner, el programa debe crearlo
y especificar el origen de los datos.
El signo igual (=) en la línea 11 indica que la variable entrada tipo Scanner debe inicializarse (es decir,
hay que prepararla para usarla en el programa) en su declaración con el resultado de la expresión new Scanner
( System.in ) a la derecha del signo igual. Esta expresión crea un objeto Scanner que lee los datos escritos por
el usuario mediante el teclado. Recuerde que el objeto de salida estándar, System.out, permite a las aplicaciones
de Java mostrar caracteres en la ventana de comandos. De manera similar, el objeto de entrada estándar, Sys-
tem.in, permite a las aplicaciones de Java leer la información escrita por el usuario. Así, la línea 11 crea un objeto
Scanner que permite a la aplicación leer la información escrita por el usuario mediante el teclado.
Las instrucciones de declaración de variables en las líneas 13 a la 15
int numero1; // primer número a sumar
int numero2; // segundo número a sumar
int suma; // suma de numero1 y numero2
declaran que las variables numero1, numero2 y suma contienen datos de tipo int; estas variables pueden contener
valores enteros (números completos, como 7, –11, 0 y 31,914). Estas variables no se han inicializado todavía. El
rango de valores para un int es de –2,147,483,648 a +2,147,483,647. Pronto hablaremos sobre los tipos float y
double, para guardar números reales, y sobre el tipo char, para guardar datos de caracteres. Los números reales
son números que contienen puntos decimales, como 3.4, 0.0 y –11.19. Las variables de tipo char representan
caracteres individuales, como una letra en mayúscula (como A), un dígito (como 7), un carácter especial (como *
o %) o una secuencia de escape (como el carácter de nueva línea, n). Los tipos como int, float, double y char
se conocen como tipos primitivos o tipos integrados. Los nombres de los tipos primitivos son palabras clave y,
por lo tanto, deben aparecer completamente en minúsculas. El apéndice D, Tipos primitivos, sintetiza las carac-
terísticas de los ocho tipos primitivos (boolean, byte, char, short, int, long, float y double).
Las instrucciones de declaración de variables pueden dividirse en varias líneas, separando los nombres de las
variables por comas (es decir, una lista de nombres de variables separados por comas). Varias variables del mismo
tipo pueden declararse en una, o en varias declaraciones. Por ejemplo, las líneas 13 a la 15 se pueden escribir como
una sola instrucción, de la siguiente manera:
int numero1, // primer número a sumar
numero2, // segundo número a sumar
suma; // suma de numero1 y numero2
2.5 Otra aplicación en Java: suma de enteros 47
Observe que utilizamos comentarios de fin de línea en las líneas 13 a la 15. Este uso de comentarios es una prác-
tica común de programación, para indicar el propósito de cada variable en el programa.
Buena práctica de programación 2.10
Declare cada variable en una línea separada. Este formato permite insertar fácilmente un comentario descriptivo a
continuación de cada declaración.
Buena práctica de programación 2.11
Seleccionar nombres de variables significativos ayuda a que un programa se autodocumente (es decir, que sea más
fácil entender con sólo leerlo, en lugar de leer manuales o ver un número excesivo de comentarios).
Buena práctica de programación 2.12
Por convención, los identificadores de nombre de variables empiezan con una letra minúscula, y cada una de las
palabras en el nombre, que van después de la primera, deben empezar con una letra mayúscula. Por ejemplo, el
identificador primerNumero tiene una N mayúscula en su segunda palabra, Numero.
La línea 17
System.out.print( "Escriba el primer entero: " ); // indicador
utiliza System.out.print para mostrar el mensaje "Escriba el primer entero: ". Este mensaje se conoce
como indicador, ya que indica al usuario que debe realizar una acción específica. En la sección 2.2 vimos que los
identificadores que empiezan con letras mayúsculas representan nombres de clases. Por lo tanto, System es una
clase; que forma parte del paquete java.lang. Observe que la clase System no se importa con una declaración
import al principio del programa.
Observación de ingeniería de software 2.1
El paquete java.lang se importa de manera predeterminada en todos los programas de Java; por ende, las clases en
java.lang son las únicas en la API que no requieren una declaración import.
La línea 18
numero1 = entrada.nextInt(); // lee el primer número del usuario
utiliza el método nextInt del objeto entrada de la clase Scanner para obtener un entero del usuario mediante
el teclado. En este punto, el programa espera a que el usuario escriba el número y oprima Intro para enviar el
número al programa.
Técnicamente, el usuario puede escribir cualquier cosa como valor de entrada. Nuestro programa asume que
el usuario escribirá un valor de entero válido, según las indicaciones; si el usuario escribe un valor no entero, se
producirá un error lógico en tiempo de ejecución y el programa no funcionará correctamente. El capítulo 13,
Manejo de excepciones, habla sobre cómo hacer sus programas más robustos, al permitirles manejar dichos erro-
res. Esto también se conoce como hacer que su programa sea tolerante a fallas.
En la línea 18, el resultado de la llamada al método nextInt (un valor int) se coloca en la variable nume-
ro1 mediante el uso del operador de asignación, =. La instrucción se lee como “numero1 obtiene el valor de
entrada.nextInt()”. Al operador = se le llama operador binario, ya que tiene dos operandos: numero1 y el
resultado de la llamada al método entrada.nextInt(). Esta instrucción se llama instrucción de asignación, ya
que asigna un valor a una variable. Todo lo que está a la derecha del operador de asignación (=) se evalúa siempre
antes de realizar la asignación.
Buena práctica de programación 2.13
Coloque espacios en cualquier lado de un operador binario, para que resalte y el programa sea más legible.
La línea 20
System.out.print( "Escriba el segundo entero: " ); // indicador
48 Capítulo 2 Introducción a las aplicaciones en Java
pide al usuario que escriba el segundo entero.
La línea 21
numero2 = entrada.nextInt(); // lee el segundo número del usuario
lee el segundo entero y lo asigna a la variable numero2.
La línea 23
suma = numero1 + numero2; // suma los números
es una instrucción de asignación que calcula la suma de las variables numero1 y numero2, y asigna el resultado a
la variable suma mediante el uso del operador de asignación, =. La instrucción se lee como “suma obtiene el valor
de numero1 + numero2”. La mayoría de los cálculos se realizan en instrucciones de asignación. Cuando el pro-
grama encuentra la operación de suma, utiliza los valores almacenados en las variables numero1 y numero2 para
realizar el cálculo. En la instrucción anterior, el operador de suma es binario; sus dos operandos son numero1 y
numero2. Las partes de las instrucciones que contienen cálculos se llaman expresiones. De hecho, una expresión
es cualquier parte de una instrucción que tiene un valor asociado. Por ejemplo, el valor de la expresión numero1
+ numero2 es la suma de los números. De manera similar, el valor de la expresión entrada.nextInt() es un
entero escrito por el usuario.
Una vez realizado el cálculo, la línea 25
System.out.printf( "La suma es %dn", suma ); // muestra la suma
utiliza el método System.out.printf para mostrar la suma. El especificador de formato %d es un receptáculo
para un valor int (en este caso, el valor de suma); la letra d representa “entero decimal”. Observe que aparte del
especificador de formato %d, el resto de los caracteres en la cadena de formato son texto fijo. Por lo tanto, el méto-
do printf imprime en pantalla "La suma es ", seguido del valor de suma (en la posición del especificador de
formato %d) y una nueva línea.
Observe que los cálculos también pueden realizarse dentro de instrucciones printf. Podríamos haber com-
binado las instrucciones de las líneas 23 y 25 en la siguiente instrucción:
System.out.printf( "La suma es %dn", ( numero1 + numero2 ) );
Los paréntesis alrededor de la expresión numero1 + numero2 no son requeridos; se incluyen para enfatizar que el
valor de la expresión se imprime en la posición del especificador de formato %d.
Documentación de la API de Java
Para cada nueva clase de la API de Java que utilizamos, indicamos el paquete en el que se ubica. Esta información
es importante, ya que nos ayuda a localizar las descripciones de cada paquete y clase en la documentación de la
API de Java. Puede encontrar una versión basada en Web de esta documentación en
java.sun.com/javase/6/docs/api/
También puede descargar esta documentación, en su propia computadora, de
java.sun.com/javase/downloads/ea.jsp
La descarga es de aproximadamente 53 megabytes (MB). El apéndice J, Uso de la documentación de la API de
Java, describe cómo utilizar esta documentación.
2.6 Conceptos acerca de la memoria
Los nombres de variables como numero1, numero2 y suma en realidad corresponden a ciertas ubicaciones en la
memoria de la computadora. Toda variable tiene un nombre, un tipo, un tamaño y un valor.
En el programa de suma de la figura 2.7, cuando se ejecuta la instrucción (línea 18)
numero1 = entrada.nextInt(); // lee el primer número del usuario
el número escrito por el usuario se coloca en una ubicación de memoria a la cual se asigna el nombre numero1.
Suponga que el usuario escribe 45. La computadora coloca ese valor entero en la ubicación numero1, como se
2.7 Aritmética 49
muestra en la figura 2.8. Cada vez que se coloca un nuevo valor en una ubicación de memoria, se sustituye al valor
anterior en esa ubicación; es decir, el valor anterior se pierde.
Cuando se ejecuta la instrucción (línea 21)
numero2 = entrada.nextInt(); // lee el segundo número del usuario
suponga que el usuario escribe 72. La computadora coloca ese valor entero en la ubicación numero2. La memoria
ahora aparece como se muestra en la figura 2.9.
Una vez que el programa de la figura 2.7 obtiene valores para numero1 y numero2, los suma y coloca el resul-
tado en la variable suma. La instrucción (línea 23)
suma = numero1 + numero2; // suma los números
realiza la suma y después sustituye el valor anterior de suma. Una vez que se calcula suma, la memoria aparece
como se muestra en la figura 2.10. Observe que los valores de numero1 y numero2 aparecen exactamente como
antes de usarlos en el cálculo de suma. Estos valores se utilizaron, pero no se destruyeron, cuando la computadora
realizó el cálculo. Por ende, cuando se lee un valor de una ubicación de memoria, el proceso es no destructivo.
2.7 Aritmética
La mayoría de los programas realizan cálculos aritméticos. Los operadores aritméticos se sintetizan en la figura
2.11. Observe el uso de varios símbolos especiales que no se utilizan en álgebra. El asterisco (*) indica la multi-
plicación, y el signo de porcentaje (%) es el operador residuo (conocido como módulo en algunos lenguajes), el
cual describiremos en breve. Los operadores aritméticos en la figura 2.11 son binarios, ya que funcionan con dos
operandos. Por ejemplo, la expresión f + 7 contiene el operador binario + y los dos operandos f y 7.
La división de enteros produce un cociente entero: por ejemplo, la expresión 7 / 4 da como resultado 1, y
la expresión 17 / 5 da como resultado 3. Cualquier parte fraccionaria en una división de enteros simplemente se
descarta (es decir, se trunca); no ocurre un redondeo. Java proporciona el operador residuo, %, el cual produce el
residuo después de la división. La expresión x % y produce el residuo después de que x se divide entre y. Por lo
tanto, 7 % 4 produce 3, y 17 % 5 produce 2. Por lo general, este operador se utiliza más con operandos enteros,
pero también puede usarse con otros tipos aritméticos. En los ejercicios de este capítulo y de capítulos posterio-
res, consideramos muchas aplicaciones interesantes del operador residuo, como determinar si un número es
múltiplo de otro.
Figura 2.8 | Ubicación de memoria que muestra el nombre y el valor de la variable numero1.
Figura 2.9 | Ubicaciones de memoria, después de almacenar valores para numero1 y numero2.
Figura 2.10 | Ubicaciones de memoria, después de almacenar la suma de numero1 y numero2.
45
72
117
numero1
numero2
suma
45
72
numero1
numero2
45
numero1
50 Capítulo 2 Introducción a las aplicaciones en Java
Expresiones aritméticas en formato de línea recta
Las expresiones aritméticas en Java deben escribirse en formato de línea recta para facilitar la escritura de pro-
gramas en la computadora. Por lo tanto, las expresiones como “a dividida entre b” deben escribirse como a / b,
de manera que todas las constantes, variables y operadores aparezcan en una línea recta. La siguiente notación
algebraica no es generalmente aceptable para los compiladores:
a
b
Paréntesis para agrupar subexpresiones
Los paréntesis se utilizan para agrupar términos en las expresiones en Java, de la misma manera que en las expre-
siones algebraicas. Por ejemplo, para multiplicar a por la cantidad b + c, escribimos
a * ( b + c )
Si una expresión contiene paréntesis anidados, como
( ( a + b ) * c )
se evalúa primero la expresión en el conjunto más interno de paréntesis (a + b en este caso).
Reglas de precedencia de operadores
Java aplica los operadores en expresiones aritméticas en una secuencia precisa, determinada por las siguientes
reglas de precedencia de operadores, que generalmente son las mismas que las que se utilizan en álgebra
(figura 2.12):
1. Las operaciones de multiplicación, división y residuo se aplican primero. Si una expresión contiene
varias de esas operaciones, los operadores se aplican de izquierda a derecha. Los operadores de multipli-
cación, división y residuo tienen el mismo nivel de precedencia.
2. Las operaciones de suma y resta se aplican a continuación. Si una expresión contiene varias de esas
operaciones, los operadores se aplican de izquierda a derecha. Los operadores de suma y resta tienen el
mismo nivel de precedencia.
Operación
en Java
Operador
aritmético
Expresión
algebraica
Expresión
en Java
Suma + f + 7 f + 7
Resta - p — c p — c
Multiplicación * bm b * m
División / x / y
Residuo % r mod s r % s
Operador(es) Operación(es) Orden de evaluación (precedencia)
* Multiplicación Se evalúan primero. Si hay varios operadores de este tipo, se evalúan de
izquierda a derecha.
/ División
% Residuo
+ Suma Se evalúan después. Si hay varios operadores de este tipo, se evalúan de
izquierda a derecha.
- Resta
Figura 2.12 | Precedencia de los operadores aritméticos.
Figura 2.11 | Operadores aritméticos.
x/y o o x ∏y
x
y
-
-
2.7 Aritmética 51
Estas reglas permiten a Java aplicar los operadores en el orden correcto. Cuando decimos que los operadores
se aplican de izquierda a derecha, nos referimos a su asociatividad; veremos que algunos se asocian de derecha a
izquierda. La figura 2.12 sintetiza estas reglas de precedencia de operadores; esta tabla se expandirá a medida que
se introduzcan más operadores en Java. En el apéndice A, Tabla de precedencia de los operadores, se incluye una
tabla de precedencias completa.
Ejemplos de expresiones algebraicas y de Java
Ahora, consideremos varias expresiones en vista de las reglas de precedencia de operadores. Cada ejemplo enlista
una expresión algebraica y su equivalente en Java. El siguiente es un ejemplo de una media (promedio) aritmética
de cinco términos:
Álgebra:
Java:
a + b + c + d + e
5
m = ( a + b + c + d + e ) / 5;
m =
Los paréntesis son obligatorios, ya que la división tiene una mayor precedencia que la suma. La cantidad completa
( a + b + c + d + e ) va a dividirse entre 5. Si por error se omiten los paréntesis, obtenemos a + b + c +
d + e / 5, lo cual da como resultado
a + b + c + d +
5
e
El siguiente es un ejemplo de una ecuación de línea recta:
Álgebra: y = mx + b
Java: y = m * x + b;
No se requieren paréntesis. El operador de multiplicación se aplica primero, ya que la multiplicación tiene mayor
precedencia sobre la suma. La asignación ocurre al último, ya que tiene menor precedencia que la multiplicación
o suma.
El siguiente ejemplo contiene las operaciones residuo (%), multiplicación, división, suma y resta:
Álgebra: z = pr%q + w/x – y
Java: z = p * r % q + w / x – y;
6 1 2 4 3 5
Los números dentro de los círculos bajo la instrucción, indican el orden en el que Java aplica los operadores. Las
operaciones de multiplicación, residuo y división se evalúan primero, en orden de izquierda a derecha (es decir, se
asocian de izquierda a derecha), ya que tienen mayor precedencia que la suma y la resta. Las operaciones de suma
y resta se evalúan a continuación; estas operaciones también se aplican de izquierda a derecha.
Evaluación de un polinomio de segundo grado
Para desarrollar una mejor comprensión de las reglas de precedencia de operadores, considere la evaluación de un
polinomio de segundo grado (y = ax2 + bx + c):
y = a * x * x + b * x + c;
6 1 2 4 3 5
Los números dentro de los círculos indican el orden en el que Java aplica los operadores. Las operaciones de
multiplicación se evalúan primero en orden de izquierda a derecha (es decir, se asocian de izquierda a derecha),
ya que tienen mayor precedencia que la suma. Las operaciones de suma se evalúan a continuación y se aplican de
izquierda a derecha. No existe un operador aritmético para la potencia de un número en Java, por lo que x2 se
representa como x * x. La sección 5.4 muestra una alternativa para calcular la potencia de un número en Java.
52 Capítulo 2 Introducción a las aplicaciones en Java
Suponga que a, b, c y x en el polinomio de segundo grado anterior se inicializan (reciben valores) como
sigue: a = 2, b = 3, c = 7 y x = 5. La figura 2.13 muestra el orden en el que se aplican los operadores.
Al igual que en álgebra, es aceptable colocar paréntesis innecesarios en una expresión para hacer que ésta
sea más clara. A dichos paréntesis se les llama paréntesis redundantes. Por ejemplo, la instrucción de asignación
anterior podría colocarse entre paréntesis, de la siguiente manera:
y = ( a * x * x ) + ( b * x ) + c;
Buena práctica de programación 2.14
El uso de paréntesis para las expresiones aritméticas complejas, incluso cuando éstos no sean necesarios, puede hacer
que las expresiones aritméticas sean más fáciles de leer.
2.8 Toma de decisiones: operadores de igualdad y relacionales
Una condición es una expresión que puede ser verdadera (true) o falsa (false). Esta sección presenta la ins-
trucción if de Java, la cual permite que un programa tome una decisión, con base en el valor de una condición.
Por ejemplo, la condición “calificación es mayor o igual que 60” determina si un estudiante pasó o no una prueba.
Si la condición en una instrucción if es verdadera, el cuerpo de la instrucción if se ejecuta. Si la condición es
falsa, el cuerpo no se ejecuta. Veremos un ejemplo en breve.
Las condiciones en las instrucciones if pueden formarse utilizando los operadores de igualdad (== y !=)
y los operadores relacionales (>, <, >= y <=) que se sintetizan en la figura 2.14. Ambos operadores de igualdad
tienen el mismo nivel de precedencia, que es menor que la precedencia de los operadores relacionales. Los ope-
radores de igualdad se asocian de izquierda a derecha; y los relacionales tienen el mismo nivel de precedencia y
también se asocian de izquierda a derecha.
La aplicación de la figura 2.15 utiliza seis instrucciones if para comparar dos enteros introducidos por el
usuario. Si la condición en cualquiera de estas instrucciones if es verdadera, se ejecuta la instrucción de asigna-
ción asociada. El programa utiliza un objeto Scanner para recibir los dos enteros del usuario y almacenarlos en
las variables numero1 y numero2. Después, compara los números y muestra los resultados de las comparaciones
que son verdaderas.
Figura 2.13 | Orden en el cual se evalúa un polinomio de segundo grado.
(Multiplicación de más a la izquierda)
(Multiplicación de más a la izquierda)
(Multiplicación antes de la suma)
(Suma de más a la izquierda)
(Última suma)
(Última operación; colocar 72 en y)
Paso 1. y = 2 * 5 * 5 + 3 * 5 + 7;
2 * 5 es 10
Paso 2. y = 10 * 5 + 3 * 5 + 7;
10 * 5 es 50
Paso 3. y = 50 + 3 * 5 + 7;
3 * 5 es 15
Paso 4. y = 50 + 15 + 7;
50 + 15 es 65
Paso 5. y = 65 + 7;
65 + 7 es 72
Paso 6. y = 72
2.8 Toma de decisiones: operadores de igualdad y relacionales 53
Operador estándar
algebraico de igualdad
o relacional
Operador de
igualdad o
relacional de Java
Ejemplo
de condición
en Java
Significado de la
condición en Java
Operadores de igualdad
= == x == y x es igual a y
≠ != x != y x no es igual a y
Operadores relacionales
> > x > y x es mayor que y
< < x < y x es menor que y
≥ >= x >= y x es mayor o igual que y
≤ <= x <= y x es menor o igual que y
1 // Fig. 2.15: Comparacion.java
2 // Compara enteros utilizando instrucciones if, operadores relacionales
3 // y de igualdad.
4 import java.util.Scanner; // el programa utiliza la clase Scanner
5
6 public class Comparacion
7 {
8 // el método main empieza la ejecución de la aplicación en Java
9 public static void main( String args[] )
10 {
11 // crea objeto Scanner para obtener la entrada de la ventana de comandos
12 Scanner entrada = new Scanner( System.in );
13
14 int numero1; // primer número a comparar
15 int numero2; // segundo número a comparar
16
17 System.out.print( "Escriba el primer entero: " ); // indicador
18 numero1 = entrada.nextInt(); // lee el primer número del usuario
19
20 System.out.print( “Escriba el segundo entero: “ ); // indicador
21 numero2 = entrada.nextInt(); // lee el segundo número del usuario
22
23 if ( numero1 == numero2 )
24 System.out.printf( “%d == %dn”, numero1, numero2 );
25
26 if ( numero1 != numero2 )
27 System.out.printf( “%d != %dn”, numero1, numero2 );
28
29 if ( numero1 < numero2 )
30 System.out.printf( “%d < %dn”, numero1, numero2 );
31
32 if ( numero1 > numero2 )
33 System.out.printf( “%d > %dn”, numero1, numero2 );
34
35 if ( numero1 <= numero2 )
36 System.out.printf( “%d <= %dn”, numero1, numero2 );
Figura 2.15 | Operadores de igualdad y relacionales. (Parte 1 de 2).
Figura 2.14 | Operadores de igualdad y relacionales.
54 Capítulo 2 Introducción a las aplicaciones en Java
La declaración de la clase Comparacion comienza en la línea 6
public class Comparacion
El método main de la clase (líneas 9 a 41) empieza la ejecución del programa. La línea 12
Scanner entrada = new Scanner( System.in );
declara la variable entrada de la clase Scanner y le asigna un objeto Scanner que recibe datos de la entrada
estándar (es decir, el teclado).
Las líneas 14 y 15
int numero1; // primer número a comparar
int numero2; // segundo número a comparar
declaran las variables int que se utilizan para almacenar los valores introducidos por el usuario.
Las líneas 17-18
System.out.print( "Escriba el primer entero: " ); // indicador
numero1 = entrada.nextInt(); // lee el primer número del usuario
piden al usuario que introduzca el primer entero y el valor, respectivamente. El valor de entrada se almacena en
la variable numero1.
Las líneas 20-21
System.out.print( "Escriba el segundo entero: " ); // indicador
numero2 = entrada.nextInt(); // lee el segundo número del usuario
piden al usuario que introduzca el segundo entero y el valor, respectivamente. El valor de entrada se almacena en
la variable numero2.
37
38 if ( numero1 >= numero2 )
39 System.out.printf( “%d >= %dn”, numero1, numero2 );
40
41 } // fin del método main
42
43 } // fin de la clase Comparacion
Figura 2.1 | Programa para imprimir texto.
Escriba el primer entero: 777
Escriba el segundo entero: 777
777 == 777
777 <= 777
777 >= 777
Escriba el primer entero: 1000
Escriba el segundo entero: 2000
1000 != 2000
1000 < 2000
1000 <= 2000
Escriba el primer entero: 2000
Escriba el segundo entero: 1000
2000 != 1000
2000 > 1000
2000 >= 1000
Figura 2.15 | Operadores de igualdad y relacionales. (Parte 2 de 2).
2.8 Toma de decisiones: operadores de igualdad y relacionales 55
Las líneas 23-24
if ( numero1 == numero2 )
System.out.printf( “%d == %dn”, numero1, numero2 );
declaran una instrucción if que compara los valores de las variables numero1 y numero2, para determinar si son
iguales o no. Una instrucción if siempre empieza con la palabra clave if, seguida de una condición entre parén-
tesis. Una instrucción if espera una instrucción en su cuerpo. La sangría de la instrucción del cuerpo que se
muestra aquí no es obligatoria, pero mejora la legibilidad del programa al enfatizar que la instrucción en la línea
24 forma parte de la instrucción if que empieza en la línea 23. La línea 24 sólo se ejecuta si los números alma-
cenados en las variables numero1 y numero2 son iguales (es decir, si la condición es verdadera). Las instrucciones
if en las líneas 26-27, 29-30, 32-33, 35-36 y 38-39 comparan a numero1 y numero2 con los operadores !=, <,
>, <= y >=, respectivamente. Si la condición en cualquiera de las instrucciones if es verdadera, se ejecuta la
instrucción del cuerpo correspondiente.
Error común de programación 2.9
Olvidar los paréntesis izquierdo y/o derecho de la condición en una instrucción if es un error de sintaxis; los parén-
tesis son obligatorios.
Error común de programación 2.10
Confundir el operador de igualdad (==) con el de asignación (=) puede producir un error lógico o de sintaxis. El
operador de igualdad debe leerse como “es igual a”, y el de asignación como “obtiene” u “obtiene el valor de”. Para
evitar confusión, algunas personas leen el operador de igualdad como “doble igual” o “igual igual”.
Error común de programación 2.11
Es un error de sintaxis si los operadores ==, !=, >= y <= contienen espacios entre sus símbolos, como en = =, ! =, >
= y < =, respectivamente.
Error común de programación 2.12
Invertir los operadores !=, >= y <=, como en =!, => y =<, es un error de sintaxis.
Buena práctica de programación 2.15
Aplique sangría al cuerpo de una instrucción if para hacer que resalte y mejorar la legibilidad del programa.
Buena práctica de programación 2.16
Coloque sólo una instrucción por línea en un programa. Este formato mejora la legibilidad del programa.
Observe que no hay punto y coma (;) al final de la primera línea de cada instrucción if. Dicho punto y
coma produciría un error lógico en tiempo de compilación. Por ejemplo,
if ( numero1 == numero2 ); // error lógico
System.out.printf( "%d == %dn", numero1, numero2 );
sería interpretada por Java de la siguiente manera:
if ( numero1 == numero2 )
; // instrucción vacía
System.out.printf( "%d == %dn", numero1, numero2 );
en donde el punto y coma que aparece por sí solo en una línea (que se conoce como instrucción vacía o nula)
es la instrucción que se va a ejecutar si la condición en la instrucción if es verdadera. Al ejecutarse la instrucción
vacía, no se lleva a cabo ninguna tarea en el programa. Éste continúa con la instrucción de salida, que siempre se
56 Capítulo 2 Introducción a las aplicaciones en Java
ejecuta, sin importar que la condición sea verdadera o falsa, ya que la instrucción de salida no forma parte de la
instrucción if.
Error común de programación 2.13
Colocar un punto y coma inmediatamente después del paréntesis derecho de la condición en una instrucción if es,
generalmente, un error lógico.
Observe el uso del espacio en blanco en la figura 2.15. Recuerde que los caracteres de espacio en blanco,
como tabuladores, nuevas líneas y espacios, generalmente son ignorados por el compilador. Por lo tanto, las ins-
trucciones pueden dividirse en varias líneas y pueden espaciarse de acuerdo a las preferencias del programador, sin
afectar el significado de un programa. Es incorrecto dividir identificadores y cadenas. Idealmente las instrucciones
deben mantenerse lo más reducidas que sea posible, pero no siempre se puede hacer esto.
Buena práctica de programación 2.17
Una instrucción larga puede esparcirse en varias líneas. Si una sola instrucción debe dividirse entre varias líneas,
los puntos que elija para hacer la división deben tener sentido, como después de una coma en una lista separada por
comas, o después de un operador en una expresión larga. Si una instrucción se divide entre dos o más líneas, aplique
sangría a todas las líneas subsecuentes hasta el final de la instrucción.
La figura 2.16 muestra la precedencia de los operadores que se presentan en este capítulo. Los operadores se
muestran de arriba hacia abajo, en orden descendente de precedencia; todos, con la excepción del operador de
asignación, =, se asocian de izquierda a derecha. La suma es asociativa a la izquierda, por lo que una expresión
como x + y + z se evalúa como si se hubiera escrito así: ( x + y ) + z. El operador de asignación, =, asocia
de derecha a izquierda, por lo que una expresión como x = y = 0 se evalúa como si se hubiera escrito así: x =
( y = 0 ), en donde, como pronto veremos, primero se asigna el valor 0 a la variable y, y después se asigna el
resultado de esa asignación, 0, a x.
Buena práctica de programación 2.18
Cuando escriba expresiones que contengan muchos operadores, consulte la tabla de precedencia (apéndice A). Con-
firme que las operaciones en la expresión se realicen en el orden que usted espera. Si no está seguro acerca del orden
de evaluación en una expresión compleja, utilice paréntesis para forzar el orden, en la misma forma que lo haría con
las expresiones algebraicas. Observe que algunos operadores como el de asignación, =, asocian de derecha a izquierda,
en vez de hacerlo de izquierda a derecha.
2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo
examinar el documento de requerimientos de un problema
Ahora empezaremos nuestro ejemplo práctico opcional de diseño e implementación orientados a objetos. Las sec-
ciones del Ejemplo práctico de Ingeniería de Software al final de este y los siguientes capítulos le ayudarán a incur-
sionar en la orientación a objetos, mediante el análisis de un ejemplo práctico de una máquina de cajero automático
Operadores Asociatividad Tipo
* / % izquierda a derecha multiplicativa
+ - izquierda a derecha suma
< <= > >= izquierda a derecha relacional
== != izquierda a derecha igualdad
= derecha a izquierda asignación
Figura 2.16 | Precedencia y asociatividad de los operadores descritos hasta ahora.
2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de ... 57
(Automated Teller Machine o ATM, por sus siglas en inglés). Este ejemplo práctico le brindará una experiencia
de diseño e implementación substancial, cuidadosamente pautada y completa. En los capítulos 3 al 8 y 10, lleva-
remos a cabo los diversos pasos de un proceso de diseño orientado a objetos (DOO) utilizando UML, mientras
relacionamos estos pasos con los conceptos orientados a objetos que se describen en los capítulos. El apéndice M
implementa el ATM utilizando las técnicas de la programación orientada a objetos (POO) en Java. Presentaremos
la solución completa al ejemplo práctico. Éste no es un ejercicio, sino una experiencia de aprendizaje de extremo
a extremo, que concluye con un análisis detallado del código en Java que implementamos, con base en nuestro
diseño. Este ejemplo práctico le ayudará a acostumbrarse a los tipos de problemas substanciales que se encuentran
en la industria, y sus soluciones potenciales. Esperamos que disfrute esta experiencia de aprendizaje.
Empezaremos nuestro proceso de diseño con la presentación de un documento de requerimientos, el cual
especifica el propósito general del sistema ATM y qué debe hacer. A lo largo del ejemplo práctico, nos referiremos
al documento de requerimientos para determinar con precisión la funcionalidad que debe incluir el sistema.
Documento de requerimientos
Un banco local pretende instalar una nueva máquina de cajero automático (ATM), para permitir a los usuarios (es
decir, los clientes del banco) realizar transacciones financieras básicas (figura 2.17). Cada usuario sólo puede tener
una cuenta en el banco. Los usuarios del ATM deben poder ver el saldo de su cuenta, retirar efectivo (es decir,
sacar dinero de una cuenta) y depositar fondos (es decir, meter dinero en una cuenta). La interfaz de usuario del
cajero automático contiene los siguientes componentes:
una pantalla que muestra mensajes al usuario
un teclado que recibe datos numéricos de entrada del usuario
un dispensador de efectivo que dispensa efectivo al usuario, y
una ranura de depósito que recibe sobres para depósitos del usuario.
El dispensador de efectivo comienza cada día cargado con 500 billetes de $20. [Nota: debido al alcance limitado
de este ejemplo práctico, ciertos elementos del ATM que se describen aquí no imitan exactamente a los de un
ATM real. Por ejemplo, generalmente un ATM contiene un dispositivo que lee el número de cuenta del usuario
de una tarjeta para ATM, mientras que este ATM pide al usuario que escriba su número de cuenta. Un ATM
real también imprime por lo general un recibo al final de una sesión, pero toda la salida de este ATM aparece en
la pantalla].
•
•
•
•
Figura 2.17 | Interfaz de usuario del cajero automático.
Teclado
Pantalla
Ranura de
depósito
Dispensador
de efectivo
Bienvenido!
Escriba su número de cuenta: 12345
Escriba su NIP: 54321
Inserte aquí el sobre de depósito
Inserte aquí el sobre de depósito
Inserte aquí el sobre de depósito
Tome aquí el efectivo
Tome aquí el efectivo
Tome aquí el efectivo
58 Capítulo 2 Introducción a las aplicaciones en Java
El banco desea que usted desarrolle software para realizar las transacciones financieras que inicien los clientes
a través del ATM. Posteriormente, el banco integrará el software con el hardware del ATM. El software debe
encapsular la funcionalidad de los dispositivos de hardware (por ejemplo: dispensador de efectivo, ranura para
depósito) dentro de los componentes de software, pero no necesita estar involucrado en la manera en que estos
dispositivos ejecutan su tarea. El hardware del ATM no se ha desarrollado aún, en vez de que usted escriba un
software para ejecutarse en el ATM, deberá desarrollar una primera versión del software para que se ejecute en
una computadora personal. Esta versión debe utilizar el monitor de la computadora para simular la pantalla del
ATM y el teclado de la computadora para simular el teclado numérico del ATM.
Una sesión con el ATM consiste en la autenticación de un usuario (es decir, proporcionar la identidad del
usuario) con base en un número de cuenta y un número de identificación personal (NIP), seguida de la creación
y la ejecución de transacciones financieras. Para autenticar un usuario y realizar transacciones, el ATM debe
interactuar con la base de datos de información sobre las cuentas del banco (es decir, una colección organizada
de datos almacenados en una computadora). Para cada cuenta de banco, la base de datos almacena un número de
cuenta, un NIP y un saldo que indica la cantidad de dinero en la cuenta. [Nota: asumiremos que el banco
planea construir sólo un ATM, por lo que no necesitamos preocuparnos para que varios ATMs puedan acceder
a esta base de datos al mismo tiempo. Lo que es más, supongamos que el banco no realizará modificaciones en la
información que hay en la base de datos mientras un usuario accede al ATM. Además, cualquier sistema comer-
cial como un ATM se topa con cuestiones de seguridad con una complejidad razonable, las cuales van más allá
del alcance de un curso de programación de primer o segundo semestre. No obstante, para simplificar nuestro
ejemplo supondremos que el banco confía en el ATM para que acceda a la información en la base de datos y la
manipule sin necesidad de medidas de seguridad considerables].
Al acercarse al ATM (suponiendo que nadie lo está utilizando), el usuario deberá experimentar la siguiente
secuencia de eventos (vea la figura 2.17):
1. La pantalla muestra un mensaje de bienvenida y pide al usuario que introduzca un número de cuenta.
2. El usuario introduce un número de cuenta de cinco dígitos, mediante el uso del teclado.
3. En la pantalla aparece un mensaje, en el que se pide al usuario que introduzca su NIP (número de iden-
tificación personal) asociado con el número de cuenta especificado.
4. El usuario introduce un NIP de cinco dígitos mediante el teclado numérico.
5. Si el usuario introduce un número de cuenta válido y el NIP correcto para esa cuenta, la pantalla mues-
tra el menú principal (figura 2.18). Si el usuario introduce un número de cuenta inválido o un NIP
incorrecto, la pantalla muestra un mensaje apropiado y después el ATM regresa al paso 1 para reiniciar
el proceso de autenticación.
Una vez que el ATM autentica al usuario, el menú principal (figura 2.18) debe contener una opción nume-
rada para cada uno de los tres tipos de transacciones: solicitud de saldo (opción 1), retiro (opción 2) y depó-
sito (opción 3). El menú principal también debe contener una opción para que el usuario pueda salir del sistema
(opción 4). Después el usuario elegirá si desea realizar una transacción (oprimiendo 1, 2 o 3) o salir del siste-
ma (oprimiendo 4).
Si el usuario oprime 1 para solicitar su saldo, la pantalla mostrará el saldo de esa cuenta bancaria. Para ello,
el ATM deberá obtener el saldo de la base de datos del banco.
Los siguientes pasos describen las acciones que ocurren cuando el usuario elige la opción 2 para hacer un
retiro:
1. La pantalla muestra un menú (vea la figura 2.19) que contiene montos de retiro estándar: $20 (opción
1), $40 (opción 2), $60 (opción 3), $100 (opción 4) y $200 (opción 5). El menú también contiene una
opción que permite al usuario cancelar la transacción (opción 6).
2. El usuario introduce la selección del menú mediante el teclado numérico.
3. Si el monto a retirar elegido es mayor que el saldo de la cuenta del usuario, la pantalla muestra un men-
saje indicando esta situación y pide al usuario que seleccione un monto más pequeño. Entonces el ATM
regresa al paso 1. Si el monto a retirar elegido es menor o igual que el saldo de la cuenta del usuario (es
decir, un monto de retiro aceptable), el ATM procede al paso 4. Si el usuario opta por cancelar la tran-
sacción (opción 6), el ATM muestra el menú principal y espera la entrada del usuario.
2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de ... 59
4. Si el dispensador contiene suficiente efectivo para satisfacer la solicitud, el ATM procede al paso 5. En
caso contrario, la pantalla muestra un mensaje indicando el problema y pide al usuario que seleccione
un monto de retiro más pequeño. Después el ATM regresa al paso 1.
5. El ATM carga el monto de retiro al saldo de la cuenta del usuario en la base de datos del banco (es decir,
resta el monto de retiro al saldo de la cuenta del usuario).
6. El dispensador de efectivo entrega el monto deseado de dinero al usuario.
Menú de retiro
1 - $20 4 - $100
2 - $40 5 - $200
3 - $60 6 - Cancelar transacción
Elija un monto de retiro:
Inserte aquí el sobre de depósito
Inserte aquí el sobre de depósito
Inserte aquí el sobre de depósito
Tome aquí el efectivo
Tome aquí el efectivo
Tome aquí el efectivo
Figura 2.19 | Menú de retiro del ATM.
Menú principal
1 - Ver mi saldo
2 - Retirar efectivo
3 - Depositar fondos
4 - Salir
Escriba una opción:
Inserte aquí el sobre de depósito
Inserte aquí el sobre de depósito
Inserte aquí el sobre de depósito
Tome aquí el efectivo
Tome aquí el efectivo
Tome aquí el efectivo
Figura 2.18 | Menú principal del ATM.
60 Capítulo 2 Introducción a las aplicaciones en Java
7. La pantalla muestra un mensaje para recordar al usuario que tome el dinero.
Los siguientes pasos describen las acciones que ocurren cuando el usuario elige la opción 3 para hacer un
depósito:
1. La pantalla muestra un mensaje que pide al usuario que introduzca un monto de depósito o que escriba
0 (cero) para cancelar la transacción.
2. El usuario introduce un monto de depósito o 0 mediante el teclado numérico. [Nota: el teclado no
contiene un punto decimal o signo de dólares, por lo que el usuario no puede escribir una cantidad real
en dólares (por ejemplo, $1.25), sino que debe escribir un monto de depósito en forma de número de
centavos (por ejemplo, 125). Después, el ATM divide este número entre 100 para obtener un número
que represente un monto en dólares (por ejemplo, 125 ÷ 100 = 1.25)].
3. Si el usuario especifica un monto a depositar, el ATM procede al paso 4. Si elije cancelar la transacción
(escribiendo 0), el ATM muestra el menú principal y espera la entrada del usuario.
4. La pantalla muestra un mensaje indicando al usuario que introduzca un sobre de depósito en la ranura
para depósitos.
5. Si la ranura de depósitos recibe un sobre dentro de un plazo de tiempo no mayor a 2 minutos, el ATM
abona el monto del depósito al saldo de la cuenta del usuario en la base de datos del banco (es decir,
suma el monto del depósito al saldo de la cuenta del usuario). [Nota: este dinero no está disponible de
inmediato para retirarse. El banco debe primero verificar físicamente el monto de efectivo en el sobre
de depósito, y cualquier cheque que éste contenga debe validarse (es decir, el dinero debe transferirse de
la cuenta del emisor del cheque a la cuenta del beneficiario). Cuando ocurra uno de estos eventos, el
banco actualizará de manera apropiada el saldo del usuario que está almacenado en su base de datos.
Esto ocurre de manera independiente al sistema ATM]. Si la ranura de depósito no recibe un sobre
dentro de un plazo de tiempo no mayor a dos minutos, la pantalla muestra un mensaje indicando que
el sistema canceló la transacción debido a la inactividad. Después el ATM muestra el menú principal y
espera la entrada del usuario.
Una vez que el sistema ejecuta una transacción en forma exitosa, debe volver a mostrar el menú principal para
que el usuario pueda realizar transacciones adicionales. Si el usuario elije salir del sistema, la pantalla debe mostrar
un mensaje de agradecimiento y después el mensaje de bienvenida para el siguiente usuario.
Análisis del sistema de ATM
En la declaración anterior se presentó un ejemplo simplificado de un documento de requerimientos. Por lo gene-
ral, dicho documento es el resultado de un proceso detallado de recopilación de requerimientos, el cual podría
incluir entrevistas con usuarios potenciales del sistema y especialistas en campos relacionados con el mismo. Por
ejemplo, un analista de sistemas que se contrate para preparar un documento de requerimientos para software
bancario (por ejemplo, el sistema ATM que describimos aquí) podría entrevistar expertos financieros para obtener
una mejor comprensión de qué es lo que debe hacer el software. El analista utilizaría la información recopilada
para compilar una lista de requerimientos del sistema, para guiar a los diseñadores de sistemas en el proceso de
diseño del mismo.
El proceso de recopilación de requerimientos es una tarea clave de la primera etapa del ciclo de vida del
software. El ciclo de vida del software especifica las etapas a través de las cuales el software evoluciona desde el
momento en que fue concebido hasta que deja de utilizarse. Por lo general, estas etapas incluyen: análisis, diseño,
implementación, prueba y depuración, despliegue, mantenimiento y retiro. Existen varios modelos de ciclo de
vida del software, cada uno con sus propias preferencias y especificaciones con respecto a cuándo y qué tan a
menudo deben llevar a cabo los ingenieros de software las diversas etapas. Los modelos de cascada realizan cada
etapa una vez en sucesión, mientras que los modelos iterativos pueden repetir una o más etapas varias veces a lo
largo del ciclo de vida de un producto.
La etapa de análisis del ciclo de vida del software se enfoca en definir el problema a resolver. Al diseñar
cualquier sistema, uno debe resolver el problema de la manera correcta, pero de igual manera uno debe resolver el
problema correcto. Los analistas de sistemas recolectan los requerimientos que indican el problema específico a
resolver. Nuestro documento de requerimientos describe nuestro sistema ATM con el suficiente detalle como para
que usted no necesite pasar por una etapa de análisis exhaustiva; ya lo hicimos por usted.
2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de ... 61
Para capturar lo que debe hacer un sistema propuesto, los desarrolladores emplean a menudo una técnica
conocida como modelado de caso-uso. Este proceso identifica los casos de uso del sistema, cada uno de los cua-
les representa una capacidad distinta que el sistema provee a sus clientes. Por ejemplo, es común que los ATMs
tengan varios casos de uso, como “Ver saldo de cuenta”, “Retirar efectivo”, “Depositar fondos”, “Transferir fondos
entre cuentas” y “Comprar estampas postales”. El sistema ATM simplificado que construiremos en este ejemplo
práctico requiere sólo los tres primeros casos de uso.
Cada uno de los casos de uso describe un escenario común en el cual el usuario utiliza el sistema. Usted ya leyó
las descripciones de los casos de uso del sistema ATM en el documento de requerimientos; las listas de pasos reque-
ridos para realizar cada tipo de transacción (como solicitud de saldo, retiro y depósito) describen en realidad los tres
casos de uso de nuestro ATM: “Ver saldo de cuenta”, “Retirar efectivo” y “Depositar fondos”, respectivamente.
Diagramas de caso-uso
Ahora presentaremos el primero de varios diagramas de UML en el ejemplo práctico. Crearemos un diagrama de
caso-uso para modelar las interacciones entre los clientes de un sistema (en este ejemplo práctico, los clientes del
banco) y sus casos de uso. El objetivo es mostrar los tipos de interacciones que tienen los usuarios con un sistema
sin proveer los detalles; éstos se mostrarán en otros diagramas de UML (los cuales presentaremos a lo largo del
ejemplo práctico). A menudo, los diagramas de caso-uso se acompañan de texto informal que describe los casos
de uso con más detalle; como el texto que aparece en el documento de requerimientos. Los diagramas de caso-uso
se producen durante la etapa de análisis del ciclo de vida del software. En sistemas más grandes, los diagramas de
caso-uso son herramientas indispensables que ayudan a los diseñadores de sistemas a enfocarse en satisfacer las
necesidades de los usuarios.
La figura 2.20 muestra el diagrama de caso-uso para nuestro sistema ATM. La figura humana representa a
un actor, el cual define los roles que desempeña una entidad externa (como una persona u otro sistema) cuando
interactúa con el sistema. Para nuestro cajero automático, el actor es un Usuario que puede ver el saldo de una
cuenta, retirar efectivo y depositar fondos del ATM. El Usuario no es una persona real, sino que constituye los
roles que puede desempeñar una persona real (al desempeñar el papel de un Usuario) mientras interactúa con el
ATM. Hay que tener en cuenta que un diagrama de caso-uso puede incluir varios actores. Por ejemplo, el diagra-
ma de caso-uso para un sistema ATM de un banco real podría incluir también un actor llamado Administrador,
que rellene el dispensador de efectivo a diario.
Nuestro documento de requerimientos provee los actores: “los usuarios del ATM deben poder ver el saldo
de su cuenta, retirar efectivo y depositar fondos”. Por lo tanto, el actor en cada uno de estos tres casos de uso es el
usuario que interactúa con el ATM. Una entidad externa (una persona real) desempeña el papel del usuario para
realizar transacciones financieras. La figura 2.20 muestra un actor, cuyo nombre (Usuario) aparece debajo del
actor en el diagrama. UML modela cada caso de uso como un óvalo conectado a un actor con una línea sólida.
Los ingenieros de software (más específicamente, los diseñadores de sistemas) deben analizar el documento
de requerimientos o un conjunto de casos de uso, y diseñar el sistema antes de que los programadores lo imple-
menten en un lenguaje de programación específico. Durante la etapa de análisis, los diseñadores de sistemas
se enfocan en comprender el documento de requerimientos para producir una especificación de alto nivel que
describa qué es lo que el sistema debe hacer. El resultado de la etapa de diseño (una especificación de diseño)
Depositar fondos
Retirar efectivo
Ver saldo de cuenta
Usuario
Figura 2.20 | Diagrama de caso-uso para el sistema ATM, desde la perspectiva del usuario.
62 Capítulo 2 Introducción a las aplicaciones en Java
debe detallar claramente cómo debe construirse el sistema para satisfacer estos requerimientos. En las siguientes
secciones del Ejemplo práctico de Ingeniería de Software, llevaremos a cabo los pasos de un proceso simple de
diseño orientado a objetos (DOO) con el sistema ATM, para producir una especificación de diseño que contenga
una colección de diagramas de UML y texto de apoyo. UML está diseñado para utilizarse con cualquier pro-
ceso de DOO. Existen muchos de esos procesos, de los cuales el más conocido es Rational Unified Process™
(RUP), desarrollado por Rational Software Corporation. RUP es un proceso robusto para diseñar aplicaciones a
nivel industrial. Para este ejemplo práctico, presentaremos nuestro propio proceso de diseño simplificado, desa-
rrollado para estudiantes de cursos de programación de primer y segundo semestre.
Diseño del sistema ATM
Ahora comenzaremos la etapa de diseño de nuestro sistema ATM. Un sistema es un conjunto de componentes
que interactúan para resolver un problema. Por ejemplo, para realizar sus tareas designadas, nuestro sistema ATM
tiene una interfaz de usuario (figura 2.17), contiene software para ejecutar transacciones financieras e interactúa
con una base de datos de información de cuentas bancarias. La estructura del sistema describe los objetos del sis-
tema y sus interrelaciones. El comportamiento del sistema describe la manera en que cambia el sistema a medida
que sus objetos interactúan entre sí. Todo sistema tiene tanto estructura como comportamiento; los diseñadores
deben especificar ambos. Existen diversos tipos de estructuras y comportamientos de un sistema. Por ejemplo, las
interacciones entre los objetos en el sistema son distintas a las interacciones entre el usuario y el sistema, pero aun
así ambas constituyen una porción del comportamiento del sistema.
El estándar UML 2 especifica 13 tipos de diagramas para documentar los modelos de un sistema. Cada
tipo de diagrama modela una característica distinta de la estructura o del comportamiento de un sistema; seis
diagramas se relacionan con la estructura del sistema; los siete restantes se relacionan con su comportamiento.
Aquí listaremos sólo los seis tipos de diagramas que utilizaremos en nuestro ejemplo práctico, uno de los cuales
(el diagrama de clases) modela la estructura del sistema, mientras que los otros cinco modelan el comportamiento.
En el apéndice O, UML 2: Tipos de diagramas adicionales, veremos las generalidades sobre los siete tipos restan-
tes de diagramas de UML.
1. Los diagramas de caso-uso, como el de la figura 2.20, modelan las interacciones entre un sistema y sus
entidades externas (actores) en términos de casos de uso (capacidades del sistema, como “Ver saldo de
cuenta”, “Retirar efectivo” y “Depositar fondos”).
2. Los diagramas de clases, que estudiará en la sección 3.10, modelan las clases o “bloques de cons-
trucción” que se utilizan en un sistema. Cada sustantivo u “objeto” que se describe en el documento
de requerimientos es candidato para ser una clase en el sistema (por ejemplo, Cuenta, Teclado). Los
diagramas de clases nos ayudan a especificar las relaciones estructurales entre las partes del sistema. Por
ejemplo, el diagrama de clases del sistema ATM especificará que el ATM está compuesto físicamente de
una pantalla, un teclado, un dispensador de efectivo y una ranura para depósitos.
3. Los diagramas de máquina de estado, que estudiará en la sección 5.11, modelan las formas en que un
objeto cambia de estado. El estado de un objeto se indica mediante los valores de todos los atributos
del objeto, en un momento dado. Cuando un objeto cambia de estado, puede comportarse de manera
distinta en el sistema. Por ejemplo, después de validar el NIP de un usuario, el ATM cambia del estado
“usuario no autenticado” al estado “usuario autenticado”, punto en el cual el ATM permite al usuario rea-
lizar transacciones financieras (por ejemplo, ver el saldo de su cuenta, retirar efectivo, depositar fondos).
4. Los diagramas de actividad, que también estudiará en la sección 5.11, modelan la actividad de un
objeto: el flujo de trabajo (secuencia de eventos) del objeto durante la ejecución del programa. Un
diagrama de actividad modela las acciones que realiza el objeto y especifica el orden en el cual desem-
peña estas acciones. Por ejemplo, un diagrama de actividad muestra que el ATM debe obtener el saldo
de la cuenta del usuario (de la base de datos de información de las cuentas del banco) antes de que la
pantalla pueda mostrar el saldo al usuario.
5. Los diagramas de comunicación (llamados diagramas de colaboración en versiones anteriores de
UML) modelan las interacciones entre los objetos en un sistema, con un énfasis acerca de qué interac-
ciones ocurren. En la sección 7.14 aprenderá que estos diagramas muestran cuáles objetos deben in-
teractuar para realizar una transacción en el ATM. Por ejemplo, el ATM debe comunicarse con la base
de datos de información de las cuentas del banco para obtener el saldo de una cuenta.
2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de ... 63
6. Los diagramas de secuencia modelan también las interacciones entre los objetos en un sistema, pero a
diferencia de los diagramas de comunicación, enfatizan cuándo ocurren las interacciones. En la sección
7.14 aprenderá que estos diagramas ayudan a mostrar el orden en el que ocurren las interacciones al
ejecutar una transacción financiera. Por ejemplo, la pantalla pide al usuario que escriba un monto de
retiro antes de dispensar el efectivo.
En la sección 3.10 seguiremos diseñando nuestro sistema ATM; ahí identificaremos las clases del documento
de requerimientos. Para lograr esto, extraeremos sustantivos clave y frases nominales del documento de reque-
rimientos. Mediante el uso de estas clases, desarrollaremos nuestro primer borrador del diagrama de clases que
modelará la estructura de nuestro sistema ATM.
Recursos en Internet y Web
Los siguientes URLs proporcionan información sobre el diseño orientado a objetos con UML.
www-306.ibm.com/software/rational/uml/
Lista preguntas frecuentes acerca del UML, proporcionado por IBM Rational.
www.douglass.co.uk/documents/softdocwiz.com.UML.htm
Sitio anfitrión del Diccionario del Lenguaje unificado de modelado, el cual lista y define todos los términos utilizados
en el UML.
www-306.ibm.com/software/rational/offerings/design.html
Proporciona información acerca del software IBM Rational, disponible para el diseño de sistemas. Ofrece descargas de
versiones de prueba de 30 días de varios productos, como IBM Rational Rose® XDE Developer.
www.embarcadero.com/products/describe/index.html
Proporciona una licencia gratuita de 14 días para descargar una versión de prueba de Describe™: una herramienta de
modelado con UML de Embarcadero Technologies®.
www.borland.com/us/products/together/index.html
Proporciona una licencia gratuita de 30 días para descargar una versión de prueba de Borland® Together® Control
Center™: una herramienta de desarrollo de software que soporta el UML.
www.ilogix.com/sublevel.aspx?id=53 http://modelingcommunity.telelogic.com/developer-trial.aspx
Proporciona una licencia gratuita de 30 días para descargar una versión de prueba de I-Logix Rhapsody®: un entorno
de desarrollo controlado por modelos y basado en UML 2.
argouml.tigris.org
Contiene información y descargas para ArgoUML, una herramienta gratuita de código fuente abierto de UML,
escrita en Java.
www.objectsbydesign.com/books/booklist.html
Provee una lista de libros acerca de UML y el diseño orientado a objetos.
www.objectsbydesign.com/tools/umltools_byCompany.html
Provee una lista de herramientas de software que utilizan UML, como IBM Rational Rose, Embarcadero Describe,
Sparx Systems Enterprise Architect, I-Logix Rhapsody y Gentleware Poseidon para UML.
www.ootips.org/ood-principles.html
Proporciona respuestas a la pregunta “¿Qué se requiere para tener un buen diseño orientado a objetos?”
parlezuml.com/tutorials/umlforjava.htm
Ofrece un tutorial de UML para desarrolladores de Java, el cual presenta los diagramas de UML y los compara detalla-
damente con el código que los implementa.
www.cetus-links.org/oo_uml.html
Introduce el UML y proporciona vínculos a numerosos recursos sobre UML.
www.agilemodeling.com/essays/umlDiagrams.htm
Proporciona descripciones detalladas y tutoriales acerca de cada uno de los 13 tipos de diagramas de UML 2.
Lecturas recomendadas
Los siguientes libros proporcionan información acerca del diseño orientado a objetos con UML.
Booch, G. Object-Oriented Analysis and Design with Applications, Tercera edición. Boston: Addison-Wesley, 2004.
Eriksson, H. et al. UML 2 Toolkit. Nueva York: John Wiley, 2003.
Kruchten, P. The Rational Unified Process: An Introduction. Boston: Addison-Wesley, 2004.
64 Capítulo 2 Introducción a las aplicaciones en Java
Larman, C. Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design, Segunda edición.
Upper Saddle River, NJ: Prentice Hall, 2002.
Roques, P. UML in Practice: The Art of Modeling Software Systems Demonstrated Through Worked Examples and Solutions.
Nueva York: John Wiley, 2004.
Rosenberg, D. y K. Scott. Applying Use Case Driven Object Modeling with UML: An Annotated e-Commerce Example.
Reading, MA: Addison-Wesley, 2001.
Rumbaugh, J., I. Jacobson y G. Booch. The Complete UML Training Course. Upper Saddle River, NJ: Prentice Hall,
2000.
Rumbaugh, J., I. Jacobson y G. Booch. The Unified Modeling Language Reference Manual. Reading, MA: Addison-
Wesley, 1999.
Rumbaugh, J., I. Jacobson y G. Booch. The Unified Software Development Process. Reading, MA: Addison-Wesley,
1999.
Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
2.1 Suponga que habilitamos a un usuario de nuestro sistema ATM para transferir dinero entre dos cuentas banca-
rias. Modifique el diagrama de caso-uso de la figura 2.20 para reflejar este cambio.
2.2 Los modelan las interacciones entre los objetos en un sistema, con énfasis acerca de cuándo ocurren
estas interacciones.
a) Diagramas de clases
b) Diagramas de secuencia
c) Diagramas de comunicación
d) Diagramas de actividad
2.3 ¿Cuál de las siguientes opciones lista las etapas de un típico ciclo de vida de software, en orden secuencial?
a) diseño, análisis, implementación, prueba
b) diseño, análisis, prueba, implementación
c) análisis, diseño, prueba, implementación
d) análisis, diseño, implementación, prueba
Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
2.1 La figura 2.21 contiene un diagrama de caso-uso para una versión modificada de nuestro sistema ATM, que
también permite a los usuarios transferir dinero entre cuentas.
2.2 b.
2.3 d.
Transferir fondos
entre cuentas
Depositar fondos
Retirar efectivo
Ver saldo de cuenta
Usuario
Figura 2.21 | Diagrama de caso-uso para una versión modificada de nuestro sistema ATM, que también permite
a los usuarios transferir dinero entre varias cuentas.
Resumen 65
2.10 Conclusión
En este capítulo aprendió muchas características importantes de Java, incluyendo cómo mostrar datos en la pan-
talla en un Símbolo del sistema, recibir datos del teclado, realizar cálculos y tomar decisiones. Las aplicaciones
que presentamos aquí le sirvieron como una introducción a los conceptos básicos de programación. Como verá
en el capítulo 3, por lo general las aplicaciones de Java contienen sólo unas cuantas líneas de código en el método
main; comúnmente estas instrucciones crean los objetos que realizan el trabajo de la aplicación. En el capítulo 3
aprenderá a implementar sus propias clases, y a utilizar objetos de esas clases en las aplicaciones.
Resumen
Sección 2.2 Su primer programa en Java: imprimir una línea de texto
• Los programadores de computadoras crean aplicaciones, escribiendo programas de cómputo. Una aplicación de Java
es un programa de computadora que se ejecuta cuando utilizamos el comando java para iniciar la JVM.
• Los programadores insertan comentarios para documentar los programas y mejorar su legibilidad. El compilador de
Java ignora los comentarios.
• Un comentario que empieza con // se llama comentario de fin de línea (o de una sola línea), ya que termina al final
de la línea en la que aparece.
• Los comentarios tradicionales (de varias líneas) pueden dividirse en varias líneas, y están delimitados por /* y */. El
compilador ignora todo el texto entre los delimitadores.
• Los comentarios Javadoc se delimitan por /** y */. Estos comentarios permiten a los programadores incrustar la
documentación directamente en sus programas. La herramienta javadoc genera documentación en HTML, con
base en los comentarios Javadoc.
• La sintaxis de un lenguaje de programación especifica las reglas para crear un programa apropiado en ese lenguaje.
• Un error de sintaxis (también conocido como error de compilador, error en tiempo de compilación o error de com-
pilación) ocurre cuando el compilador encuentra código que viola las reglas del lenguaje Java.
• Los programadores utilizan líneas en blanco y espacios para facilitar la lectura de los programas. En conjunto, las
líneas en blanco, los espacios y los tabuladores se conocen como espacio en blanco. Los espacios y los tabuladores se
conocen específicamente como caracteres de espacio en blanco. El compilador ignora el espacio en blanco.
• Todo programa en Java consiste en por lo menos una declaración de clase, definida por el programador (también
conocida como clase definida por el programador, o clase definida por el usuario).
• Las palabras clave están reservadas para el uso exclusivo de Java, y siempre se escriben con letras minúsculas.
• La palabra clave class introduce una declaración de clase, y va seguida inmediatamente del nombre de la clase.
• Por convención, todos los nombres de las clases en Java empiezan con una letra mayúscula, y la primera letra de cada
palabra subsiguiente también se escribe en mayúscula (como NombreClaseDeEjemplo).
• El nombre de una clase de Java es un identificador: una serie de caracteres formada por letras, dígitos, guiones bajos
(_) y signos de dólar ($), que no empieza con un dígito y no contiene espacios. Por lo general, un identificador que
no empieza con letra mayúscula no es el nombre de una clase de Java.
• Java es sensible a mayúsculas/minúsculas; es decir, las letras mayúsculas y minúsculas son distintas.
• El cuerpo de todas las declaraciones de clases debe estar delimitado por llaves, { y }.
• La declaración de una clase public debe guardarse en un archivo con el mismo nombre que la clase, seguido de la
extensión de nombre de archivo “.java”.
• El método main es el punto de inicio de toda aplicación en Java, y debe empezar con:
public static void main( String args[] )
en caso contrario, la JVM no ejecutará la aplicación.
• Los métodos pueden realizar tareas y devolver información cuando completan éstas tareas. La palabra clave void
indica que un método realizará una tarea, pero no devolverá información.
• Las instrucciones instruyen a la computadora para que realice acciones.
• Una secuencia de caracteres entre comillas dobles se llama cadena, cadena de caracteres, mensaje o literal de
cadena.
• System.out es el objeto de salida estándar; permite a las aplicaciones de Java mostrar caracteres en la ventana de
comandos.
• El método System.out.println muestra su argumento en la ventana de comandos, seguido de un carácter de
nueva línea para colocar el cursor de salida en el inicio de la siguiente línea.
• Toda instrucción termina con un punto y coma.
• La mayoría de los sistemas operativos utilizan el comando cd para cambiar directorios en la ventana de comandos.
• Para compilar un programa se utiliza el comando javac. Si el programa no contiene errores de sintaxis, se crea un
archivo de clase que contiene los códigos de bytes de Java, los cuales representan a la aplicación. La JVM interpreta
estos códigos de bytes cuando ejecutamos el programa.
Sección 2.3 Modificación de nuestro primer programa en Java
• System.out.print muestra su argumento en pantalla y coloca el cursor de salida justo después del último carácter
visualizado.
• Una barra diagonal inversa () en una cadena es un carácter de escape. Indica que se va a imprimir un “carácter
especial”. Java combina el siguiente carácter con la barra diagonal inversa para formar una secuencia de escape. La
secuencia de escape n representa el carácter de nueva línea, el cual coloca el cursor en la siguiente línea.
Sección 2.4 Cómo mostrar texto con printf
• El método System.out.printf (f significa “formato”) muestra datos con formato.
• Cuando un método requiere varios argumentos, éstos se separan con comas (,); a esto se le conoce como lista sepa-
rada por comas.
• El primer argumento del método printf es una cadena de formato, que puede consistir en texto fijo y especifica-
dores de formato. El método printf imprime el texto fijo de igual forma que print o println. Cada especificador
de formato es un receptáculo para un valor, y especifica el tipo de datos a imprimir.
• Los especificadores de formato empiezan con un signo porcentual (%), y van seguidos de un carácter que representa
el tipo de datos. El especificador de formato %s es un receptáculo para una cadena.
• En la posición del primer especificador de formato, printf sustituye el valor del primer argumento después de la
cadena de formato. En la posición de los siguientes especificadores de formato, printf sustituye el valor del siguien-
te argumento en la lista de argumentos.
Sección 2.5 Otra aplicación en Java: suma de enteros
• Los enteros son números completos, como –22 ,7, 0 y 1024.
• Una declaración import ayuda al compilador a localizar una clase que se utiliza en un programa.
• Java cuenta con un extenso conjunto de clases predefinidas que los programadores pueden reutilizar, en vez de tener
que “reinventar la rueda”. Estas clases se agrupan en paquetes: llamados colecciones de clases.
• En conjunto, a los paquetes de Java se les conoce como la biblioteca de clases de Java, o la Interfaz de Programación
de Aplicaciones de Java (API de Java).
• Una instrucción de declaración de variable especifica el nombre y el tipo de una variable.
• Una variable es una ubicación en la memoria de la computadora, en la cual se puede guardar un valor para usarlo
posteriormente en un programa. Todas las variables deben declararse con un nombre y un tipo para poder utili-
zarlas.
• El nombre de una variable permite al programa acceder al valor de la variable en memoria. Un nombre de variable
puede ser cualquier identificador válido.
• Al igual que otras instrucciones, las instrucciones de declaración de variables terminan con un punto y coma (;).
• Un objeto Scanner (paquete java.util) permite a un programa leer datos para usarlos en éste. Los datos pueden
provenir de muchas fuentes, como un archivo en disco o del usuario, a través del teclado. Antes de usar un objeto
Scanner, el programa debe crearlo y especificar el origen de los datos.
• Las variables deben inicializarse para poder usarlas en un programa.
• La expresión new Scanner( System.in ) crea un objeto Scanner que lee datos desde el teclado. El objeto de entrada
estándar, System.in, permite a las aplicaciones de Java leer los datos escritos por el usuario.
• El tipo de datos int se utiliza para declarar variables que guardarán valores enteros. El rango de valores para un int
es de –2,147,483,648 a +2,147,483,647.
• Los tipos float y double especifican números reales, y el tipo char especifica datos de caracteres. Los números reales
son números que contienen puntos decimales, como 3.4, 0.0 y –11.19. Las variables de tipo char representan
caracteres individuales, como una letra mayúscula (por ejemplo, A), un dígito (por ejemplo, 7), un carácter especial
(por ejemplo, * o %) o una secuencia de escape (por ejemplo, el carácter de nueva línea, n).
• Los tipos como int, float, double y char se conocen comúnmente como tipos primitivos o tipos integrados. Los
nombres de los tipos primitivos son palabras clave; por ende, deben aparecer escritos sólo con letras minúsculas.
• Un indicador pide al usuario que realice una acción específica.
• El método nextInt de Scanner obtiene un entero para usarlo en un programa.
66 Capítulo 2 Introducción a las aplicaciones en Java
Terminología 67
• El operador de asignación, =, permite al programa dar un valor a una variable. El operador = se llama operador
binario, ya que tiene dos operandos. Una instrucción de asignación utiliza un operador de asignación para asignar
un valor a una variable.
• Las partes de las instrucciones que tienen valores se llaman expresiones.
• El especificador de formato %d es un receptáculo para un valor int.
Sección 2.6 Conceptos acerca de la memoria
• Los nombres de las variables corresponden a ubicaciones en la memoria de la computadora. Cada variable tiene un
nombre, un tipo, un tamaño y un valor.
• Cada vez que se coloca un valor en una ubicación de memoria, se sustituye al valor anterior en esa ubicación. El valor
anterior se pierde.
Sección 2.7 Aritmética
• La mayoría de los programas realizan cálculos aritméticos. Los operadores aritméticos son + (suma), – (resta), *
(multiplicación), / (división) y % (residuo).
• La división de enteros produce un cociente entero.
• El operador residuo, %, produce el residuo después de la división.
• Las expresiones aritméticas en Java deben escribirse en formato de línea recta.
• Si una expresión contiene paréntesis anidados, el conjunto de paréntesis más interno se evalúa primero.
• Java aplica los operadores en las expresiones aritméticas en una secuencia precisa, la cual se determina mediante las
reglas de precedencia de los operadores.
• Cuando decimos que los operadores se aplican de izquierda a derecha, nos referimos a su asociatividad. Algunos
operadores se asocian de derecha a izquierda.
• Los paréntesis redundantes en una expresión pueden hacer que ésta sea más clara.
Sección 2.8 Toma de decisiones: operadores de igualdad y relacionales
• Una condición es una expresión que puede ser verdadera o falsa. La instrucción if de Java permite que un programa
tome una decisión, con base en el valor de una condición.
• Las condiciones en las instrucciones if se forman mediante el uso de los operadores de igualdad (== y !=) y relacio-
nales (>, <, >= y <=).
• Una instrucción if siempre empieza con la palabra clave if, seguida de una condición entre paréntesis, y espera una
instrucción en su cuerpo.
• La instrucción vacía es una instrucción que no realiza una tarea.
Terminología
%d, especificador de formato
%s, especificador de formato
.class, extensión de archivo
.java, extensión de archivo
aplicación
archivo de clase
argumento
asociatividad de los operadores
autodocumentado
barra diagonal inversa (), carácter de escape
biblioteca de clases de Java
cadena
cadena de caracteres
cadena de formato
carácter de escape
caracteres de espacio en blanco
cd, comando para cambiar directorios
char, tipo primitivo
clase definida por el programador
clase definida por el usuario
class, palabra clave
comentario
comentario de fin de línea (//)
comentario de una sola línea (//)
comentario de varias líneas (/* */)
comentario tradicional (/* */)
condición
cuerpo de la declaración de un método
cuerpo de la declaración de una clase
cursor de salida
decisión
declaración de una clase
declaración de variable
división de enteros
división, operador (/)
documentación de la API de Java
documento de un programa
double, tipo primitivo
entero
error de compilación
error de compilador
error de sintaxis
error en tiempo de compilación
espacio en blanco
especificador de formato
false
float, tipo primitivo
formato de línea recta
identificador
if, instrucción
igualdad, operadores
== “es igual a”
!= “no es igual a”
import, declaración
indicador
instrucción
instrucción de asignación
instrucción de declaración de variable
instrucción vacía (;)
int (entero), tipo primitivo
Interfaz de Programación de Aplicaciones (API) de Java
java, comando
java.lang, paquete
Javadoc, comentario (/** */)
javadoc, programa de utilería
línea de comandos
lista separada por comas
literal de cadena
llave derecha (})
llave izquierda ({)
main, método
mensaje
método
multiplicación, operador (*)
nombre de una clase
nombre de una variable
nombre de variable
nueva línea, carácter (n)
objeto
objeto de entrada estándar (System.in)
objeto de salida estándar (System.out)
operador
operador binario
operador de asignación (=)
operador de suma (+)
operadores aritméticos (*, /, %, + y –)
operando
palabras reservadas
paquete
paréntesis ()
paréntesis anidados
paréntesis redundantes
precedencia
programa de cómputo
public, palabra clave
punto y coma (;)
realizar una acción
reglas de precedencia de operadores
relacionales, operadores
< “es menor que”
<= “es menor o igual a”
> “es mayor que”
>= “es mayor o igual a”
residuo, operador (%)
resta, operador (–)
Scanner, clase
secuencia de escape
sensible a mayúsculas/minúsculas
shell
símbolo de MS-DOS
Símbolo del sistema
sintaxis
System.in, objeto (entrada estándar)
System.out, objeto (salida estándar)
System.out.print, método
System.out.printf, método
System.out.println, método
tamaño de una variable
texto fijo en una cadena de formato
tipo de una variable
tipo integrado
tipo primitivo
tolerante a fallas
true
ubicación de memoria
ubicación de una variable
valor de variable
variable
ventana de comandos
ventana de Terminal
void, palabra clave
68 Capítulo 2 Introducción a las aplicaciones en Java
Ejercicios de autoevaluación
2.1 Complete las siguientes oraciones:
a) El cuerpo de cualquier método comienza con un(a) _____________ y termina con un(a) _____________.
b) Toda instrucción termina con un _____________.
c) La instrucción _____________ (presentada en este capítulo) se utiliza para tomar decisiones.
d) _____________ indica el inicio de un comentario de fin de línea.
e) ______________, ______________, ______________ y ______________ se conocen como espacio en
blanco.
Respuestas a los ejercicios de autoevaluación 69
f) Las _____________ están reservadas para su uso en Java.
g) Las aplicaciones en Java comienzan su ejecución en el método _____________.
h) Los métodos _____________, _____________ y _____________ muestran información en la ventana
de comandos.
2.2 Indique si cada una de las siguientes instrucciones es verdadera o falsa. Si es falsa, explique por qué.
a) Los comentarios hacen que la computadora imprima el texto que va después de los caracteres // en la pan-
talla, al ejecutarse el programa.
b) Todas las variables deben recibir un tipo cuando se declaran.
c) Java considera que las variables numero y NuMeRo son idénticas.
d) El operador residuo (%) puede utilizarse solamente con operandos enteros.
e) Los operadores aritméticos *, /, %, + y – tienen todos el mismo nivel de precedencia.
2.3 Escriba instrucciones para realizar cada una de las siguientes tareas:
a) Declarar las variables c, estaEsUnaVariable, q76354 y numero como de tipo int.
b) Pedir al usuario que introduzca un entero.
c) Recibir un entero como entrada y asignar el resultado a la variable int valor. Suponga que se puede utili-
zar la variable entrada tipo Scanner para recibir un valor del teclado.
d) Si la variable numero no es igual a 7, mostrar "La variable numero no es igual a 7".
e) Imprimir "Este es un programa en Java" en una línea de la ventana de comandos.
f) Imprimir "Este es un programa en Java" en dos líneas de la ventana de comandos. La primera línea
debe terminar con es un. Use el método System.out.println.
g) Imprimir "Este es un programa en Java" en dos líneas de la ventana de comandos. La primera línea
debe terminar con es un. Use el método System.out.printf y dos especificadores de formato %s.
2.4 Identifique y corrija los errores en cada una de las siguientes instrucciones:
a) if ( c < 7 );
System.out.println( "c es menor que 7" );
b) if ( c => 7 )
System.out.println( "c es igual o mayor que 7" );
2.5 Escriba declaraciones, instrucciones o comentarios para realizar cada una de las siguientes tareas:
a) Indicar que un programa calculará el producto de tres enteros.
b) Crear un objeto Scanner que lea valores de la entrada estándar.
c) Declarar las variables x, y, z y resultado de tipo int.
d) Pedir al usuario que escriba el primer entero.
e) Leer el primer entero del usuario y almacenarlo en la variable x.
f) Pedir al usuario que escriba el segundo entero.
g) Leer el segundo entero del usuario y almacenarlo en la variable y.
h) Pedir al usuario que escriba el tercer entero.
i) Leer el tercer entero del usuario y almacenarlo en la variable z.
j) Calcular el producto de los tres enteros contenidos en las variables x, y y z, y asignar el resultado a la variable
resultado.
k) Mostrar el mensaje "El producto es", seguido del valor de la variable resultado.
2.6 Utilizando las instrucciones que escribió en el ejercicio 2.5, escriba un programa completo que calcule e impri-
ma el producto de tres enteros.
Respuestas a los ejercicios de autoevaluación
2.1 a) llave izquierda ({), llave derecha (}). b) punto y coma (;). c) if. d) //. e) Líneas en blanco, caracteres
de espacio, caracteres de nueva línea y tabuladores. f) palabras clave. g) main. h) System.out.print, System.out.
println y System.out.printf.
2.2 a) Falso. Los comentarios no producen ninguna acción cuando el programa se ejecuta. Se utilizan para docu-
mentar programas y mejorar su legibilidad.
b) Verdadero.
c) Falso. Java es sensible a mayúsculas y minúsculas, por lo que estas variables son distintas.
d) Falso. El operador residuo puede utilizarse también con operandos no enteros en Java.
e) Falso. Los operadores *, / y % se encuentran en el mismo nivel de precedencia, y los operadores + y – se
encuentran en un nivel menor de precedencia.
2.3 a) int c, estaEsUnaVariable, q76354, numero;
o
int c;
int estaEsUnaVariable;
int q76354;
int numero;
b) System.out.print( "Escriba un entero " );
c) valor = entrada.nextInt();
d) if ( numero != 7 )
System.out.println( "La variable numero no es igual a 7" );
e) System.out.println( "Este es un programa en Java" );
f) System.out.println( "Este es unn programa en Java" );
g) System.out.printf( "%s%sn", "Este es un", "programa en Java" );
2.4 Las soluciones al ejercicio de autoevaluación 2.4 son las siguientes:
a) Error: hay un punto y coma después del paréntesis derecho de la condición (c < 7 ) en la instrucción if.
Corrección: quite el punto y coma que va después del paréntesis derecho. [Nota: como resultado, la instruc-
ción de salida se ejecutará, sin importar que la condición en la instrucción if sea verdadera].
b) Error: el operador relacional => es incorrecto. Corrección: cambie => a >=.
2.5 a) // Calcula el producto de tres enteros
b) Scanner entrada = new Scanner (System.in);
c) int x, y, z, resultado;
o
int x;
int y;
int z;
int resultado;
d) System.out.print( "Escriba el primer entero: " );
e) x = entrada.nextInt();
f) System.out.print( "Escriba el segundo entero: " );
g) y = entrada.nextInt();
h) System.out.print( "Escriba el tercer entero: " );
i) z = entrada.nextInt();
j) resultado = x * y * z;
k) System.out.printf( "El producto es %dn", resultado );
l) System.exit( 0 );
2.6 La solución para el ejercicio 2.6 es la siguiente:
1 // Ejemplo 2.6: Producto.java
2 // Calcular el producto de tres enteros.
3 import java.util.Scanner; // el programa usa Scanner
4
5 public class Producto
6 {
7 public static void main( String args[] )
8 {
9 // crea objeto Scanner para obtener la entrada de la ventana de comandos
10 Scanner entrada = new Scanner( System.in );
11
12 int x; // primer número introducido por el usuario
13 int y; // segundo número introducido por el usuario
70 Capítulo 2 Introducción a las aplicaciones en Java
Ejercicios 71
Ejercicios
2.7 Complete las siguientes oraciones:
a) _____________ se utilizan para documentar un programa y mejorar su legibilidad.
b) Una decisión puede tomarse en un programa en Java con un(a) _____________.
c) Los cálculos se realizan normalmente mediante instrucciones _____________.
d) Los operadores aritméticos con la misma precedencia que la multiplicación son _____________ y _____
________ .
e) Cuando los paréntesis en una expresión aritmética están anidados, el conjunto _____________ de parén-
tesis se evalúa primero.
f) Una ubicación en la memoria de la computadora que puede contener distintos valores en diversos instantes
de tiempo, durante la ejecución de un programa, se llama _____________.
2.8 Escriba instrucciones en Java que realicen cada una de las siguientes tareas:
a) Mostrar el mensaje "Escriba un entero:", dejando el cursor en la misma línea.
b) Asignar el producto de las variables b y c a la variable a.
c) Indicar que un programa va a realizar un cálculo de nómina de muestra (es decir, usar texto que ayude a
documentar un programa).
2.9 Conteste con verdadero o falso a cada una de las siguientes proposiciones; en caso de ser falso, explique por qué.
a) Los operadores en Java se evalúan de izquierda a derecha.
b) Los siguientes nombres de variables son todos válidos: _barra_inferior_, m928134,t5, j7, sus_ventas$,
su_$cuenta_total, a, b$, c, z y z2.
c) Una expresión aritmética válida en Java sin paréntesis se evalúa de izquierda a derecha.
d) Los siguientes nombres de variables son todos inválidos: 3g, 87, 67h2, h22 y 2h.
2.10 Suponiendo que x = 2 y y = 3, ¿qué muestra cada una de las siguientes instrucciones?
a) System.out.printf( "x = %dn", x );
b) System.out.printf( "El valor de %d + %d es %dn", x, x, ( x + x ) );
c) System.out.printf( "x =" );
d) System.out.printf( "%d = %dn", ( x + y ), ( y + x ) );
14 int z; // tercer número introducido por el usuario
15 int resultado; // producto de los números
16
17 System.out.print( "Escriba el primer entero: " ); // indicador de entrada
18 x = entrada.nextInt(); // lee el primer entero
19
20 System.out.print( "Escriba el segundo entero: " ); // indicador de entrada
21 y = entrada.nextInt(); // lee el segundo entero
22
23 System.out.print( "Escriba el tercer entero: " ); // indicador de entrada
24 z = entrada.nextInt(); // lee el tercer entero
25
26 resultado = x * y * z; // calcula el producto de los números
27
28 System.out.printf( "El producto es %dn", resultado );
29
30 } // fin del método main
31
32 } // fin de la clase Producto
Escriba el primer entero: 10
Escriba el segundo entero: 20
Escriba el tercer entero: 30
El producto es 6000
2.11 ¿Cuáles de las siguientes instrucciones de Java contienen variables, cuyos valores se modifican?
a) p = i + j + k + 7;
b) System.out.println( "variables cuyos valores se destruyen" );
c) System.out.println( "a = 5" );
d) valor = entrada.nextInt();
2.12 Dado que y = ax3+ 7, ¿cuáles de las siguientes instrucciones en Java son correctas para esta ecuación?
a) y = a * x * x * x + 7;
b) y = a * x * x * ( x + 7 );
c) y = ( a * x ) * x * ( x + 7 );
d) y = ( a * x ) * x * x + 7;
e) y = a * ( x * x * x ) + 7;
f) y = a * x * ( x * x + 7 );
2.13 Indique el orden de evaluación de los operadores en cada una de las siguientes instrucciones en Java, y muestre
el valor x después de ejecutar cada una de ellas:
a) x = 7 + 3 * 6 / 2 – 1;
b) x = 2 % 2 + 2 * 2 – 2 / 2;
c) x = ( 3 * 9 * ( 3 + ( 9 * 3 / ( 3 ) ) ) );
2.14 Escriba una aplicación que muestre los números del 1 al 4 en la misma línea, con cada par de números adya-
centes separado por un espacio. Escriba el programa utilizando las siguientes técnicas:
a) Utilizando una instrucción System.out.println.
b) Utilizando cuatro instrucciones System.out.print.
c) Utilizando una instrucción System.out.printf.
2.15 Escriba una aplicación que pida al usuario que escriba dos números, que obtenga los números del usuario e
imprima la suma, producto, diferencia y cociente (división) de los números. Use las técnicas que se muestran en la
figura 2.7.
2.16 Escriba una aplicación que pida al usuario que escriba dos enteros, que obtenga los números del usuario y
muestre el número más grande, seguido de las palabras "es más grande". Si los números son iguales, imprima el
mensaje "Estos números son iguales". Utilice las técnicas que se muestran en la figura 2.15.
2.17 Escriba una aplicación que reciba tres enteros del usuario y muestre la suma, promedio, producto, menor y
mayor de esos números. Utilice las técnicas que se muestran en la figura 2.15. [Nota: el cálculo del promedio en este
ejercicio debe resultar en una representación entera del promedio. Por lo tanto, si la suma de los valores es 7, el prome-
dio debe ser 2, no 2.3333...].
2.18 Escriba una aplicación que muestre un cuadro, un óvalo, una flecha y un diamante usando asteriscos (*), como
se muestra a continuación:
********* *** * *
* * * * *** * *
* * * * ***** * *
* * * * * * *
* * * * * * *
* * * * * * *
* * * * * * *
* * * * * * *
********* *** * *
2.19 ¿Qué imprime el siguiente código?
System.out.println( "*n**n***n****n*****" );
2.20 ¿Qué imprime el siguiente código?
System.out.println( "*" );
System.out.println( "***" );
System.out.println( "*****" );
System.out.println( "****" );
System.out.println( "**" );
72 Capítulo 2 Introducción a las aplicaciones en Java
Ejercicios 73
2.21 ¿Qué imprime el siguiente código?
System.out.print( "*" );
System.out.print( "***" );
System.out.print( "*****" );
System.out.print( "****" );
System.out.println( "**" );
2.22 ¿Qué imprime el siguiente código?
System.out.print( "*" );
System.out.println( "***" );
System.out.println( "*****" );
System.out.print( "****" );
System.out.println( "**" );
2.23 ¿Qué imprime el siguiente código?
System.out.printf( "%sn%sn%sn", "*", "***", "*****" );
2.24 Escriba una aplicación que lea cinco enteros y que determine e imprima los enteros mayor y menor en el grupo.
Use solamente las técnicas de programación que aprendió en este capítulo.
2.25 Escriba una aplicación que lea un entero y que determine e imprima si es impar o par. [Sugerencia: use el
operador residuo. Un número par es un múltiplo de 2. Cualquier múltiplo de 2 deja un residuo de 0 cuando se divide
entre 2].
2.26 Escriba una aplicación que lea dos enteros, determine si el primero es un múltiplo del segundo e imprima el
resultado. [Sugerencia: use el operador residuo].
2.27 Escriba una aplicación que muestre un patrón de tablero de damas, como se muestra a continuación:
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
2.28 He aquí un adelanto. En este capítulo, aprendió sobre los enteros y el tipo int. Java también puede representar
números de punto flotante que contienen puntos decimales, como 3.14159. Escriba una aplicación que reciba del usua-
rio el radio de un círculo como un entero, y que imprima el diámetro, la circunferencia y el área del círculo mediante el
uso del valor de punto flotante 3.14159 para π. Use las técnicas que se muestran en la figura 2.7. [Nota: también puede
utilizar la constante predefinida Math.PI para el valor de π. Esta constante es más precisa que el valor 3.14159. La clase
Math se define en el paquete java.lang. Las clases en este paquete se importan de manera automática, por lo que no
necesita importar la clase Math mediante la instrucción import para usarla]. Use las siguientes fórmulas (r es el radio):
diámetro = 2r
circunferencia = 2πr
área = πr2
No almacene los resultados de cada cálculo en una variable. En vez de ello, especifique cada cálculo como el valor
que se imprimirá en una instrucción System.out.printf. Observe que los valores producidos por los cálculos del
área y la circunferencia son números de punto flotante. Dichos valores pueden imprimirse con el especificador
de formato %f en una instrucción System.out.printf. En el capítulo 3 aprenderá más acerca de los números de
punto flotante.
2.29 He aquí otro adelanto. En este capítulo, aprendió acerca de los enteros y el tipo int. Java puede también repre-
sentar letras en mayúsculas, en minúsculas y una considerable variedad de símbolos especiales. Cada carácter tiene su
correspondiente representación entera. El conjunto de caracteres que utiliza una computadora, y las correspondientes
representaciones enteras de esos caracteres, se conocen como el conjunto de caracteres de esa computadora. Usted puede
indicar un valor de carácter en un programa con sólo encerrar ese carácter entre comillas sencillas, como en 'A'.
Usted puede determinar el equivalente entero de un carácter si antepone a ese carácter la palabra (int), como en
(int) 'A'
Esta forma se conoce como operador de conversión de tipo. (Hablaremos más sobre estos operadores en el capítulo 4).
La siguiente instrucción imprime un carácter y su equivalente entero:
System.out.printf(
"El caracter %c tiene el valor %dn", 'A', ( (int) 'A' ) );
Cuando se ejecuta esta instrucción, muestra el carácter A y el valor 65 (del conjunto de caracteres conocido como Uni-
code®) como parte de la cadena. Observe que el especificador de formato %c es un receptáculo para un carácter (en este
caso, el carácter 'A').
Utilizando instrucciones similares a la mostrada anteriormente en este ejercicio, escriba una aplicación que
muestre los equivalentes enteros de algunas letras en mayúsculas, en minúsculas, dígitos y símbolos especiales.
Muestre los equivalentes enteros de los siguientes caracteres: A B C a b c 0 1 2 $ * + / y el carácter en
blanco.
2.30 Escriba una aplicación que reciba del usuario un número compuesto por cinco dígitos, que separe ese número
en sus dígitos individuales y los imprima, cada uno separado de los demás por tres espacios. Por ejemplo, si el usuario
escribe el número 42339, el programa debe imprimir
4 2 3 3 9
Suponga que el usuario escribe el número correcto de dígitos. ¿Qué ocurre cuando ejecuta el programa y
escribe un número con más de cinco dígitos? ¿Qué ocurre cuando ejecuta el programa y escribe un número con
menos de cinco dígitos? [Sugerencia: es posible hacer este ejercicio con las técnicas que aprendió en este capítulo.
Necesitará utilizar los operadores de división y residuo para “seleccionar” cada dígito].
2.31 Utilizando sólo las técnicas de programación que aprendió en este capítulo, escriba una aplicación que calcule
los cuadrados y cubos de los números del 0 al 10, y que imprima los valores resultantes en formato de tabla, como se
muestra a continuación. [Nota: Este programa no requiere de ningún tipo de entrada por parte del usuario].
numero cuadrado cubo
0 0 0
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000
2.32 Escriba un programa que reciba cinco números, y que determine e imprima la cantidad de números negativos,
positivos, y la cantidad de ceros recibidos.
74 Capítulo 2 Introducción a las aplicaciones en Java
Introducción
a las clases
y los objetos
OBJETIVOS
En este capítulo aprenderá a:
Comprender qué son las clases, los objetos, los métodos y las
variables de instancia.
Declarar una clase y utilizarla para crear un objeto.
Declarar métodos en una clase para implementar los
comportamientos de ésta.
Declarar variables de instancia en una clase para implementar
los atributos de ésta.
Saber cómo llamar a los métodos de un objeto para hacer que
realicen sus tareas.
Conocer las diferencias entre las variables de instancia de una
clase y las variables locales de un método.
Utilizar un constructor para asegurar que los datos de un
objeto se inicialicen cuando se cree el objeto.
Conocer las diferencias entre los tipos primitivos y los tipos por
referencia.
Q
Q
Q
Q
Q
Q
Q
Q
Usted verá algo nuevo.
Dos cosas. Y las llamo
Cosa Uno y Cosa Dos.
—Dr. Theodor Seuss Geisel.
Nada puede tener valor
sin ser un objeto de utilidad.
—Karl Marx
Sus sirvientes públicos le
sirven bien.
—Adlai E. Stevenson
Saber cómo responder
a alguien que habla,
contestar a alguien que
envía un mensaje.
—Amenemope
3
76 Capítulo 3 Introducción a las clases y los objetos
3.1 Introducción
En la sección 1.16 le presentamos la terminología básica y los conceptos acerca de la programación orientada a
objetos. El capítulo 2 comenzó a utilizar esos conceptos para crear aplicaciones simples que mostraran mensajes
al usuario, que obtuvieran información del usuario, realizaran cálculos y tomaran decisiones. Una característica
común de todas las aplicaciones del capítulo 2 fue que todas las instrucciones que realizaban tareas se encontraban
en el método main. Por lo general, las aplicaciones que usted desarrollará en este libro consistirán de dos o más
clases, cada una de las cuales contendrá dos o más métodos. Si usted se integra a un equipo de desarrollo en la
industria, podría trabajar en aplicaciones que contengan cientos, o incluso hasta miles de clases. En este capítulo
presentaremos un marco de trabajo simple para organizar las aplicaciones orientadas a objetos en Java.
Primero explicaremos el concepto de las clases mediante el uso de un ejemplo real. Después presentaremos
cinco aplicaciones completas para demostrarle cómo crear y utilizar sus propias clases. Los primeros cuatro ejem-
plos empiezan nuestro ejemplo práctico acerca de cómo desarrollar una clase tipo libro de calificaciones, que
los instructores pueden utilizar para mantener las calificaciones de las pruebas de sus estudiantes. Durante los
siguientes capítulos ampliaremos este ejemplo práctico y culminaremos con la versión que se presenta en el capí-
tulo 7, Arreglos. El último ejemplo en este capítulo introduce los números de punto flotante (es decir, números
que contienen puntos decimales, como 0.0345, –7.23 y 100.7) en el contexto de una clase tipo cuenta bancaria,
la cual mantiene el saldo de un cliente.
3.2 Clases, objetos, métodos y variables de instancia
Comenzaremos con una analogía simple, para ayudarle a comprender el concepto de las clases y su contenido.
Suponga que desea conducir un automóvil y, para hacer que aumente su velocidad, debe presionar el pedal del ace-
lerador. ¿Qué debe ocurrir antes de que pueda hacer esto? Bueno, antes de poder conducir un automóvil, alguien
tiene que diseñarlo. Por lo general, un automóvil empieza en forma de dibujos de ingeniería, similares a los planos
de construcción que se utilizan para diseñar una casa. Estos dibujos de ingeniería incluyen el diseño del pedal
del acelerador, para que el automóvil aumente su velocidad. El pedal “oculta” los complejos mecanismos que se
encargan de que el automóvil aumente su velocidad, de igual forma que el pedal del freno “oculta” los mecanismos
que disminuyen la velocidad del automóvil y por otro lado, el volante “oculta” los mecanismos que hacen que el
automóvil de vuelta. Esto permite que las personas con poco o nada de conocimiento acerca de cómo funcionan los
motores puedan conducir un automóvil con facilidad.
Desafortunadamente, no puede conducir los dibujos de ingeniería de un auto. Antes de poder conducir un
automóvil, éste debe construirse a partir de los dibujos de ingeniería que lo describen. Un automóvil completo
tendrá un pedal acelerador verdadero para hacer que aumente su velocidad, pero aún así no es suficiente; el auto-
móvil no acelerará por su propia cuenta, así que el conductor debe oprimir el pedal del acelerador.
Ahora utilizaremos nuestro ejemplo del automóvil para introducir los conceptos clave de programación de
esta sección. Para realizar una tarea en una aplicación se requiere un método. El método describe los mecanismos
3.1 Introducción
3.2 Clases, objetos, métodos y variables de instancia
3.3 Declaración de una clase con un método e instanciamiento de un objeto de una clase
3.4 Declaración de un método con un parámetro
3.5 Variables de instancia, métodos establecer y métodos obtener
3.6 Comparación entre tipos primitivos y tipos por referencia
3.7 Inicialización de objetos mediante constructores
3.8 Números de punto flotante y el tipo double
3.9 (Opcional) Ejemplo práctico de GUI y gráficos: uso de cuadros de diálogo
3.10 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un documento
de requerimientos
3.11 Conclusión
Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios
Pla
n
g
e
ne
r
a
l
que se encargan de realizar sus tareas; y oculta al usuario las tareas complejas que realiza, de la misma forma que el
pedal del acelerador de un automóvil oculta al conductor los complejos mecanismos para hacer que el automóvil
vaya más rápido. En Java, empezamos por crear una unidad de aplicación llamada clase para alojar a un método,
así como los dibujos de ingeniería de un automóvil alojan el diseño del pedal del acelerador. En una clase se pro-
porcionan uno o más métodos, los cuales están diseñados para realizar las tareas de esa clase. Por ejemplo, una
clase que representa a una cuenta bancaria podría contener un método para depositar dinero en una cuenta, otro
para retirar dinero de una cuenta y un tercero para solicitar el saldo actual de la cuenta.
Así como no podemos conducir un dibujo de ingeniería de un automóvil, tampoco podemos “conducir” una
clase. De la misma forma que alguien tiene que construir un automóvil a partir de sus dibujos de ingeniería para
poder conducirlo, también debemos construir un objeto de una clase para poder hacer que un programa realice
las tareas que la clase le describe cómo realizar. Ésta es una de las razones por las cuales Java se conoce como un
lenguaje de programación orientado a objetos.
Cuando usted conduce un automóvil, si oprime el pedal del acelerador se envía un mensaje al automóvil para
que realice una tarea-hacer que el automóvil vaya más rápido. De manera similar, se envían mensajes a un objeto;
cada mensaje se conoce como la llamada a un método, e indica a un método del objeto que realice su tarea.
Hasta ahora, hemos utilizado la analogía del automóvil para introducir las clases, los objetos y los métodos.
Además de las capacidades con las que cuenta un automóvil, también tiene muchos atributos como su color, el
número de puertas, la cantidad de gasolina en su tanque, su velocidad actual y el total de kilómetros recorridos (es
decir, la lectura de su odómetro). Al igual que las capacidades del automóvil, estos atributos se representan como
parte del diseño en sus diagramas de ingeniería. Cuando usted conduce un automóvil, estos atributos siempre están
asociados con él. Cada uno mantiene sus propios atributos. Por ejemplo, cada conductor sabe cuánta gasolina tiene
en su propio tanque, pero no cuánta hay en los tanques de otros automóviles. De manera similar, un objeto tiene
atributos que lleva consigo cuando se utiliza en un programa. Éstos se especifican como parte de la clase del objeto.
Por ejemplo, un objeto tipo cuenta bancaria tiene un atributo llamado saldo, el cual representa la cantidad de dine-
ro en la cuenta. Cada objeto tipo cuenta bancaria conoce el saldo en la cuenta que representa, pero no los saldos de
las otras cuentas en el banco. Los atributos se especifican mediante las variables de instancia de la clase.
El resto de este capítulo presenta ejemplos que demuestran los conceptos que presentamos aquí, dentro del
contexto de la analogía del automóvil. Los primeros cuatro ejemplos se encargan de construir en forma incremen-
tal una clase llamada LibroCalificaciones para demostrar estos conceptos:
1. El primer ejemplo presenta una clase llamada LibroCalificaciones, con un método que simplemente
muestra un mensaje de bienvenida cuando se le llama. Después le mostraremos cómo crear un objeto de
esa clase y cómo llamarlo, para que muestre el mensaje de bienvenida.
2. El segundo ejemplo modifica el primero, al permitir que el método reciba el nombre de un curso como
argumento, y al mostrar ese nombre como parte del mensaje de bienvenida.
3. El tercer ejemplo muestra cómo almacenar el nombre del curso en un objeto tipo LibroCalificacio-
nes. Para esta versión de la clase, también le mostraremos cómo utilizar los métodos para establecer el
nombre del curso y obtener este nombre.
4. El cuarto ejemplo demuestra cómo pueden inicializarse los datos en un objeto tipo LibroCalificaciones,
a la hora de crear el objeto; el constructor de la clase se encarga de realizar el proceso de inicialización.
El último ejemplo en el capítulo presenta una clase llamada Cuenta, la cual refuerza los conceptos presentados
en los primeros cuatro ejemplos, e introduce los números de punto flotante. Para este fin, presentamos una clase
llamada Cuenta, la cual representa una cuenta bancaria y mantiene su saldo como un número de punto flotante.
La clase contiene dos métodos —uno para acreditar un depósito a la cuenta, con lo cual se incrementa el saldo,
y otro para obtener el saldo. El constructor de la clase permite inicializar el saldo de cada objeto tipo Cuenta,
a la hora de crear el objeto. Crearemos dos objetos tipo Cuenta y haremos depósitos en cada uno de ellos, para
mostrar que cada objeto mantiene su propio saldo. El ejemplo también demuestra cómo recibir e imprimir en
pantalla números de punto flotante.
3.3 Declaración de una clase con un método e instanciamiento
de un objeto de una clase
Comenzaremos con un ejemplo que consiste en las clases LibroCalificaciones (figura 3.1) y PruebaLibroCa-
lificaciones (figura 3.2). La clase LibroCalificaciones (declarada en el archivo LibroCalificaciones.java)
3.3 Declaración de una clase con un método e instanciamiento de un objeto de una clase 77
78 Capítulo 3 Introducción a las clases y los objetos
se utilizará para mostrar un mensaje en la pantalla (figura 3.2), para dar la bienvenida al instructor a la aplicación
del libro de calificaciones. La clase PruebaLibroCalificaciones (declarada en el archivo PruebaLibroCalifi-
caciones.java) es una clase de aplicación en la que el método main utilizará a la clase LibroCalificaciones.
Cada declaración de clase que comienza con la palabra clave public debe almacenarse en un archivo que tenga
el mismo nombre que la clase, y que termine con la extensión de archivo .java. Por lo tanto, las clases Libro-
Calificaciones y PruebaLibroCalificaciones deben declararse en archivos separados, ya que cada clase se
declara como public.
Error común de programación 3.1
Declarar más de una clase public en el mismo archivo es un error de compilación.
La clase LibroCalificaciones
La declaración de la clase LibroCalificaciones (figura 3.1) contiene un método llamado mostrarMensaje
(líneas 7-10), el cual muestra un mensaje en la pantalla. La línea 9 de la clase realiza el trabajo de mostrar el men-
saje. Recuerde que una clase es como un plano de construcción; necesitamos crear un objeto de esta clase y llamar
a su método para hacer que se ejecute la línea 9 y que muestre su mensaje.
La declaración de la clase empieza en la línea 4. La palabra clave public es un modificador de acceso.
Por ahora, simplemente declararemos cada clase como public. Toda declaración de clase contiene la palabra
clave class, seguida inmediatamente por el nombre de la clase. El cuerpo de toda clase se encierra entre una llave
izquierda y una derecha ({ y }), como en las líneas 5 y 12 de la clase LibroCalificaciones.
En el capítulo 2, cada clase que declaramos tenía un método llamado main. La clase LibroCalificaciones
también tiene un método: mostrarMensaje (líneas 7-10). Recuerde que main es un método especial, que siem-
pre es llamado, automáticamente, por la Máquina Virtual de Java (JVM) a la hora de ejecutar una aplicación.
La mayoría de los métodos no se llaman en forma automática. Como veremos en breve, es necesario llamar al
método mostrarMensaje para decirle que haga su trabajo.
La declaración del método comienza con la palabra clave public para indicar que el método está “disponible
al público”; es decir, los métodos de otras clases pueden llamarlo desde el exterior del cuerpo de la declaración
de la clase. La palabra clave void indica que este método realizará una tarea pero no devolverá (es decir, regre-
sará) información al método que hizo la llamada cuando complete su tarea. Ya hemos utilizado métodos que
devuelven información; por ejemplo, en el capítulo 2 utilizó el método nextInt de Scanner para recibir un
entero escrito por el usuario desde el teclado. Cuando nextInt recibe un valor de entrada, devuelve ese valor para
utilizarlo en el programa.
El nombre del método, mostrarMensaje, va después del tipo de valor de retorno. Por convención, los
nombres de los métodos comienzan con una letra minúscula, y el resto de las palabras en el nombre empiezan
con letra mayúscula. Los paréntesis después del nombre del método indican que éste es un método. Un conjunto
vacío de paréntesis, como se muestra en la línea 7, indica que este método no requiere información adicional para
realizar su tarea. La línea 7 se conoce comúnmente como el encabezado del método. El cuerpo de cada método
se delimita mediante una llave izquierda y una llave derecha ({ y }), como en las líneas 8 y 10.
Figura 3.1 | Declaración de una clase con un método.
1 // Fig. 3.1: LibroCalificaciones.java
2 // Declaración de una clase con un método.
3
4 public class LibroCalificaciones
5 {
6 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones
7 public void mostrarMensaje()
8 {
9 System.out.println( “Bienvenido al Libro de calificaciones!” );
10 } // fin del método mostrarMensaje
11
12 } // fin de la clase LibroCalificaciones
El cuerpo de un método contiene una o varias instrucciones que realizan su trabajo. En este caso, el método
contiene una instrucción (línea 9) que muestra el mensaje "Bienvenido al Libro de calificaciones!",
seguido de una nueva línea en la ventana de comandos. Una vez que se ejecuta esta instrucción, el método ha
completado su trabajo.
A continuación, nos gustaría utilizar la clase LibroCalificaciones en una aplicación. Como aprendió en el
capítulo 2, el método main empieza la ejecución de todas las aplicaciones. Una clase que contiene el método main
es una aplicación de Java. Dicha clase es especial, ya que la JVM puede utilizar a main como un punto de entrada
para empezar la ejecución. La clase LibroCalificaciones no es una aplicación, ya que no contiene a main. Por
lo tanto, si trata de ejecutar LibroCalificaciones escribiendo java LibroCalificaciones en la ventana de
comandos, recibirá un mensaje de error como este:
Exception in thread "main" java.lang.NoSuchMethodError: main
Esto no fue un problema en el capítulo 2, ya que cada clase que declaramos tenía un método main. Para corregir
este problema con la clase LibroCalificaciones, debemos declarar una clase separada que contenga un método
main, o colocar un método main en la clase LibroCalificaciones. Para ayudarlo a prepararse para los programas
más extensos que encontrará más adelante en este libro y en la industria, utilizamos una clase separada (Prueba-
LibroCalificaciones en este ejemplo) que contiene el método main para probar cada nueva clase que vayamos
a crear en este capítulo.
La clase PruebaLibroCalificaciones
La declaración de la clase PruebaLibroCalificaciones (figura 3.2) contiene el método main que controlará la
ejecución de nuestra aplicación. Cualquier clase que contiene el método main, declarado como se muestra en
la línea 7, puede utilizarse para ejecutar una aplicación. La declaración de la clase PruebaLibroCalificaciones
empieza en la línea 4 y termina en la línea 16. La clase sólo contiene un método main, algo común en muchas
clases que empiezan la ejecución de una aplicación.
Las líneas 7 a la 14 declaran el método main. En el capítulo 2 vimos que el encabezado main debe aparecer
como se muestra en la línea 7; en caso contrario, no se ejecutará la aplicación. Una parte clave para permitir que
la JVM localice y llame al método main para empezar la ejecución de la aplicación es la palabra clave static
(línea 7), la cual indica que main es un método static. Un método static es especial, ya que puede llamarse
sin tener que crear primero un objeto de la clase en la cual se declara ese método. En el capítulo 6, Métodos: un
análisis más detallado, explicaremos a detalle los métodos static.
Figura 3.2 | Cómo crear un objeto de la clase LibroCalificaciones y llamar a su método mostrarMensaje.
1 // Fig. 3.2: PruebaLibroCalificaciones.java
2 // Crea un objeto LibroCalificaciones y llama a su método mostrarMensaje.
3
4 public class PruebaLibroCalificaciones
5 {
6 // el método main empieza la ejecución del programa
7 public static void main( String args[] )
8 {
9 // crea un objeto LibroCalificaciones y lo asigna a miLibroCalificaciones
10 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones();
11
12 // llama al método mostrarMensaje de miLibroCalificaciones
13 miLibroCalificaciones.mostrarMensaje();
14 } // fin de main
15
16 } // fin de la clase PruebaLibroCalificaciones
Bienvenido al Libro Calificaciones!
3.3 Declaración de una clase con un método e instanciamiento de un objeto de una clase 79
80 Capítulo 3 Introducción a las clases y los objetos
En esta aplicación nos gustaría llamar al método mostrarMensaje de la clase LibroCalificaciones para
mostrar el mensaje de bienvenida en la ventana de comandos. Por lo general, no podemos llamar a un método
que pertenece a otra clase, sino hasta crear un objeto de esa clase, como se muestra en la línea 10. Empezaremos
por declarar la variable miLibroCalificaciones. Observe que el tipo de la variable es LibroCalificaciones; la
clase que declaramos en la figura 3.1. Cada nueva clase que creamos se convierte en un nuevo tipo, que puede
usarse para declarar variables y crear objetos. Los programadores pueden declarar nuevos tipos de clases según lo
necesiten; ésta es una razón por la cual Java se conoce como un lenguaje extensible.
La variable miLibroCalificaciones se inicializa con el resultado de la expresión de creación de instan-
cia de clase new LibroCalificaciones(). La palabra clave new crea un nuevo objeto de la clase especificada a la
derecha de la palabra clave (es decir, LibroCalificaciones). Los paréntesis a la derecha de LibroCalificaciones
son obligatorios. Como veremos en la sección 3.7, esos paréntesis en combinación con el nombre de una clase
representan una llamada a un constructor, que es similar a un método, pero se utiliza sólo cuando se crea un
objeto, para inicializar los datos de éste. En esa sección verá que los datos pueden colocarse entre paréntesis para
especificar los valores iniciales para los datos del objeto. Por ahora, sólo dejaremos los paréntesis vacíos.
Así como podemos usar el objeto System.out para llamar a los métodos print, printf y println, tam-
bién podemos usar el objeto miLibroCalificaciones para llamar al método mostrarMensaje. La línea 13 llama
al método mostrarMensaje (líneas 7-10 de la figura 3.1), usando miLibroCalificaciones seguida de un sepa-
rador punto (.), el nombre del método mostrarMensaje y un conjunto vacío de paréntesis. Esta llamada hace
que el método mostrarMensaje realice su tarea. La llamada a este método difiere de las del capítulo 2 en las que
se mostraba la información en una ventana de comandos; cada una de estas llamadas al método proporcionaban
argumentos que especificaban los datos a mostrar. Al inicio de la línea 13, “miLibroCalificaciones”. Indica que
main debe utilizar el objeto miLibroCalificaciones que se creó en la línea 10. La línea 7 de la figura 3.1 indica
que el método mostrarMensaje tiene una lista de parámetros vacía; es decir, mostrarMensaje no requiere infor-
mación adicional para realizar su tarea. Por esta razón, la llamada al método (línea 13 de la figura 3.2) especifica
un conjunto vacío de paréntesis después del nombre del método, para indicar que no se van a pasar argumentos
al método mostrarMensaje. Cuando el método mostrarMensaje completa su tarea, el método main continúa
su ejecución en la línea 14. Éste es el final del método main, por lo que el programa termina.
Compilación de una aplicación con varias clases
Debe compilar las clases de las figuras 3.1 y 3.2 antes de poder ejecutar la aplicación. Primero, cambie al directorio
que contiene los archivos de código fuente de la aplicación. Después, escriba el comando
javac LibroCalificaciones.java PruebaLibroCalificaciones.java
para compilar ambas clases a la vez. Si el directorio que contiene la aplicación sólo incluye los archivos de esta
aplicación, puede compilar todas las clases que haya en el directorio con el comando
javac *.java
El asterisco (*) en *.java indica que deben compilarse todos los archivos en el directorio actual que terminen
con la extensión de nombre de archivo “.java”.
Diagrama de clases de UML para la clase LibroCalificaciones
La figura 3.3 presenta un diagrama de clases de UML para la clase LibroCalificaciones de la figura 3.1. En
la sección 1.16 vimos que UML es un lenguaje gráfico, utilizado por los programadores para representar sistemas
orientados a objetos en forma estandarizada. En UML, cada clase se modela en un diagrama de clases en forma
de un rectángulo con tres componentes. El compartimiento superior contiene el nombre de la clase, centrado
en forma horizontal y en negrita. El compartimiento de en medio contiene los atributos de la clase, que en Java
corresponden a las variables de instancia. En la figura 3.3, el compartimiento de en medio está vacío, ya que la
versión de la clase LibroCalificaciones en la figura 3.1 no tiene atributos. El compartimiento inferior contiene
las operaciones de la clase, que en Java corresponden a los métodos. Para modelar las operaciones, UML lista el
nombre de la operación precedido por un modificador de acceso y seguido de un conjunto de paréntesis. La clase
LibroCalificaciones tiene un solo método llamado mostrarMensaje, por lo que el compartimiento inferior de
la figura 3.3 lista una operación con este nombre. El método mostrarMensaje no requiere información adicio-
nal para realizar sus tareas, por lo que los paréntesis que van después del nombre del método en el diagrama de
clases están vacíos, de igual forma que como aparecieron en la declaración del método, en la línea 7 de la figura
3.1. El signo más (+) que va antes del nombre de la operación indica que mostrarMensaje es una operación
public en UML (es decir, un método public en Java). Utilizaremos los diagramas de clases de UML a menudo
para sintetizar los atributos y las operaciones de una clase.
3.4 Declaración de un método con un parámetro
En nuestra analogía del automóvil de la sección 3.2, hablamos sobre el hecho de que al oprimir el pedal del acele-
rador se envía un mensaje al automóvil para que realice una tarea: hacer que vaya más rápido. Pero ¿qué tan rápido
debería acelerar el automóvil? Como sabe, entre más oprima el pedal, mayor será la aceleración del automóvil.
Por lo tanto, el mensaje para el automóvil en realidad incluye tanto la tarea a realizar como información adicional
que ayuda al automóvil a ejecutar su tarea. A la información adicional se le conoce como parámetro; el valor del
parámetro ayuda al automóvil a determinar qué tan rápido debe acelerar. De manera similar, un método puede
requerir uno o más parámetros que representan la información adicional que necesita para realizar su tarea. La
llamada a un método proporciona valores (llamados argumentos) para cada uno de los parámetros de ese méto-
do. Por ejemplo, el método System.out.println requiere un argumento que especifica los datos a mostrar en
una ventana de comandos. De manera similar, para realizar un depósito en una cuenta bancaria, un método
llamado deposito especifica un parámetro que representa el monto a depositar. Cuando se hace una llamada
al método deposito, se asigna al parámetro del método un valor como argumento, que representa el monto a
depositar. Entonces, el método realiza un depósito por ese monto.
Nuestro siguiente ejemplo declara la clase LibroCalificaciones (figura 3.4), con un método mostrar-
Mensaje que muestra el nombre del curso como parte del mensaje de bienvenida (en la figura 3.5 podrá ver la
ejecución de ejemplo). El nuevo método mostrarMensaje requiere un parámetro que representa el nombre del
curso a imprimir en pantalla.
Antes de hablar sobre las nuevas características de la clase LibroCalificaciones, veamos cómo se utiliza la
nueva clase desde el método main de la clase PruebaLibroCalificaciones (figura 3.5). La línea 12 crea un obje-
to Scanner llamado entrada, para recibir el nombre del curso escrito por el usuario. La línea 15 crea un objeto
de la clase LibroCalificaciones y lo asigna a la variable miLibroCalificaciones. La línea 18 pide al usuario
que escriba el nombre de un curso. La línea 19 lee el nombre que introduce el usuario y lo asigna a la variable
nombreDelCurso, mediante el uso del método nextLine de Scanner para realizar la operación de entrada. El
Figura 3.3 | Diagrama de clases de UML, el cual indica que la clase LibroCalificaciones tiene una operación
public llamada mostrarMensaje.
LibroCalificaciones
+ mostrarMensaje( )
Figura 3.4 | Declaración de una clase con un método que tiene un parámetro.
1 // Fig. 3.4: LibroCalificaciones.java
2 // Declaración de una clase con un método que tiene un parámetro.
3
4 public class LibroCalificaciones
5 {
6 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones
7 public void mostrarMensaje( String nombreDelCurso )
8 {
9 System.out.printf( “Bienvenido al libro de calificaciones paran%s!n”,
10 nombreDelCurso );
11 } // fin del método mostrarMensaje
12
13 } // fin de la clase LibroCalificaciones
3.4 Declaración de un método con un parámetro 81
82 Capítulo 3 Introducción a las clases y los objetos
usuario escribe el nombre del curso y oprime Intro para enviarlo al programa. Observe que al oprimir Intro se
inserta un carácter de nueva línea al final de los caracteres escritos por el usuario. El método nextLine lee los
caracteres que escribió el usuario hasta encontrar el carácter de nueva línea, y después devuelve un objeto String
que contiene los caracteres hasta, pero sin incluir, la nueva línea. El carácter de nueva línea se descarta. La clase
Scanner también cuenta con un método similar (next) para leer palabras individuales. Cuando el usuario oprime
Intro después de escribir la entrada, el método next lee caracteres hasta encontrar un carácter de espacio en blanco
(espacio, tabulador o nueva línea), y después devuelve un objeto String que contiene los caracteres hasta, pero
sin incluir, el carácter de espacio en blanco (que se descarta). No se pierde toda la información que va después
del primer carácter de espacio en blanco; estará disponible para que la lean otras instrucciones que llamen a los
métodos de Scanner, más adelante en el programa.
La línea 24 llama al método mostrarMensaje de miLibroCalificaciones. La variable nombreDelCurso
entre paréntesis es el argumento que se pasa al método mostrarMensaje, para que éste pueda realizar su tarea. El
valor de la variable nombreDelCurso en main se convierte en el valor del parámetro nombreDelCurso del método
mostrarMensaje, en la línea 7 de la figura 3.4. Al ejecutar esta aplicación, observe que el método mostrarMen-
saje imprime en pantalla el nombre que usted escribió como parte del mensaje de bienvenida (figura 3.5).
Observación de ingeniería de software 3.1
Por lo general, los objetos se crean mediante el uso de new. Una excepción es la literal de cadena que está encerrada
entre comillas, como “hola”. Las literales de cadena son referencias a objetos String que Java crea de manera
implícita.
1 // Fig. 3.5: PruebaLibroCalificaciones.java
2 // Crea un objeto LibroCalificaciones y pasa un objeto String
3 // a su método mostrarMensaje.
4 import java.util.Scanner; // el programa usa la clase Scanner
5
6 public class PruebaLibroCalificaciones
7 {
8 // el método main empieza la ejecución del programa
9 public static void main( String args[] )
10 {
11 // crea un objeto Scanner para obtener la entrada de la ventana de comandos
12 Scanner entrada = new Scanner( System.in );
13
14 // crea un objeto LibroCalificaciones y lo asigna a miLibroCalificaciones
15 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones();
16
17 // pide y recibe el nombre del curso como entrada
18 System.out.println( “Escriba el nombre del curso:” );
19 String nombreDelCurso = entrada.nextLine(); // lee una línea de texto
20 System.out.println(); // imprime una línea en blanco
21
22 // llama al método mostrarMensaje de miLibroCalificaciones
23 // y pasa nombreDelCurso como argumento
24 miLibroCalificaciones.mostrarMensaje( nombreDelCurso );
25 } // fin de main
26
27 } // fin de la clase PruebaLibroCalificaciones
Escriba el nombre del curso:
CS101 Introduccion a la programacion en Java
Bienvenido al libro de calificaciones para
CS101 Introduccion a la programacion en Java!
Figura 3.5 | Cómo crear un objeto LibroCalificaciones y pasar un objeto String a su método mostrarMensaje.
Más sobre los argumentos y los parámetros
Al declarar un método, debe especificar si el método requiere datos para realizar su tarea. Para ello es necesario
colocar información adicional en la lista de parámetros del método, la cual se encuentra en los paréntesis que van
después del nombre del método. La lista de parámetros puede contener cualquier número de parámetros, incluso
ninguno. Los paréntesis vacíos después del nombre del método (como en la figura 3.1, línea 7) indican que un
método no requiere parámetros. En la figura 3.4, la lista de parámetros de mostrarMensaje (línea 7) declara que
el método requiere un parámetro. Cada parámetro debe especificar un tipo y un identificador. En este caso, el tipo
String y el identificador nombreDelCurso indican que el método mostrarMensaje requiere un objeto String
para realizar su tarea. En el instante en que se llama al método, el valor del argumento en la llamada se asigna
al parámetro correspondiente (en este caso, nombreDelCurso) en el encabezado del método. Después, el cuerpo
del método utiliza el parámetro nombreDelCurso para acceder al valor. Las líneas 9 y 10 de la figura 3.4 muestran
el valor del parámetro nombreDelCurso, mediante el uso del especificador de formato %s en la cadena de formato
de printf. Observe que el nombre de la variable de parámetro (figura 3.4, línea 7) puede ser igual o distinto al
nombre de la variable de argumento (figura 3.5, línea 24).
Un método puede especificar múltiples parámetros; sólo hay que separar un parámetro de otro mediante una
coma (en el capítulo 6 veremos un ejemplo de esto). El número de argumentos en la llamada a un método debe
coincidir con el número de parámetros en la lista de parámetros de la declaración del método que se llamó. Ade-
más, los tipos de los argumentos en la llamada al método deben ser “consistentes con” los tipos de los parámetros
correspondientes en la declaración del método (como veremos en capítulos posteriores, no siempre se requiere
que el tipo de un argumento y el tipo de su correspondiente parámetro sean idénticos). En nuestro ejemplo, la
llamada al método pasa un argumento de tipo String (nombreDelCurso se declara como String en la línea 19
de la figura 3.5) y la declaración del método especifica un parámetro de tipo String (línea 7 en la figura 3.4).
Por lo tanto, en este ejemplo, el tipo del argumento en la llamada al método coincide exactamente con el tipo del
parámetro en el encabezado del método.
Error común de programación 3.2
Si el número de argumentos en la llamada a un método no coincide con el número de parámetros en la declaración
del método, se produce un error de compilación.
Error común de programación 3.3
Si los tipos de los argumentos en la llamada a un método no son consistentes con los tipos de los parámetros corres-
pondientes en la declaración del método, se produce un error de compilación.
Diagrama de clases de UML actualizado para la clase LibroCalificaciones
El diagrama de clases de UML de la figura 3.6 modela la clase LibroCalificaciones de la figura 3.4. Al igual
que la Figura 3.1, esta clase LibroCalificaciones contiene la operación public llamada mostrarMensaje. Sin
embargo, esta versión de mostrarMensaje tiene un parámetro. La forma en que UML modela un parámetro es
un poco distinta a la de Java, ya que lista el nombre del parámetro, seguido de dos puntos y del tipo del parámetro
entre paréntesis, después del nombre de la operación. UML tiene sus propios tipos de datos, que son similares a
los de Java (pero como veremos, no todos los tipos de datos de UML tienen los mismos nombres que los tipos
correspondientes en Java). El tipo String de UML corresponde al tipo String de Java. El método mostrarMen-
saje de LibroCalificaciones (figura 3.4) tiene un parámetro String llamado nombreDelCurso, por lo que en
la figura 3.6 se lista a nombreDelCurso : String entre los paréntesis que van después de mostrarMensaje.
Figura 3.6 | Diagrama de clases de UML, que indica que la clase LibroCalificaciones tiene una operación
llamada mostrarMensaje, con un parámetro llamado nombreDelCurso de tipo String de UML.
LibroCalificaciones
+ mostrarMensaje( nombreDelCurso : String )
3.4 Declaración de un método con un parámetro 83
84 Capítulo 3 Introducción a las clases y los objetos
Observaciones acerca del uso de las declaraciones import
Observe la declaración import en la figura 3.5 (línea 4). Esto indica al compilador que el programa utiliza la clase
Scanner. ¿Por qué necesitamos importar la clase Scanner, pero no las clases System, String o LibroCalifica-
ciones? La mayoría de las clases que utilizará en los programas de Java deben importarse. Las clases System y
String están en el paquete java.lang, que se importa de manera implícita en todo programa de Java, por lo que
todos los programas pueden usar las clases del paquete java.lang sin tener que importarlas de manera explícita.
Hay una relación especial entre las clases que se compilan en el mismo directorio en el disco, como las cla-
ses LibroCalificaciones y PruebaLibroCalificaciones. De manera predeterminada, se considera que dichas
clases se encuentran en el mismo paquete; a éste se le conoce como el paquete predeterminado. Las clases en
el mismo paquete se importan implícitamente en los archivos de código fuente de las otras clases en el mismo
paquete. Por ende, no se requiere una declaración import cuando la clase en un paquete utiliza a otra en el mis-
mo paquete; como cuando PruebaLibroCalificaciones utiliza a la clase LibroCalificaciones.
La declaración import en la línea 4 no es obligatoria si siempre hacemos referencia a la clase Scanner como
java.util.Scanner, que incluye el nombre completo del paquete y de la clase. Esto se conoce como el nombre
de clase completamente calificado. Por ejemplo, la línea 12 podría escribirse como
java.util.Scanner entrada = new java.util.Scanner( System.in );
Observación de ingeniería de software 3.2
El compilador de Java no requiere declaraciones import en un archivo de código fuente de Java, si se especifica el
nombre de clase completamente calificado cada vez que se utilice el nombre de una clase en el código fuente. Pero la
mayoría de los programadores de Java consideran que el uso de nombres completamente calificados es incómodo, por
lo cual prefieren usar declaraciones import.
3.5 Variables de instancia, métodos establecer y métodos obtener
En el capítulo 2 declaramos todas las variables de una aplicación en el método main. Las variables que se declaran
en el cuerpo de un método específico se conocen como variables locales, y sólo se pueden utilizar en ese método.
Cuando termina ese método, se pierden los valores de sus variables locales. En la sección 3.2 vimos que un objeto
tiene atributos que lleva consigo cuando se utiliza en un programa. Dichos atributos existen antes de que un
objeto llame a un método, y después de que el método completa su ejecución.
Por lo general, una clase consiste en uno o más métodos que manipulan los atributos pertenecientes a un
objeto específico de la clase. Los atributos se representan como variables en la declaración de la clase. Dichas
variables se llaman campos, y se declaran dentro de la declaración de una clase, pero fuera de los cuerpos de las
declaraciones de los métodos de la clase. Cuando cada objeto de una clase mantiene su propia copia de un atribu-
to, el campo que representa a ese atributo se conoce también como variable de instancia; cada objeto (instancia)
de la clase tiene una instancia separada de la variable en memoria. El ejemplo en esta sección demuestra una
clase LibroCalificaciones, que contiene una variable de instancia llamada nombreDelCurso para representar el
nombre del curso de un objeto LibroCalificaciones específico.
La clase LibroCalificaciones con una variable de instancia, un método establecer
y un método obtener
En nuestra siguiente aplicación (figuras 3.7 y 3.8), la clase LibroCalificaciones (figura 3.7) mantiene el nom-
bre del curso como una variable de instancia, para que pueda usarse o modificarse en cualquier momento, durante
la ejecución de una aplicación. Esta clase contiene tres métodos: establecerNombreDelCurso, obtenerNom-
breDelCurso y mostrarMensaje. El método establecerNombreDelCurso almacena el nombre de un curso en
un LibroCalificaciones. El método obtenerNombreDelCurso obtiene el nombre del curso de un LibroCali-
ficaciones. El método mostrarMensaje, que en este caso no especifica parámetros, sigue mostrando un mensaje
de bienvenida que incluye el nombre del curso; como veremos más adelante, el método ahora obtiene el nombre
del curso mediante una llamada a otro método en la misma clase: obtenerNombreDelCurso.
Por lo general, un instructor enseña más de un curso, cada uno con su propio nombre. La línea 7 declara que
nombreDelCurso es una variable de tipo String. Como la variable se declara en el cuerpo de la clase, pero fuera
de los cuerpos de los métodos de la misma (líneas 10 a la 13, 16 a la 19 y 22 a la 28), la línea 7 es una declaración
para una variable de instancia. Cada instancia (es decir, objeto) de la clase LibroCalificaciones contiene una
copia de cada variable de instancia. Por ejemplo, si hay dos objetos LibroCalificaciones, cada objeto tiene su
propia copia de nombreDelCurso (una por cada objeto). Un beneficio de hacer de nombreDelCurso una variable
de instancia es que todos los métodos de la clase (en este caso, LibroCalificaciones) pueden manipular cual-
quier variable de instancia que aparezca en la clase (en este caso, nombreDelCurso).
Los modificadores de acceso public y private
La mayoría de las declaraciones de variables de instancia van precedidas por la palabra clave private (como en la
línea 7). Al igual que public, la palabra clave private es un modificador de acceso. Las variables o los méto-
dos declarados con el modificador de acceso private son accesibles sólo para los métodos de la clase en la que
se declaran. Así, la variable nombreDelCurso sólo puede utilizarse en los métodos establecerNombreDelCurso,
obtenerNombreDelCurso y mostrarMensaje de (cada objeto de) la clase LibroCalificaciones.
Observación de ingeniería de software 3.3
Es necesario colocar un modificador de acceso antes de cada declaración de un campo y de un método. Como regla empí-
rica, las variables de instancia deben declararse como private y los métodos, como public. (Más adelante veremos
que es apropiado declarar ciertos métodos como private, si sólo van a estar accesibles por otros métodos de la clase).
Buena práctica de programación 3.1
Preferimos listar los campos de una clase primero, para que, a medida que usted lea el código, pueda ver los nom-
bres y tipos de las variables antes de ver su uso en los métodos de la clase. Es posible listar los campos de la clase en
cualquier parte de la misma, fuera de las declaraciones de sus métodos, pero si se esparcen por todo el código, éste será
más difícil de leer.
1 // Fig. 3.7: LibroCalificaciones.java
2 // Clase LibroCalificaciones que contiene una variable de instancia nombreDelCurso
3 // y métodos para establecer y obtener su valor.
4
5 public class LibroCalificaciones
6 {
7 private String nombreDelCurso; // nombre del curso para este LibroCalificaciones
8
9 // método para establecer el nombre del curso
10 public void establecerNombreDelCurso( String nombre )
11 {
12 nombreDelCurso = nombre; // almacena el nombre del curso
13 } // fin del método establecerNombreDelCurso
14
15 // método para obtener el nombre del curso
16 public String obtenerNombreDelCurso()
17 {
18 return nombreDelCurso;
19 } // fin del método obtenerNombreDelCurso
20
21 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones
22 public void mostrarMensaje()
23 {
24 // esta instrucción llama a obtenerNombreDelCurso para obtener el
25 // nombre del curso que representa este LibroCalificaciones
26 System.out.printf( “Bienvenido al libro de calificaciones paran%s!n”,
27 obtenerNombreDelCurso() );
28 } // fin del método mostrarMensaje
29
30 } // fin de la clase LibroCalificaciones
Figura 3.7 | Clase LibroCalificaciones que contiene una variable de instancia nombreDelCurso y métodos para
establecer y obtener su valor.
3.5 Variables de instancia, métodos establecer y métodos obtener 85
86 Capítulo 3 Introducción a las clases y los objetos
Buena práctica de programación 3.2
Coloque una línea en blanco entre las declaraciones de los métodos, para separarlos y mejorar la legibilidad del
programa.
El proceso de declarar variables de instancia con el modificador de acceso private se conoce como oculta-
miento de datos. Cuando un programa crea (instancia) un objeto de la clase LibroCalificaciones, la variable
nombreDelCurso se encapsula (oculta) en el objeto, y sólo está accesible para los métodos de la clase de ese objeto.
En la clase LibroCalificaciones, los métodos establecerNombreDelCurso y obtenerNombreDelCurso mani-
pulan a la variable de instancia nombreDelCurso.
El método establecerNombreDelCurso (líneas 10 a la 13) no devuelve datos cuando completa su tarea, por
lo que su tipo de valor de retorno es void. El método recibe un parámetro (nombre), el cual representa el nom-
bre del curso que se pasará al método como un argumento. La línea 12 asigna nombre a la variable de instancia
nombreDelCurso.
El método obtenerNombreDelCurso (líneas 16 a la 19) devuelve un nombreDelCurso de un objeto Libro-
Calificaciones específico. El método tiene una lista de parámetros vacía, por lo que no requiere información
adicional para realizar su tarea. El método especifica que devuelve un objeto String; a éste se le conoce como el
tipo de valor de retorno del método. Cuando se hace una llamada a un método que especifica un tipo de valor
de retorno, y éste completa su tarea, devuelve un resultado al método que lo llamó. Por ejemplo, cuando usted
va a un cajero automático (ATM) y solicita el saldo de su cuenta, espera que el ATM le devuelva un valor que
representa su saldo. De manera similar, cuando una instrucción llama al método obtenerNombreDelCurso en un
objeto LibroCalificaciones, la instrucción espera recibir el nombre del curso de LibroCalificaciones (en este
caso, un objeto String, como se especifica en el tipo de valor de retorno de la declaración del método). Si tiene
un método cuadrado que devuelve el cuadrado de su argumento, es de esperarse que la instrucción
int resultado = cuadrado( 2 );
devuelva 4 del método cuadrado y asigne 4 a la variable resultado. Si tiene un método maximo que devuelve el
mayor de tres argumentos enteros, es de esperarse que la siguiente instrucción
int mayor = maximo( 27, 114, 51 );
devuelva 114 del método maximo y asigne 114 a la variable mayor.
Observe que cada una de las instrucciones en las líneas 12 y 18 utilizan nombreDelCurso, aun cuando esta
variable no se declaró en ninguno de los métodos. Podemos utilizar nombreDelCurso en los métodos de la clase
LibroCalificaciones, ya que nombreDelCurso es un campo de la clase. Observe además que el orden en el que
se declaran los métodos en una clase no determina cuándo se van a llamar en tiempo de ejecución. Por lo tanto,
el método obtenerNombreDelCurso podría declararse antes del método establecerNombreDelCurso.
El método mostrarMensaje (líneas 22 a la 28) no devuelve datos cuando completa su tarea, por lo que su
tipo de valor de retorno es void. El método no recibe parámetros, por lo que la lista de parámetros está vacía. Las
líneas 26 y 27 imprimen un mensaje de bienvenida, que incluye el valor de la variable de instancia nombreDel-
Curso. Una vez más, necesitamos crear un objeto de la clase LibroCalificaciones y llamar a sus métodos para
poder mostrar en pantalla el mensaje de bienvenida.
La clase PruebaLibroCalificaciones que demuestra a la clase LibroCalificaciones
La clase PruebaLibroCalificaciones (figura 3.8) crea un objeto de la clase LibroCalificaciones y demuestra
el uso de sus métodos. La línea 11 crea un objeto Scanner, que se utilizará para obtener el nombre de un curso
del usuario. La línea 14 crea un objeto LibroCalificaciones y lo asigna a la variable local miLibroCalificacio-
nes, de tipo LibroCalificaciones. Las líneas 17-18 muestran el nombre inicial del curso mediante una llamada
al método obtenerNombreDelCurso del objeto. Observe que la primera línea de la salida muestra el nombre
“null”. A diferencia de las variables locales, que no se inicializan de manera automática, cada campo tiene un
valor inicial predeterminado: un valor que Java proporciona cuando el programador no especifica el valor inicial
del campo. Por ende, no se requiere que los campos se inicialicen explícitamente antes de usarlos en un programa,
a menos que deban inicializarse con valores distintos de los predeterminados. El valor predeterminado para un
campo de tipo String (como nombreDelCurso en este ejemplo) es null, de lo cual hablaremos con más detalle
en la sección 3.6.
La línea 21 pide al usuario que escriba el nombre para el curso. La variable String local elNombre (decla-
rada en la línea 22) se inicializa con el nombre del curso que escribió el usuario, el cual se devuelve mediante
la llamada al método nextLine del objeto Scanner llamado entrada. La línea 23 llama al método estable-
cerNombreDelCurso del objeto miLibroCalificaciones y provee elNombre como argumento para el método.
Cuando se hace la llamada al método, el valor del argumento se asigna al parámetro nombre (línea 10, figura 3.7)
del método establecerNombreDelCurso (líneas 10 a la 13, figura 3.7). Después, el valor del parámetro se asigna
a la variable de instancia nombreDelCurso (línea 12, figura 3.7). La línea 24 (figura 3.8) salta una línea en la
salida, y después la línea 27 llama al método mostrarMensaje del objeto miLibroCalificaciones para mostrar
en pantalla el mensaje de bienvenida, que contiene el nombre del curso.
Los métodos establecer y obtener
Los campos private de una clase pueden manipularse sólo mediante los métodos de esa clase. Por lo tanto, un
cliente de un objeto (es decir, cualquier clase que llame a los métodos del objeto) llama a los métodos public de
la clase para manipular los campos private de un objeto de esa clase. Esto explica por qué las instrucciones en
el método main (figura 3.8) llaman a los métodos establecerNombreDelCurso, obtenerNombreDelCurso y
mostrarMensaje en un objeto LibroCalificaciones. A menudo, las clases proporcionan métodos public para
permitir a los clientes de la clase establecer (es decir, asignar valores a) u obtener (es decir, obtener los valores
de) variables de instancia private. Los nombres de estos métodos no necesitan empezar con establecer u obtener,
pero esta convención de nomenclatura es muy recomendada en Java, y es requerida para ciertos componentes
de software especiales de Java, conocidos como JavaBeans, que pueden simplificar la programación en muchos
entornos de desarrollo integrados (IDEs). El método que establece la variable nombreDelCurso en este ejemplo se
llama establecerNombreDelCurso, y el método que obtiene el valor de la variable de instancia nombreDelCurso
se llama obtenerNombreDelCurso.
Figura 3.8 | Creación y manipulación de un objeto LibroCalificaciones. (Parte 1 de 2).
1 // Fig. 3.8: PruebaLibroCalificaciones.java
2 // Crea y manipula un objeto LibroCalificaciones.
3 import java.util.Scanner; // el programa usa la clase Scanner
4
5 public class PruebaLibroCalificaciones
6 {
7 // el método main empieza la ejecución del programa
8 public static void main( String args[] )
9 {
10 // crea un objeto Scanner para obtener la entrada de la ventana de comandos
11 Scanner entrada = new Scanner( System.in );
12
13 // crea un objeto LibroCalificaciones y lo asigna a miLibroCalificaciones
14 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones();
15
16 // muestra el valor inicial de nombreDelCurso
17 System.out.printf( “El nombre inicial del curso es: %snn”,
18 miLibroCalificaciones.obtenerNombreDelCurso() );
19
20 // pide y lee el nombre del curso
21 System.out.println( “Escriba el nombre del curso:” );
22 String elNombre = entrada.nextLine(); // lee una línea de texto
23 miLibroCalificaciones.establecerNombreDelCurso( elNombre ); // establece el nombre
del curso
24 System.out.println(); // imprime una línea en blanco
25
26 // muestra el mensaje de bienvenida después de especificar el nombre del curso
27 miLibroCalificaciones.mostrarMensaje();
28 } // fin de main
29
30 } // fin de la clase PruebaLibroCalificaciones
3.5 Variables de instancia, métodos establecer y métodos obtener 87
88 Capítulo 3 Introducción a las clases y los objetos
Diagrama de clases de UML para la clase LibroCalificaciones con una variable de instancia,
y métodos establecer y obtener
La figura 3.9 contiene un diagrama de clases de UML actualizado para la versión de la clase LibroCalifica-
ciones de la figura 3.7. Este diagrama modela la variable de instancia nombreDelCurso de la clase Libro-
Calificaciones como un atributo en el compartimiento intermedio de la clase. UML representa a las variables de
instancia como atributos, listando el nombre del atributo, seguido de dos puntos y del tipo del atributo. El tipo
de UML del atributo nombreDelCurso es String. La variable de instancia nombreDelCurso es private en Java,
por lo que el diagrama de clases lista un signo menos (-) en frente del nombre del atributo correspondiente. La
clase LibroCalificaciones contiene tres métodos public, por lo que el diagrama de clases lista tres operaciones
en el tercer compartimiento. Recuerde que el signo más (+) antes de cada nombre de operación indica que ésta es
public. La operación establecerNombreDelCurso tiene un parámetro String llamado nombre. UML indica
el tipo de valor de retorno de una operación colocando dos puntos y el tipo de valor de retorno después de los
paréntesis que le siguen al nombre de la operación. El método obtenerNombreDelCurso de la clase LibroCali-
ficaciones (figura 3.7) tiene un tipo de valor de retorno String en Java, por lo que el diagrama de clases muestra
un tipo de valor de retorno String en UML. Observe que las operaciones establecerNombreDelCurso y mos-
trarMensaje no devuelven valores (es decir, devuelven void en Java), por lo que el diagrama de clases de UML
no especifica un tipo de valor de retorno después de los paréntesis de estas operaciones.
El nombre inicial del curso es: null
Escriba el nombre del curso:
CS101 Introduccion a la programacion en Java
Bienvenido al libro de calificaciones para
CS101 Introduccion a la programacion en Java!
Figura 3.8 | Creación y manipulación de un objeto LibroCalificaciones. (Parte 2 de 2).
Figura 3.9 | Diagrama de clases de UML, en el que se indica que la clase LibroCalificaciones tiene un atributo
nombreDelCurso de tipo String en UML, y tres operaciones: establecerNombreDelCurso (con un pará-
metro nombre de tipo String de UML), obtenerNombreDelCurso (que devuelve el tipo String de UML) y
mostrarMensaje.
LibroCalificaciones
– nombreDelCurso : String
+ establecerNombreDelCurso( nombre : String )
+ obtenerNombreDelCurso( ) : String
+ mostrarMensaje( )
3.6 Comparación entre tipos primitivos y tipos por referencia
Los tipos de datos en Java se dividen en dos categorías: tipos primitivos y tipos por referencia (algunas veces
conocidos como tipos no primitivos). Los tipos primitivos son boolean, byte, char, short, int, long,
float y double. Todos los tipos no primitivos son tipos por referencia, por lo cual las clases, que especifican los
tipos de objetos, son tipos por referencia.
Una variable de tipo primitivo puede almacenar sólo un valor de su tipo declarado a la vez. Por ejemplo, una
variable int puede almacenar un número completo (como 7) a la vez. Cuando se asigna otro valor a esa variable,
se sustituye su valor inicial. Las variables de instancia de tipo primitivo se inicializan de manera predeterminada;
las variables de los tipos byte, char, short, int, long, float y double se inicializan con 0, y las variables
de tipo boolean se inicializan con false. Usted puede especificar sus propios valores iniciales para las variables de
tipo primitivo. Recuerde que las variables locales no se inicializan de manera predeterminada.
Tip para prevenir errores 3.1
Cualquier intento de utilizar una variable local que no se haya inicializado produce un error de compilación.
Los programas utilizan variables de tipo por referencia (que por lo general se llaman referencias) para alma-
cenar las ubicaciones de los objetos en la memoria de la computadora. Se dice que dicha variable hace referencia a
un objeto en el programa. Cada uno de los objetos a los que se hace referencia pueden contener muchas variables
de instancia y métodos. La línea 14 de la figura 3.8 crea un objeto de la clase LibroCalificaciones, y la variable
miLibroCalificaciones contiene una referencia a ese objeto LibroCalificaciones. Las variables de instancia
de tipo por referencia se inicializan de manera predeterminada con el valor null: una palabra reservada que repre-
senta una “referencia a nada”. Esto explica por qué la primera llamada a obtenerNombreDelCurso en la línea 18
de la figura 3.8 devolvía null; no se había establecido el valor de nombreDelCurso, por lo que se devolvía el valor
inicial predeterminado null. En el apéndice C, Palabras clave y palabras reservadas, se muestra una lista completa
de las palabras reservadas y las palabras clave.
Es obligatorio que una referencia a un objeto invoque (es decir, llame) a los métodos de un objeto. En la
aplicación de la figura 3.8, las instrucciones en el método main utilizan la variable miLibroCalificaciones para
enviar mensajes al objeto LibroCalificaciones. Estos mensajes son llamadas a métodos (como establecer-
NombreDelCurso y obtenerNombreDelCurso) que permiten al programa interactuar con el objeto LibroCali-
ficaciones. Por ejemplo, la instrucción en la línea 23 utiliza a miLibroCalificaciones para enviar el mensaje
establecerNombreDelCurso al objeto LibroCalificaciones. El mensaje incluye el argumento que requiere es-
tablecerNombreDelCurso para realizar su tarea. El objeto LibroCalificaciones utiliza esta información para
establecer la variable de instancia nombreDelCurso. Tenga en cuenta que las variables de tipo primitivo no hacen
referencias a objetos, por lo que dichas variables no pueden utilizarse para invocar métodos.
Observación de ingeniería de software 3.4
El tipo declarado de una variable (por ejemplo, int, double o LibroCalificaciones) indica si la variable es de
tipo primitivo o por referencia. Si el tipo de una variable no es uno de los ocho tipos primitivos, entonces es un tipo
por referencia. (Por ejemplo, Cuenta cuenta1 indica que cuenta1 es una referencia a un objeto Cuenta).
3.7 Inicialización de objetos mediante constructores
Como mencionamos en la sección 3.5, cuando se crea un objeto de la clase LibroCalificaciones (figura 3.7),
su variable de instancia nombreCurso se inicializa con null de manera predeterminada. ¿Qué pasa si usted desea
proporcionar el nombre de un curso a la hora de crear un objeto LibroCalificaciones? Cada clase que usted
declare puede proporcionar un constructor, el cual puede utilizarse para inicializar un objeto de una clase al
momento de crear ese objeto. De hecho, Java requiere una llamada al constructor para cada objeto que se crea.
La palabra clave new llama al constructor de la clase para realizar la inicialización. La llamada al constructor se
indica mediante el nombre de la clase, seguido de paréntesis; el constructor debe tener el mismo nombre que la
clase. Por ejemplo, la línea 14 de la figura 3.8 primero utiliza new para crear un objeto LibroCalificaciones. Los
paréntesis vacíos después de "new LibroCalificaciones" indican una llamada sin argumentos al constructor de
la clase. De manera predeterminada, el compilador proporciona un constructor predeterminado sin parámetros,
en cualquier clase que no incluya un constructor en forma explícita. Cuando una clase sólo tiene el constructor
predeterminado, sus variables de instancia se inicializan con sus valores predeterminados. Las variables de los tipos
char, byte, short, int, long, float y double se inicializan con 0, las variables de tipo boolean se inicializan con
false, y las variables de tipo por referencia se inicializan con null.
Cuando usted declara una clase, puede proporcionar su propio constructor para especificar una inicialización
personalizada para los objetos de su clase. Por ejemplo, tal vez un programador quiera especificar el nombre de un
curso para un objeto LibroCalificaciones cuando se crea este objeto, como en
LibroCalificaciones miLibroCalificaciones =
new LibroCalificaciones( "CS101 Introduccion a la programacion en Java" );
En este caso, el argumento "CS101 Introduccion a la programacion en Java" se pasa al constructor del
objeto LibroCalificaciones y se utiliza para inicializar el nombreDelCurso. La instrucción anterior requiere que
la clase proporcione un constructor con un parámetro String. La figura 3.10 contiene una clase LibroCalifi-
caciones modificada con dicho constructor.
3.7 Inicialización de objetos mediante constructores 89
90 Capítulo 3 Introducción a las clases y los objetos
Las líneas 9 a la 12 declaran el constructor para la clase LibroCalificaciones. Un constructor debe tener el
mismo nombre que su clase. Al igual que un método, un constructor especifica en su lista de parámetros los da-
tos que requiere para realizar su tarea. Cuando usted crea un nuevo objeto (como haremos en la figura 3.11), estos
datos se colocan en los paréntesis que van después del nombre de la clase. La línea 9 indica que el constructor de
la clase LibroCalificaciones tiene un parámetro String llamado nombre. El nombre que se pasa al constructor
se asigna a la variable de instancia nombreCurso en la línea 11 del cuerpo del constructor.
La figura 3.11 demuestra la inicialización de objetos LibroCalificaciones mediante el uso de este cons-
tructor. Las líneas 11 y 12 crean e inicializan el objeto libroCalificaciones1 de LibroCalificaciones. El
constructor de la clase LibroCalificaciones se llama con el argumento "CS101 Introduccion a la pro-
gramacion en Java" para inicializar el nombre del curso. La expresión de creación de la instancia de la clase a
la derecha del signo = en las líneas 11 y 12 devuelve una referencia al nuevo objeto, el cual se asigna a la variable
libroCalificaciones1. Las líneas 13 y 14 repiten este proceso para otro objeto LibroCalificaciones llama-
do libroCalificaciones2, pero esta vez se le pasa el argumento "CS102 Estructuras de datos en Java" para
inicializar el nombre del curso para libroCalificaciones2. Las líneas 17 a la 20 utilizan el método obtenerNom-
breDelCurso de cada objeto para obtener los nombres de los cursos y mostrar que, sin duda, se inicializaron en
el momento en el que se crearon los objetos. En la introducción a la sección 3.5, aprendió que cada instancia (es
decir, objeto) de una clase contiene su propia copia de las variables de instancia de la clase. La salida confirma que
cada objeto LibroCalificaciones mantiene su propia copia de la variable de instancia nombreCurso.
Figura 3.10 | La clase LibroCalificaciones con un constructor para inicializar el nombre del curso.
1 // Fig. 3.10: LibroCalificaciones.java
2 // La clase LibroCalificaciones con un constructor para inicializar el nombre del curso.
3
4 public class LibroCalificaciones
5 {
6 private String nombreDelCurso; // nombre del curso para este LibroCalificaciones
7
8 // el constructor inicializa nombreDelCurso con el objeto String que se provee como
argumento
9 public LibroCalificaciones( String nombre )
10 {
11 nombreDelCurso = nombre; // inicializa nombreDelCurso
12 } // fin del constructor
13
14 // método para establecer el nombre del curso
15 public void establecerNombreDelCurso( String nombre )
16 {
17 nombreDelCurso = nombre; // almacena el nombre del curso
18 } // fin del método establecerNombreDelCurso
19
20 // método para obtener el nombre del curso
21 public String obtenerNombreDelCurso()
22 {
23 return nombreDelCurso;
24 } // fin del método obtenerNombreDelCurso
25
26 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones
27 public void mostrarMensaje()
28 {
29 // esta instrucción llama a obtenerNombreDelCurso para obtener el
30 // nombre del curso que este LibroCalificaciones representa
31 System.out.printf( “Bienvenido al Libro de calificaciones paran%s!n”,
32 obtenerNombreDelCurso() );
33 } // fin del método mostrarMensaje
34
35 } // fin de la clase LibroCalificaciones
Al igual que los métodos, los constructores también pueden recibir argumentos. No obstante, una importan-
te diferencia entre los constructores y los métodos es que los constructores no pueden devolver valores, por lo cual
no pueden especificar un tipo de valor de retorno (ni siquiera void). Por lo general, los constructores se declaran
como public. Si una clase no incluye un constructor, las variables de instancia de esa clase se inicializan con sus
valores predeterminados. Si un programador declara uno o más constructores para una clase, el compilador de
Java no creará un constructor predeterminado para esa clase.
Tip para prevenir errores 3.2
A menos que sea aceptable la inicialización predeterminada de las variables de instancia de su clase, deberá propor-
cionar un constructor para asegurarse que las variables de instancia de su clase se inicialicen en forma apropiada con
valores significativos, a la hora de crear cada nuevo objeto de su clase.
Agregar el constructor al diagrama de clases de UML de la clase LibroCalificaciones
El diagrama de clases de UML de la figura 3.12 modela la clase LibroCalificaciones de la figura 3.10, la cual
tiene un constructor con un parámetro llamado nombre, de tipo String. Al igual que las operaciones, en un
diagrama de clases, UML modela a los constructores en el tercer compartimiento de una clase. Para diferenciar a
un constructor de las operaciones de una clase, UML requiere que se coloque la palabra “constructor” entre los
signos « y » antes del nombre del constructor. Es costumbre listar los constructores antes de otras operaciones en
el tercer compartimiento.
3.8 Números de punto flotante y el tipo double
En nuestra siguiente aplicación, dejaremos por un momento nuestro ejemplo práctico con la clase LibroCalifi-
caciones para declarar una clase llamada Cuenta, la cual mantiene el saldo de una cuenta bancaria. La mayoría
de los saldos de las cuentas no son números enteros (por ejemplo, 0, –22 y 1024). Por esta razón, la clase Cuenta
Figura 3.11 | El constructor de LibroCalificaciones se utiliza para especificar el nombre del curso cada vez que se
crea un objeto LibroCalificaciones.
1 // Fig. 3.11: PruebaLibroCalificaciones.java
2 // El constructor de LibroCalificaciones se utiliza para especificar el
3 // nombre del curso cada vez que se crea cada objeto LibroCalificaciones.
4
5 public class PruebaLibroCalificaciones
6 {
7 // el método main empieza la ejecución del programa
8 public static void main( String args[] )
9 {
10 // crea objeto LibroCalificaciones
11 LibroCalificaciones libroCalificaciones1 = new LibroCalificaciones(
12 “CS101 Introduccion a la programacion en Java” );
13 LibroCalificaciones libroCalificaciones2 = new LibroCalificaciones(
14 “CS102 Estructuras de datos en Java” );
15
16 // muestra el valor inicial de nombreDelCurso para cada LibroCalificaciones
17 System.out.printf( “El nombre del curso de libroCalificaciones1 es: %sn”,
18 libroCalificaciones1.obtenerNombreDelCurso() );
19 System.out.printf( “El nombre del curso de libroCalificaciones2 es: %sn”,
20 libroCalificaciones2.obtenerNombreDelCurso() );
21 } // fin de main
22
23 } // fin de la clase PruebaLibroCalificaciones
El nombre del curso de libroCalificaciones1 es: CS101 Introduccion a la programacion en Java
El nombre del curso de libroCalificaciones2 es: CS102 Estructuras de datos en Java
3.8 Números de punto flotante y el tipo double 91
92 Capítulo 3 Introducción a las clases y los objetos
representa el saldo de las cuentas como un número de punto flotante (es decir, un número con un punto deci-
mal, como 7.33, 0.0975 o 1000.12345). Java cuenta con dos tipos primitivos para almacenar números de punto
flotante en la memoria: float y double. La principal diferencia entre ellos es que las variables tipo double pueden
almacenar números con mayor magnitud y detalle (es decir, más dígitos a la derecha del punto decimal; lo que
también se conoce como precisión del número) que las variables float.
Precisión de los números de punto flotante y requerimientos de memoria
Las variables de tipo float representan números de punto flotante de precisión simple y tienen siete dígitos
significativos. Las variables de tipo double representan números de punto flotante de precisión doble. Éstos
requieren el doble de memoria que las variables float y proporcionan 15 dígitos significativos; aproximadamente
el doble de precisión de las variables float. Para el rango de valores requeridos por la mayoría de los programas,
debe bastar con las variables de tipo float, pero podemos utilizar variables tipo double para “ir a la segura”. En
algunas aplicaciones, incluso hasta las variables de tipo double serán inadecuadas; dichas aplicaciones se encuen-
tran más allá del alcance de este libro. La mayoría de los programadores representan los números de punto flotante
con el tipo double. De hecho, Java trata a todos los números de punto flotante que escribimos en el código fuente
de un programa (como 7.33 y 0.0975) como valores double de manera predeterminada. Dichos valores en el
código fuente se conocen como literales de punto flotante. En el apéndice D, Tipos primitivos, podrá consultar
los rangos de los valores para los tipos float y double.
Aunque los números de punto flotante no son siempre 100% precisos, tienen numerosas aplicaciones. Por
ejemplo, cuando hablamos de una temperatura corporal “normal” de 36.8, no necesitamos una precisión con un
número extenso de dígitos. Cuando leemos la temperatura en un termómetro como 36.8, en realidad podría ser
36.7999473210643. Si consideramos a este número simplemente como 36.8, está bien para la mayoría de las apli-
caciones en las que se trabaja con las temperaturas corporales. Debido a la naturaleza imprecisa de los números de
punto flotante, se prefiere el tipo double al tipo float ya que las variables double pueden representar números
de punto flotante con más precisión. Por esta razón, utilizaremos el tipo double a lo largo de este libro.
Los números de punto flotante también surgen como resultado de la división. En la aritmética convencional,
cuando dividimos 10 entre 3 el resultado es 3.3333333…, y la secuencia de números 3 se repite en forma inde-
finida. La computadora asigna sólo una cantidad fija de espacio para almacenar un valor de este tipo, por lo que,
sin duda, el valor de punto flotante almacenado sólo puede ser una aproximación.
Error común de programación 3.4
El uso de números de punto flotante en una forma en la que se asuma que se representan con precisión puede producir
errores lógicos.
La clase Cuenta con una variable de instancia de tipo double
Nuestra siguiente aplicación (figuras 3.13 y 3.14) contiene una clase llamada Cuenta (figura 3.13), la cual man-
tiene el saldo de una cuenta bancaria. Un banco ordinario da servicio a muchas cuentas, cada una con su propio
saldo, por lo que la línea 7 declara una variable de instancia, de tipo double, llamada saldo . La variable saldo
es una variable de instancia, ya que está declarada en el cuerpo de la clase pero fuera de las declaraciones de los
métodos de la misma (líneas 10 a la 16, 19 a la 22 y 25 a la 28). Cada instancia (es decir, objeto) de la clase Cuenta
contiene su propia copia de saldo.
Figura 3.12 | Diagrama de clases de UML, en el cual se indica que la clase LibroCalificaciones tiene un
constructor con un parámetro nombre del tipo String de UML.
LibroCalificaciones
– nombreDelCurso : String
«constructor» LibroCalificaciones( nombre : String )
+ establecerNombreDelCurso( nombre : String )
+ obtenerNombreDelCurso( ) : String
+ mostrarMensaje( )
La clase Cuenta contiene un constructor y dos métodos. Debido a que es común que alguien abra una cuenta
para depositar dinero de inmediato, el constructor (líneas 10 a la 16) recibe un parámetro llamado saldoInicial
de tipo double, el cual representa el saldo inicial de la cuenta. Las líneas 14 y 15 aseguran que saldoInicial sea
mayor que 0.0. De ser así, el valor de saldoInicial se asigna a la variable de instancia saldo. En caso contrario,
saldo permanece en 0.0, su valor inicial predeterminado.
El método abonar (líneas 19 a la 22) no devuelve datos cuando completa su tarea, por lo que su tipo de valor
de retorno es void. El método recibe un parámetro llamado monto: un valor double que se sumará al saldo. La
línea 21 suma monto al valor actual de saldo, y después asigna el resultado a saldo (con lo cual se sustituye el
monto del saldo anterior).
El método obtenerSaldo (líneas 25 a la 28) permite a los clientes de la clase (es decir, otras clases que utili-
cen esta clase) obtener el valor del saldo de un objeto Cuenta específico. El método especifica el tipo de valor de
retorno double y una lista de parámetros vacía.
Observe una vez más que las instrucciones en las líneas 15, 21 y 27 utilizan la variable de instancia saldo,
aun y cuando no se declaró en ninguno de los métodos. Podemos usar saldo en estos métodos, ya que es una
variable de instancia de la clase.
La clase PruebaCuenta que utiliza a la clase Cuenta
La clase PruebaCuenta (figura 3.14) crea dos objetos Cuenta (líneas 10 y 11) y los inicializa con 50.00 y -7.53,
respectivamente. Las líneas 14 a la 17 imprimen el saldo en cada objeto Cuenta mediante una llamada al método
obtenerSaldo de Cuenta. Cuando se hace una llamada al método obtenerSaldo para cuenta1 en la línea 15,
se devuelve el valor del saldo de cuenta1 de la línea 27 en la figura 3.13, y se imprime en pantalla mediante la
instrucción System.out.printf (figura 3.14, líneas 14 y 15). De manera similar, cuando se hace la llamada al
método obtenerSaldo para cuenta2 en la línea 17, se devuelve el valor del saldo de cuenta2 de la línea 27 en la
Figura 3.13 | La clase Cuenta con una variable de instancia de tipo double.
1 // Fig. 3.13: Cuenta.java
2 // La clase Cuenta con un constructor para
3 // inicializar la variable de instancia saldo.
4
5 public class Cuenta
6 {
7 private double saldo; // variable de instancia que almacena el saldo
8
9 // constructor
10 public Cuenta( double saldoInicial )
11 {
12 // valida que saldoInicial sea mayor que 0.0;
13 // si no lo es, saldo se inicializa con el valor predeterminado 0.0
14 if ( saldoInicial > 0.0 )
15 saldo = saldoInicial;
16 } // fin del constructor de Cuenta
17
18 // abona (suma) un monto a la cuenta
19 public void abonar( double monto )
20 {
21 saldo = saldo + monto; // suma el monto al saldo
22 } // fin del método abonar
23
24 // devuelve el saldo de la cuenta
25 public double obtenerSaldo()
26 {
27 return saldo; // proporciona el valor de saldo al método que hizo la llamada
28 } // fin del método obtenerSaldo
29
30 } // fin de la clase Cuenta
3.8 Números de punto flotante y el tipo double 93
94 Capítulo 3 Introducción a las clases y los objetos
figura 3.13, y se imprime en pantalla mediante la instrucción System.out.printf (figura 3.14, líneas 16 y 17).
Observe que el saldo de cuenta2 es 0.00, ya que el constructor se aseguró de que la cuenta no pudiera empezar
con un saldo negativo. El valor se imprime en pantalla mediante printf, con el especificador de formato %.2f.
El especificador de formato %f se utiliza para imprimir valores de tipo float o double. El .2 entre % y f repre-
senta el número de lugares decimales (2) que deben imprimirse a la derecha del punto decimal en el número de
punto flotante; a esto también se le conoce como la precisión del número. Cualquier valor de punto flotante que
se imprima con %.2f se redondeará a la posición de las centenas; por ejemplo, 123.457 se redondearía a 123.46,
y 27.333 se redondearía a 27.33.
Figura 3.14 | Entrada y salida de números de punto flotante con objetos Cuenta. (Parte 1 de 2).
1 // Fig. 3.14: PruebaCuenta.java
2 // Entrada y salida de números de punto flotante con objetos Cuenta.
3 import java.util.Scanner;
4
5 public class PruebaCuenta
6 {
7 // el método main empieza la ejecución de la aplicación de Java
8 public static void main( String args[] )
9 {
10 Cuenta cuenta1 = new Cuenta( 50.00 ); // crea objeto Cuenta
11 Cuenta cuenta2 = new Cuenta( -7.53 ); // crea objeto Cuenta
12
13 // muestra el saldo inicial de cada objeto
14 System.out.printf( “Saldo de cuenta1: $%.2fn”,
15 cuenta1.obtenerSaldo() );
16 System.out.printf( “Saldo de cuenta2: $%.2fnn”,
17 cuenta2.obtenerSaldo() );
18
19 // crea objeto Scanner para obtener la entrada de la ventana de comandos
20 Scanner entrada = new Scanner( System.in );
21 double montoDeposito; // deposita el monto escrito por el usuario
22
23 System.out.print( “Escriba el monto a depositar para cuenta1: “ ); // indicador
24 montoDeposito = entrada.nextDouble(); // obtiene entrada del usuario
25 System.out.printf( “nsumando %.2f al saldo de cuenta1nn”,
26 montoDeposito );
27 cuenta1.abonar( montoDeposito ); // suma al saldo de cuenta1
28
29 // muestra los saldos
30 System.out.printf( “Saldo de cuenta1: $%.2fn”,
31 cuenta1.obtenerSaldo() );
32 System.out.printf( “Saldo de cuenta2: $%.2fnn”,
33 cuenta2.obtenerSaldo() );
34
35 System.out.print( “Escriba el monto a depositar para cuenta2: “ ); // indicador
36 montoDeposito = entrada.nextDouble(); // obtiene entrada del usuario
37 System.out.printf( “nsumando %.2f al saldo de cuenta2nn”,
38 montoDeposito );
39 cuenta2.abonar( montoDeposito ); // suma al saldo de cuenta2
40
41 // muestra los saldos
42 System.out.printf( “Saldo de cuenta1: $%.2fn”,
43 cuenta1.obtenerSaldo() );
44 System.out.printf( “Saldo de cuenta2: $%.2fn”,
45 cuenta2.obtenerSaldo() );
46 } // fin de main
47
48 } // fin de la clase PruebaCuenta
La línea 20 crea un objeto Scanner, el cual se utilizará para obtener montos de depósito de un usuario. La
línea 21 declara la variable local montoDeposito para almacenar cada monto de depósito introducido por el usua-
rio. A diferencia de la variable de instancia saldo en la clase Cuenta, la variable local montoDeposito en main no
se inicializa con 0.0 de manera predeterminada. Sin embargo, esta variable no necesita inicializarse aquí, ya que
su valor se determinará con base a la entrada del usuario.
La línea 23 pide al usuario que escriba un monto a depositar para cuenta1. La línea 24 obtiene la entrada
del usuario, llamando al método nextDouble del objeto Scanner llamado entrada, el cual devuelve un valor
double introducido por el usuario. Las líneas 25 y 26 muestran el monto del depósito. La línea 27 llama al méto-
do abonar del objeto cuenta1 y le suministra montoDeposito como argumento. Cuando se hace la llamada al
método, el valor del argumento se asigna al parámetro monto (línea 19 de la figura 3.13) del método abonar
(líneas 19 a la 22 de la figura 3.13), y después el método abonar suma ese valor al saldo (línea 21 de la figura
3.13). Las líneas 30 a la 33 (figura 3.14) imprimen en pantalla los saldos de ambos objetos Cuenta otra vez,
para mostrar que sólo se modificó el saldo de cuenta1.
La línea 35 pide al usuario que escriba un monto a depositar para cuenta2. La línea 36 obtiene la entrada
del usuario, llamando al método nextDouble del objeto Scanner llamado entrada. Las líneas 37 y 38 muestran
el monto del depósito. La línea 39 llama al método abonar del objeto cuenta2 y le suministra montoDeposito
como argumento; después, el método abonar suma ese valor al saldo. Por último, las líneas 42 a la 45 imprimen
en pantalla los saldos de ambos objetos Cuenta otra vez, para mostrar que sólo se modificó el saldo de cuenta2.
Diagrama de clases de UML para la clase Cuenta
El diagrama de clases de UML en la figura 3.15 modela la clase Cuenta de la figura 3.13. El diagrama modela la
propiedad private llamada saldo con el tipo Double de UML, para que corresponda a la variable de instancia
saldo de la clase, que tiene el tipo double de Java. El diagrama modela el constructor de la clase Cuenta con un
parámetro saldoInicial del tipo Double de UML en el tercer compartimiento de la clase. Los dos métodos
public de la clase se modelan como operaciones en el tercer compartimiento también. El diagrama modela la
operación abonar con un parámetro monto de tipo Double de UML (ya que el método correspondiente tiene un
parámetro monto de tipo double en Java) y la operación obtenerSaldo con un tipo de valor de retorno Double
(ya que el método correspondiente en Java devuelve un valor double).
3.9 (Opcional) Ejemplo práctico de GUI y gráficos: uso de cuadros
de diálogo
Este ejemplo práctico opcional está diseñado para aquellos quienes desean empezar a conocer las poderosas herra-
mientas de Java para crear interfaces gráficas de usuario (GUIs) y gráficos antes de las principales discusiones de
estos temas en el capítulo 11, Componentes de la GUI: parte 1, el capítulo 12, Gráficos y Java 2D™, y el capítulo
22, Componentes de la GUI: parte 2.
Figura 3.14 | Entrada y salida de números de punto flotante con objetos Cuenta. (Parte 2 de 2).
Saldo de cuenta1: $50.00
Saldo de cuenta2: $0.00
Escriba el monto a depositar para cuenta1: 25.53
sumando 25.53 al saldo de cuenta1
Saldo de cuenta1: $75.53
Saldo de cuenta2: $0.00
Escriba el monto a depositar para cuenta2: 123.45
sumando 123.45 al saldo de cuenta2
Saldo de cuenta1: $75.53
Saldo de cuenta2: $123.45
3.9 (Opcional) Ejemplo práctico de GUI y gráficos: uso de cuadros de diálogo 95
96 Capítulo 3 Introducción a las clases y los objetos
El ejemplo práctico de GUI y gráficos aparece en 10 secciones breves (figura 3.16). Cada sección introduce
unos cuantos conceptos básicos y proporciona ejemplos visuales y gráficos. En las primeras secciones, creará sus
primeras aplicaciones gráficas; en las secciones posteriores, utilizará los conceptos de programación orientada a
objetos que se presentan a lo largo del capítulo 10 para crear una aplicación de dibujo, la cual dibuja una varie-
dad de figuras. Cuando presentemos formalmente a las GUIs en el capítulo 11, utilizaremos el ratón para elegir
exactamente qué figuras dibujar y en dónde dibujarlas. En el capítulo 12, agregaremos las herramientas de la API
de gráficos en 2D de Java para dibujar las figuras con distintos grosores de línea y rellenos. Esperamos que este
ejemplo práctico le sea informativo y divertido.
Ubicación Título – Ejercicio(s)
Sección 3.9
Sección 4.14
Sección 5.10
Sección 6.13
Sección 7.13
Sección 8.18
Sección 9.8
Sección 10.8
Ejercicio 11.18
Ejercicio 12.31
Uso de cuadros de diálogo: entrada y salida básica con cuadros de diálogo.
Creación de dibujos simples: mostrar y dibujar líneas en la pantalla.
Dibujo de rectángulos y óvalos: uso de figuras para representar datos.
Colores y figuras rellenas: dibujar un tiro al blanco y gráficos aleatorios.
Dibujo de arcos: dibujar espirales con arcos.
Uso de objetos con gráficos: almacenar figuras como objetos.
Mostrar texto e imágenes mediante el uso de etiquetas: proporcionar información de estado.
Dibujo con polimorfismo: identificar las similitudes entre figuras.
Expansión de la interfaz: uso de componentes de la GUI y manejo de eventos.
Agregar Java 2D: uso de la API 2D de Java para mejorar los dibujos.
Figura 3.16 | Glosario de GUI ejemplo práctico en cada capítulo.
Cómo mostrar texto en un cuadro de diálogo
Los programas que hemos presentado hasta ahora muestran su salida en la ventana de comandos. Muchas aplica-
ciones utilizan ventanas, o cuadros de diálogo (también llamados diálogos) para mostrar la salida. Por ejemplo,
los navegadores Web como Firefox o Microsoft Internet Explorer muestran las páginas Web en sus propias venta-
nas. Los programas de correo electrónico le permiten escribir y leer mensajes en una ventana. Por lo general, los
cuadros de diálogo son ventanas en las que los programas muestran mensajes importantes a los usuarios. La clase
JOptionPane cuenta con cuadros de diálogo previamente empaquetados, los cuales permiten a los programas
mostrar ventanas que contengan mensajes; a dichas ventanas se les conoce como diálogos de mensaje. La figura
3.17 muestra el objeto String “BienvenidonanJava” en un diálogo de mensaje.
La línea 3 indica que el programa utiliza la clase JOptionPane del paquete javax.swing. Este paquete
contiene muchas clases que le ayudan a crear interfaces gráficas de usuario (GUIs) para las aplicaciones. Los
componentes de la GUI facilitan la entrada de datos al usuario del programa, y la presentación de los datos de
salida. La línea 10 llama al método showMessageDialog de JOptionPane para mostrar un cuadro de diálogo
que contiene un mensaje. El método requiere dos argumentos. El primero ayuda a Java a determinar en dónde
Cuenta
– balance : Double
«constructor» Cuenta( saldoInicial : Double )
+ abonar( monto : Double )
+ obtenerSaldo( ) : Double
Figura 3.15 | Diagrama de clases de UML, el cual indica que la clase Cuenta tiene un atributo private
llamado saldo, con el tipo Double de UML, un constructor (con un parámetro de tipo Double de UML) y dos
operaciones public: abonar (con un parámetro monto de tipo Double de UML) y obtenerSaldo (devuelve el
tipo Double de UML).
colocar el cuadro de diálogo. Cuando el primer argumento es null, el cuadro de diálogo aparece en el centro de
la pantalla de la computadora. El segundo argumento es el objeto String a mostrar en el cuadro de diálogo.
El método showMessageDialog es un método static de la clase JOptionPane. A menudo, los métodos
static definen las tareas utilizadas con frecuencia, y no se requiere crear explícitamente un objeto. Por ejemplo,
muchos programas muestran cuadros de diálogo. En vez de que usted tenga que crear código para realizar esta
tarea, los diseñadores de la clase JOptionPane de Java declaran un método static que realiza esta tarea por usted.
Por lo general, la llamada a un método static se realiza mediante el uso del nombre de su clase, seguido de un
punto (.) y del nombre del método, como en
NombreClase.nombreMétodo( argumentos )
El capítulo 6, Métodos: un análisis más detallado, habla sobre los métodos static con detalle.
Introducir texto en un cuadro de diálogo
La aplicación de la figura 3.18 utiliza otro cuadro de diálogo JOptionPane predefinido, conocido como diálogo
de entrada, el cual permite al usuario introducir datos en el programa. El programa pide el nombre del usuario,
y responde con un diálogo de mensaje que contiene un saludo y el nombre introducido por el usuario.
Figura 3.17 | Uso de JOptionPane para mostrar varias líneas en un cuadro de diálogo.
1 // Fig. 3.17: Dialogo1.java
2 // Imprimir varias líneas en un cuadro de diálogo.
3 import javax.swing.JOptionPane; // importa la clase JOptionPane
4
5 public class Dialogo1
6 {
7 public static void main( String args[] )
8 {
9 // muestra un cuadro de diálogo con un mensaje
10 JOptionPane.showMessageDialog( null, “BienvenidonanJava” );
11 } // fin de main
12 } // fin de la clase Dialogo1
1 // Fig. 3.18: DialogoNombre.java
2 // Entrada básica con un cuadro de diálogo.
3 import javax.swing.JOptionPane;
4
5 public class DialogoNombre
6 {
7 public static void main( String args[] )
8 {
9 // pide al usuario que escriba su nombre
10 String nombre =
11 JOptionPane.showInputDialog( “Cual es su nombre?” );
12
13 // crea el mensaje
14 String mensaje =
Figura 3.18 | Cómo obtener la entrada del usuario mediante un cuadro de diálogo. (Parte 1 de 2).
3.9 Ejemplo práctico de GUI y gráficos: uso de cuadros de diálogo 97
98 Capítulo 3 Introducción a las clases y los objetos
15 String.format( “Bienvenido, %s, a la programacion en Java!”, nombre );
16
17 // muestra el mensaje para dar la bienvenida al usuario por su nombre
18 JOptionPane.showMessageDialog( null, mensaje );
19 } // fin de main
20 } // fin de la clase DialogoNombre
Las líneas 10 y 11 utilizan el método showInputDialog de JOptionPane para mostrar un diálogo de entrada
que contiene un indicador y un campo (conocido como campo de texto), en el cual el usuario puede escribir
texto. El argumento de showInputDialog es el indicador que muestra lo que el usuario debe escribir. El usuario
escribe caracteres en el campo de texto, y después hace clic en el botón Aceptar u oprime la tecla Intro para
devolver el objeto String al programa. El método showInputDialog (línea 11) devuelve un objeto String que
contiene los caracteres escritos por el usuario. Almacenamos el objeto String en la variable nombre (línea 10).
[Nota: si oprime el botón Cancelar en el cuadro de diálogo, el método devuelve null y el programa muestra la
palabra clave “null” como el nombre].
Las líneas 14 y 15 utilizan el método static String llamado format para devolver un objeto String que
contiene un saludo con el nombre del usuario. El método format es similar al método System.out.printf,
excepto que format devuelve el objeto String con formato, en vez de mostrarlo en una ventana de comandos.
La línea 18 muestra el saludo en un cuadro de diálogo de mensaje.
Ejercicio del ejemplo práctico de GUI y gráficos
3.1 Modifique el programa de suma en la figura 2.7 para usar la entrada y salida basadas en cuadro de diálogo con los
métodos de la clase JOptionPane. Como el método showInputDialog devuelve un objeto String, debe convertir el objeto
String que introduce el usuario a un int para usarlo en los cálculos. El método
Integer.parseInt( String s )
toma un argumento String que representa a un entero (por ejemplo, el resultado de JOptionPane.showInputDialog) y
devuelve el valor como un int. El método parseInt es un método static de la clase Integer (del paquete java.lang).
Observe que si el objeto String no contiene un entero válido, el programa terminará con un error.
3.10 (Opcional) Ejemplo práctico de Ingeniería de Software:
identificación de las clases en un documento de requerimientos
Ahora empezaremos a diseñar el sistema ATM que presentamos en el capítulo 2. En esta sección identificaremos
las clases necesarias para crear el sistema ATM, analizando los sustantivos y las frases nominales que aparecen en
el documento de requerimientos. Presentaremos los diagramas de clases de UML para modelar las relaciones entre
estas clases. Este primer paso es importante para definir la estructura de nuestro sistema.
Identificación de las clases en un sistema
Para comenzar nuestro proceso de DOO, identificaremos las clases requeridas para crear el sistema ATM. Más
adelante describiremos estas clases mediante el uso de los diagramas de clases de UML y las implementaremos en
Java. Primero debemos revisar el documento de requerimientos de la sección 2.9, para identificar los sustantivos y
frases nominales clave que nos ayuden a identificar las clases que conformarán el sistema ATM. Tal vez decidamos
que algunos de estos sustantivos y frases nominales sean atributos de otras clases en el sistema. Tal vez también
concluyamos que algunos de los sustantivos no corresponden a ciertas partes del sistema y, por ende, no deben
modelarse. A medida que avancemos por el proceso de diseño podemos ir descubriendo clases adicionales.
Figura 3.18 | Cómo obtener la entrada del usuario mediante un cuadro de diálogo. (Parte 2 de 2).
La figura 3.19 lista los sustantivos y frases nominales que se encontraron en el documento de requerimientos
de la sección 2.9. Los listaremos de izquierda a derecha, en el orden en el que los encontramos por primera vez en
el documento de requerimientos. Sólo listaremos la forma singular de cada sustantivo o frase nominal.
Sustantivos y frases nominales en el documento de requerimientos del ATM
banco
ATM
usuario
cliente
transacción
cuenta
saldo
dinero / fondos
pantalla
teclado numérico
dispensador de efectivo
billete de $20 / efectivo
ranura de depósito
sobre de depósito
número de cuenta
NIP
base de datos del banco
solicitud de saldo
retiro
depósito
Figura 3.19 | Sustantivos y frases nominales en el documento de requerimientos del ATM.
Crearemos clases sólo para los sustantivos y frases nominales que tengan importancia en el sistema ATM.
No modelamos “banco” como una clase, ya que el banco no es una parte del sistema ATM; el banco sólo quiere
que nosotros construyamos el ATM. “Cliente” y “usuario” también representan entidades fuera del sistema; son
importantes debido a que interactúan con nuestro sistema ATM, pero no necesitamos modelarlos como clases
en el software del ATM. Recuerde que modelamos un usuario del ATM (es decir, un cliente del banco) como el
actor en el diagrama de casos de uso de la figura 2.20.
No necesitamos modelar “billete de $20” ni “sobre de depósito” como clases. Éstos son objetos físicos en el
mundo real, pero no forman parte de lo que se va a automatizar. Podemos representar en forma adecuada la pre-
sencia de billetes en el sistema, mediante el uso de un atributo de la clase que modela el dispensador de efectivo
(en la sección 4.15 asignaremos atributos a las clases del sistema ATM). Por ejemplo, el dispensador de efec-
tivo mantiene un conteo del número de billetes que contiene. El documento de requerimientos no dice nada
acerca de lo que debe hacer el sistema con los sobres de depósito después de recibirlos. Podemos suponer que
con sólo admitir la recepción de un sobre (una operación que realiza la clase que modela la ranura de depósito)
es suficiente para representar la presencia de un sobre en el sistema (en la sección 6.14 asignaremos operaciones
a las clases del sistema ATM).
En nuestro sistema ATM simplificado, lo más apropiado sería representar varios montos de “dinero”, inclu-
yendo el “saldo” de una cuenta, como atributos de clases. De igual forma, los sustantivos “número de cuenta”
y “NIP” representan piezas importantes de información en el sistema ATM. Son atributos importantes de una
cuenta bancaria. Sin embargo, no exhiben comportamientos. Por ende, podemos modelarlos de la manera más
apropiada como atributos de una clase de cuenta.
Aunque, con frecuencia, el documento de requerimientos describe una “transacción” en un sentido general,
no modelaremos la amplia noción de una transacción financiera en este momento. En vez de ello, modelaremos
los tres tipos de transacciones (es decir, “solicitud de saldo”, “retiro” y “depósito”) como clases individuales. Estas
clases poseen los atributos específicos necesarios para ejecutar las transacciones que representan. Por ejemplo, para
un retiro se necesita conocer el monto de dinero que el usuario desea retirar. Sin embargo, una solicitud de saldo
no requiere datos adicionales. Lo que es más, las tres clases de transacciones exhiben comportamientos únicos.
Para un retiro se requiere entregar efectivo al usuario, mientras que para un depósito se requiere recibir un sobre
de depósito del usuario. [Nota: en la sección 10.9, “factorizaremos” las características comunes de todas las tran-
sacciones en una clase de “transacción” general, mediante el uso del concepto orientado a objetos de herencia].
Determinaremos las clases para nuestro sistema con base en los sustantivos y frases nominales restantes de la
figura 3.19. Cada una de ellas se refiere a uno o varios de los siguientes elementos:
ATM
pantalla
teclado numérico
dispensador de efectivo
ranura de depósito
•
•
•
•
•
cuenta
base de datos del banco
solicitud de saldo
retiro
depósito
•
•
•
•
•
3.10 (Opción) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un... 99
100 Capítulo 3 Introducción a las clases y los objetos
Es probable que los elementos de esta lista sean clases que necesitaremos implementar en nuestro sistema.
Ahora podemos modelar las clases en nuestro sistema, con base en la lista que hemos creado. En el proceso
de diseño escribimos los nombres de las clases con la primera letra en mayúscula (una convención de UML),
como lo haremos cuando escribamos el código de Java para implementar nuestro diseño. Si el nombre de una
clase contiene más de una palabra, juntaremos todas las palabras y escribiremos la primera letra de cada una de
ellas en mayúscula (por ejemplo, NombreConVariasPalabras). Utilizando esta convención, crearemos las clases
ATM, Pantalla, Teclado, DispensadorEfectivo, RanuraDeposito, Cuenta, BaseDatosBanco, Soli-
citudSaldo, Retiro y Deposito. Construiremos nuestro sistema mediante el uso de todas estas clases como
bloques de construcción. Sin embargo, antes de empezar a construir el sistema, debemos comprender mejor la
forma en que las clases se relacionan entre sí.
Modelado de las clases
UML nos permite modelar, a través de los diagramas de clases, las clases en el sistema ATM y sus interrelaciones.
La figura 3.20 representa a la clase ATM. En UML, cada clase se modela como un rectángulo con tres comparti-
mientos. El compartimiento superior contiene el nombre de la clase, centrado horizontalmente y en negrita. El
compartimiento intermedio contiene los atributos de la clase (en las secciones 4.15 y 5.11 hablaremos sobre los
atributos). El compartimiento inferior contiene las operaciones de la clase (que veremos en la sección 6.14). En la
figura 3.20, los compartimientos intermedio e inferior están vacíos, ya que no hemos determinado los atributos
y operaciones de esta clase todavía.
Los diagramas de clases también muestran las relaciones entre las clases del sistema. La figura 3.21 muestra
cómo nuestras clases ATM y Retiro se relacionan una con la otra. Por el momento modelaremos sólo este subcon-
junto de las clases del ATM, por cuestión de simpleza. Más adelante en esta sección, presentaremos un diagrama
de clases más completo. Observe que los rectángulos que representan a las clases en este diagrama no están sub-
divididos en compartimientos. UML permite suprimir los atributos y las operaciones de una clase de esta forma,
cuando sea apropiado, para crear diagramas más legibles. Un diagrama de este tipo se denomina diagrama con
elementos omitidos (elided diagram): su información, como el contenido de los compartimientos segundo y
tercero, no se modela. En las secciones 4.15 y 6.14 colocaremos información en estos compartimientos.
En la figura 3.21, la línea sólida que conecta a las dos clases representa una asociación: una relación entre
clases. Los números cerca de cada extremo de la línea son valores de multiplicidad; éstos indican cuántos objetos
de cada clase participan en la asociación. En este caso, al seguir la línea de un extremo al otro se revela que, en un
momento dado, un objeto ATM participa en una asociación con cero o con un objeto Retiro; cero si el usuario
actual no está realizando una transacción o si ha solicitado un tipo distinto de transacción, y uno si el usuario ha
solicitado un retiro. UML puede modelar muchos tipos de multiplicidad. La figura 3.22 lista y explica los tipos
de multiplicidad.
Una asociación puede tener nombre. Por ejemplo, la palabra Ejecuta por encima de la línea que conecta a
las clases ATM y Retiro en la figura 3.21 indica el nombre de esa asociación. Esta parte del diagrama se lee así:
“un objeto de la clase ATM ejecuta cero o un objeto de la clase Retiro”. Los nombres de las asociaciones son direc-
cionales, como lo indica la punta de flecha rellena; por lo tanto, sería inapropiado, por ejemplo, leer la anterior
asociación de derecha a izquierda como “cero o un objeto de la clase Retiro ejecuta un objeto de la clase ATM”.
Figura 3.20 | Representación de una clase en UML mediante un diagrama de clases.
ATM
Figura 3.21 | Diagrama de clases que muestra una asociación entre clases.
Ejecuta
1
transaccionActual
0..1
Retiro
ATM
La palabra transaccionActual en el extremo de Retiro de la línea de asociación en la figura 3.21 es un
nombre de rol, el cual identifica el rol que desempeña el objeto Retiro en su relación con el ATM. Un nombre
de rol agrega significado a una asociación entre clases, ya que identifica el rol que desempeña una clase dentro del
contexto de una asociación. Una clase puede desempeñar varios roles en el mismo sistema. Por ejemplo, en un
sistema de personal de una universidad, una persona puede desempeñar el rol de “profesor” con respecto a los
estudiantes. La misma persona puede desempeñar el rol de “colega” cuando participa en una asociación con otro
profesor, y de “entrenador” cuando entrena a los atletas estudiantes. En la figura 3.21, el nombre de rol transac-
cionActual indica que el objeto Retiro que participa en la asociación Ejecuta con un objeto de la clase ATM
representa a la transacción que está procesando el ATM en ese momento. En otros contextos, un objeto Retiro
puede desempeñar otros roles (por ejemplo, la transacción anterior). Observe que no especificamos un nombre
de rol para el extremo del ATM de la asociación Ejecuta. A menudo, los nombres de los roles se omiten en los
diagramas de clases, cuando el significado de una asociación está claro sin ellos.
Además de indicar relaciones simples, las asociaciones pueden especificar relaciones más complejas, como
cuando los objetos de una clase están compuestos de objetos de otras clases. Considere un cajero automático real.
¿Qué “piezas” reúne un fabricante para construir un ATM funcional? Nuestro documento de requerimientos
nos indica que el ATM está compuesto de una pantalla, un teclado, un dispensador de efectivo y una ranura de
depósito.
En la figura 3.23, los diamantes sólidos que se adjuntan a las líneas de asociación de la clase ATM indican
que esta clase tiene una relación de composición con las clases Pantalla, Teclado, DispensadorEfectivo y
RanuraDeposito. La composición implica una relación todo/parte. La clase que tiene el símbolo de composición
(el diamante sólido) en su extremo de la línea de asociación es el todo (en este caso, ATM), y las clases en el otro
extremo de las líneas de asociación son las partes; en este caso, las clases Pantalla, Teclado, Dispensador-
Efectivo y RanuraDeposito. Las composiciones en la figura 3.23 indican que un objeto de la clase ATM está
formado por un objeto de la clase Pantalla, un objeto de la clase DispensadorEfectivo, un objeto de la clase
Teclado y un objeto de la clase RanuraDeposito. El ATM “tiene una” pantalla, un teclado, un dispensador de
efectivo y una ranura de depósito. La relación tiene un define la composición (en la sección del Ejemplo práctico
de Ingeniería de Software del capítulo 10 veremos que la relación “es un” define la herencia).
De acuerdo con la especificación del UML (www.uml.org), las relaciones de composición tienen las siguien-
tes propiedades:
1. Sólo una clase en la relación puede representar el todo (es decir, el diamante puede colocarse sólo en un
extremo de la línea de asociación). Por ejemplo, la pantalla es parte del ATM o el ATM es parte de la
pantalla, pero la pantalla y el ATM no pueden representar ambos el “todo” dentro de la relación.
2. Las partes en la relación de composición existen sólo mientras exista el todo, y el todo es responsable de
la creación y destrucción de sus partes. Por ejemplo, el acto de construir un ATM incluye la manufactura
de sus partes. Lo que es más, si el ATM se destruye, también se destruyen su pantalla, teclado, dispen-
sador de efectivo y ranura de depósito.
3. Una parte puede pertenecer sólo a un todo a la vez, aunque esa parte puede quitarse y unirse a otro todo,
el cual entonces asumirá la responsabilidad de esa parte.
3.10 (Opción) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un... 101
Símbolo Significado
0
1
m
0..1
m, n
m..n
*
0..*
1..*
Ninguno.
Uno.
Un valor entero.
Cero o uno.
m o n.
Cuando menos m, pero no más que n.
Cualquier entero no negativo (cero o más).
Cero o más (idéntico a *).
Uno o más.
Figura 3.22 | Tipos de multiplicidad.
102 Capítulo 3 Introducción a las clases y los objetos
Los diamantes sólidos en nuestros diagramas de clases indican las relaciones de composición que cumplen
con estas tres propiedades. Si una relación “es un” no satisface uno o más de estos criterios, UML especifica que
se deben adjuntar diamantes sin relleno a los extremos de las líneas de asociación para indicar una agregación:
una forma más débil de la composición. Por ejemplo, una computadora personal y un monitor de computadora
participan en una relación de agregación: la computadora “tiene un” monitor, pero las dos partes pueden existir en
forma independiente, y el mismo monitor puede conectarse a varias computadoras a la vez, con lo cual se violan
las propiedades segunda y tercera de la composición.
La figura 3.24 muestra un diagrama de clases para el sistema ATM. Este diagrama modela la mayoría de las
clases que identificamos antes en esta sección, así como las asociaciones entre ellas que podemos inferir del docu-
mento de requerimientos. [Nota: las clases SolicitudSaldo y Deposito participan en asociaciones similares a las
de la clase Retiro, por lo que preferimos omitirlas en este diagrama por cuestión de simpleza. En el capítulo 10
expandiremos nuestro diagrama de clases para incluir todas las clases en el sistema ATM].
La figura 3.24 presenta un modelo gráfico de la estructura del sistema ATM. Este diagrama de clases incluye
a las clases BaseDatosBanco y Cuenta, junto con varias asociaciones que no presentamos en las figuras 3.21 o
3.23. El diagrama de clases muestra que la clase ATM tiene una relación de uno a uno con la clase BaseDatos-
Banco: un objeto ATM autentica a los usuarios en base a un objeto BaseDatosBanco. En la figura 3.24 también
modelamos el hecho de que la base de datos del banco contiene información sobre muchas cuentas; un objeto de
la clase BaseDatosBanco participa en una relación de composición con cero o más objetos de la clase Cuenta.
Recuerde que en la figura 3.22 se muestra que el valor de multiplicidad 0..* en el extremo de la clase Cuenta, de
la asociación entre las clases BaseDatosBanco y Cuenta, indica que cero o más objetos de la clase Cuenta par-
ticipan en la asociación. La clase BaseDatosBanco tiene una relación de uno a varios con la clase Cuenta;
BaseDatosBanco puede contener muchos objetos Cuenta. De manera similar, la clase Cuenta tiene una relación
de varios a uno con la clase BaseDatosBanco; puede haber muchos objetos Cuenta en BaseDatosBanco. [Nota:
si recuerda la figura 3.22, el valor de multiplicidad * es idéntico a 0..*. Incluimos 0..* en nuestros diagramas de
clases por cuestión de claridad].
La figura 3.24 también indica que si el usuario va a realizar un retiro, “un objeto de la clase Retiro accede
a/modifica el saldo de una cuenta a través de un objeto de la clase BaseDatosBanco”. Podríamos haber creado una
asociación directamente entre la clase Retiro y la clase Cuenta. No obstante, el documento de requerimientos
indica que el “ATM debe interactuar con la base de datos de información de las cuentas del banco” para realizar
transacciones. Una cuenta de banco contiene información delicada, por lo que los ingenieros de sistemas deben
considerar siempre la seguridad de los datos personales al diseñar un sistema. Por ello, sólo BaseDatosBanco pue-
de acceder a una cuenta y manipularla en forma directa. Todas las demás partes del sistema deben interactuar con
la base de datos para recuperar o actualizar la información de las cuentas (por ejemplo, el saldo de una cuenta).
El diagrama de clases de la figura 3.24 también modela las asociaciones entre la clase Retiro y las clases
Pantalla, DispensadorEfectivo y Teclado. Una transacción de retiro implica pedir al usuario que seleccione
el monto a retirar; también implica recibir entrada numérica. Estas acciones requieren el uso de la pantalla y del
teclado, respectivamente. Además, para entregar efectivo al usuario se requiere acceso al dispensador de efectivo.
1 1 1 1
1
1
1
1
Pantalla
ATM
Teclado
RanuraDeposito DispensadorEfectivo
Figura 3.23 | Diagrama de clases que muestra las relaciones de composición.
Aunque no se muestran en la figura 3.24, las clases SolicitudSaldo y Deposito participan en varias asocia-
ciones con las otras clases del sistema ATM. Al igual que la clase Retiro, cada una de estas clases se asocia con las
clases ATM y BaseDatosBanco. Un objeto de la clase SolicitudSaldo también se asocia con un objeto de la clase
Pantalla para mostrar al usuario el saldo de una cuenta. La clase Deposito se asocia con las clases Pantalla,
Teclado y RanuraDeposito. Al igual que los retiros, las transacciones de depósito requieren el uso de la pantalla
y el teclado para mostrar mensajes y recibir datos de entrada, respectivamente. Para recibir sobres de depósito, un
objeto de la clase Deposito accede a la ranura de depósitos.
Ya hemos identificado las clases en nuestro sistema ATM (aunque tal vez descubramos otras, a medida que
avancemos con el diseño y la implementación). En la sección 4.15 determinaremos los atributos para cada una
de estas clases, y en la sección 5.11 utilizaremos estos atributos para examinar la forma en que cambia el sistema
con el tiempo.
Figura 3.24 | Diagrama de clases para el modelo del sistema ATM.
Accede a/modifica el saldo
de una cuenta a través de
Ejecuta
1
1
1
1 1
1
1
1
1 1 1 1
1
0..*
0..1
0..1
0..1 0..1
0..1
1
Contiene
Autentica al usuario en base a
Teclado
Retiro
RanuraDeposito
ATM
DispensadorEfectivo
Pantalla
Cuenta
BaseDatosBanco
Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
3.1 Suponga que tenemos una clase llamada Auto, la cual representa a un automóvil. Piense en algunas de las distin-
tas piezas que podría reunir un fabricante para producir un automóvil completo. Cree un diagrama de clases (similar a la
figura 3.23) que modele algunas de las relaciones de composición de la clase Auto.
3.2 Suponga que tenemos una clase llamada Archivo, la cual representa un documento electrónico en una computadora
independiente, sin conexión de red, representada por la clase Computadora. ¿Qué tipo de asociación existe entre la clase
Computadora y la clase Archivo?
a) La clase Computadora tiene una relación de uno a uno con la clase Archivo.
b) La clase Computadora tiene una relación de varios a uno con la clase Archivo.
c) La clase Computadora tiene una relación de uno a varios con la clase Archivo.
d) La clase Computadora tiene una relación de varios a varios con la clase Archivo.
3.3 Indique si la siguiente aseveración es verdadera o falsa. Si es falsa, explique por qué: un diagrama de clases de UML,
en el que no se modelan los compartimientos segundo y tercero, se denomina diagrama con elementos omitidos (elided
diagram).
3.4 Modifique el diagrama de clases de la figura 3.24 para incluir la clase Deposito, en vez de la clase Retiro.
3.10 (Opción) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un... 103
104 Capítulo 3 Introducción a las clases y los objetos
Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
3.1 [Nota: las respuestas de los estudiantes pueden variar]. La figura 3.25 presenta un diagrama de clases que muestra
algunas de las relaciones de composición de una clase Auto.
3.2 c. [Nota: en una computadora con conexión de red, esta relación podría ser de varios a varios].
3.3 Verdadera.
3.4 La figura 3.26 presenta un diagrama de clases para el ATM, en el cual se incluye la clase Deposito en vez de la clase
Retiro (como en la figura 3.24). Observe que la clase Deposito no se asocia con la clase DispensadorEfectivo, sino que se
asocia con la clase RanuraDeposito.
Auto
Rueda
Parabrisas
CinturonSeguridad
Volante
1
1 5
2
1
1
4
1
Accede a/modifica el saldo
de una cuenta a través de
Ejecuta
1
1
1
1
1
1
1
1
1 1 1 1
1
0..*
0..1
0..1
0..1 0..1
0..1
1
Contiene
Autentica el usuario en base a
Teclado
Deposito
RanuraDeposito
ATM
DispensadorEfectivo
Pantalla
Cuenta
BaseDatosBanco
Figura 3.25 | Diagrama de clases que muestra algunas relaciones de composición de una clase Auto.
Figura 3.26 | Diagrama de clases para el modelo del sistema ATM, incluyendo la clase Deposito.
3.11 Conclusión
En este capítulo aprendió los conceptos básicos de las clases, los objetos, los métodos y las variables de instancia;
utilizará estos conceptos en la mayoría de las aplicaciones de Java que vaya a crear. En especial, aprendió a declarar
variables de instancia de una clase para mantener los datos de cada objeto de la clase, y cómo declarar métodos
que operen sobre esos datos. Aprendió cómo llamar a un método para decirle que realice su tarea y cómo pasar
información a los métodos en forma de argumentos. Vio la diferencia entre una variable local de un método y
una variable de instancia de una clase, y que sólo las variables de instancia se inicializan en forma automática.
También aprendió a utilizar el constructor de una clase para especificar los valores iniciales para las variables de
instancia de un objeto. A lo largo del capítulo, vio cómo puede usarse UML para crear diagramas de clases que
modelen los constructores, métodos y atributos de las clases. Por último, aprendió acerca de los números de punto
flotante: cómo almacenarlos con variables del tipo primitivo double, cómo recibirlos en forma de datos de entra-
da mediante un objeto Scanner y cómo darles formato con printf y el especificador de formato %f para fines de
visualización. En el siguiente capítulo empezaremos nuestra introducción a las instrucciones de control, las cuales
especifican el orden en el que se realizan las acciones de un programa. Utilizará estas instrucciones en sus métodos
para especificar cómo deben realizar sus tareas.
Resumen
Sección 3.2 Clases, objetos, métodos y variables de instancia
• Para realizar una tarea en un programa se requiere un método. Dentro del método se colocan los mecanismos que
hacen que éste realice sus tareas; es decir, el método oculta los detalles de implementación de las tareas que realiza.
• La unidad de programa que aloja a un método se llama clase. Una clase puede contener uno o más métodos, que
están diseñados para realizar las tareas de esa clase.
• Un método puede realizar una tarea y devolver un resultado.
• Puede utilizarse una clase para crear una instancia de la clase, a la cual se le llama objeto. Ésta es una de las razones
por las que Java se conoce como lenguaje de programación orientado a objetos.
• Cada mensaje que se envía a un objeto se conoce como llamada a un método, y ésta le indica a un método del objeto
que realice su tarea.
• Cada método puede especificar parámetros que representan la información adicional requerida por el método para
realizar su tarea correctamente. La llamada a un método suministra valores (llamados argumentos) para los paráme-
tros del método.
• Un objeto tiene atributos que se acarrean con el objeto, a medida que éste se utiliza en un programa. Estos atributos
se especifican como parte de la clase del objeto. Los atributos se especifican en las clases mediante campos.
Sección 3.3 Declaración de una clase con un método, e instanciamiento de un objeto de una clase
• Cada declaración de clase que empieza con la palabra clave public debe almacenarse en un archivo que tenga exac-
tamente el mismo nombre que la clase, y que termine con la extensión de nombre de archivo .java.
• La palabra clave public es un modificador de acceso.
• Cada declaración de clase contiene la palabra clave class, seguida inmediatamente por el nombre de la clase.
• La declaración de un método que empieza con la palabra clave public indica que el método está “disponible para el
público”; es decir, lo pueden llamar otras clases declaradas fuera de la declaración de esa clase.
• La palabra clave void indica que un método realizará una tarea, pero no devolverá información cuando la termine.
• Por convención, los nombres de los métodos empiezan con la primera letra en minúscula, y todas las palabras sub-
siguientes en el nombre empiezan con la primera letra en mayúscula.
• Los paréntesis vacíos después del nombre de un método indican que éste no requiere parámetros para realizar su
tarea.
• El cuerpo de todos los métodos está delimitado por llaves izquierda y derecha ({ y }).
• El cuerpo de un método contiene instrucciones que realizan la tarea de éste. Una vez que se ejecutan las instruccio-
nes, el método ha terminado su tarea.
• Cuando intentamos ejecutar una clase, Java busca el método main de la clase para empezar la ejecución.
• Cualquier clase que contenga public static void main( String args[] ) puede usarse para ejecutar una aplica-
ción.
• Por lo general, no podemos llamar a un método que pertenece a otra clase, sino hasta crear un objeto de esa clase.
Resumen 105
• Las expresiones de creación de instancias de clases que empiezan con la palabra clave new crean nuevos objetos.
• Para llamar a un método de un objeto, se pone después del nombre de la variable un separador punto (.), el nombre
del método y un conjunto de paréntesis, que contienen los argumentos del método.
• En UML, cada clase se modela en un diagrama de clases en forma de rectángulo con tres compartimientos. El
compartimiento superior contiene el nombre de la clase, centrado horizontalmente y en negrita. El compartimiento
intermedio contiene los atributos de la clase, que corresponden a los campos en Java. El compartimiento inferior
contiene las operaciones de la clase, que corresponden a los métodos y constructores en Java.
• Para modelar las operaciones, UML lista el nombre de la operación, seguido de un conjunto de paréntesis. Un signo
más (+) enfrente del nombre de la operación indica que ésta es una operación public en UML (es decir, un método
public en Java).
Sección 3.4 Declaración de un método con un parámetro
• A menudo, los métodos requieren información adicional para realizar sus tareas. Dicha información adicional se
proporciona mediante argumentos en las llamadas a los métodos.
• El método nextLine de Scanner lee caracteres hasta encontrar una nueva línea, y después devuelve los caracteres que
leyó en forma de un objeto String.
• El método next de Scanner lee caracteres hasta encontrar cualquier carácter de espacio en blanco, y después devuelve
los caracteres que leyó en forma de un objeto String.
• Un método que requiere datos para realizar su tarea debe especificar esto en su declaración, para lo cual coloca infor-
mación adicional en la lista de parámetros del método.
• Cada parámetro debe especificar tanto un tipo como un identificador.
• Cuando se hace la llamada a un método, sus argumentos se asignan a sus parámetros. Entonces, el cuerpo del méto-
do utiliza las variables de los parámetros para acceder a los valores de los argumentos.
• Un método puede especificar varios parámetros, separando un parámetro del otro mediante una coma.
• El número de argumentos en la llamada a un método debe coincidir con el número de parámetros en la lista de
parámetros de la declaración del método. Además, los tipos de los argumentos en la llamada al método deben ser
consistentes con los tipos de los parámetros correspondientes en la declaración del método.
• La clase String está en el paquete java.lang que, por lo general se importa de manera implícita en todos los archivos
de código fuente.
• Hay una relación especial entre las clases que se compilan en el mismo directorio en el disco. De manera predetermi-
nada, se considera que dichas clases están en el mismo paquete, al cual se le conoce como paquete predeterminado.
Las clases en el mismo paquete se importan implícitamente en los archivos de código fuente de las otras clases que
están en el mismo paquete. Por ende, no se requiere una declaración import cuando una clase en un paquete utiliza
a otra clase en el mismo paquete.
• No se requiere una declaración import si siempre hacemos referencia a una clase con su nombre de clase completa-
mente calificado.
• Para modelar un parámetro de una operación, UML lista el nombre del parámetro, seguido de dos puntos y el tipo
del parámetro entre los paréntesis que van después del nombre de la operación.
• UML tiene sus propios tipos de datos, similares a los de Java. No todos los tipos de datos de UML tienen los mismos
nombres que los tipos correspondientes en Java.
• El tipo String de UML corresponde al tipo String de Java.
Sección 3.5 Variables de instancia, métodos establecer y métodos obtener
• Las variables que se declaran en el cuerpo de un método específico se conocen como variables locales, y pueden
utilizarse sólo en ese método.
• Por lo general, una clase consiste en uno o más métodos que manipulan los atributos (datos) pertenecientes a un
objeto específico de esa clase. Los atributos se representan como campos en la declaración de una clase. Dichas
variables se llaman campos, y se declaran dentro de la declaración de una clase, pero fuera de los cuerpos de las
declaraciones de los métodos de esa clase.
• Cuando cada objeto de una clase mantiene su propia copia de un atributo, el campo que representa a ese atributo
también se conoce como variable de instancia. Cada objeto (instancia) de la clase tiene una instancia separada de la
variable en la memoria.
• La mayoría de las declaraciones de variables de instancia van precedidas por el modificador de acceso private. Las
variables o métodos declarados con el modificador de acceso private sólo están accesibles para los métodos de la
clase en la que están declarados.
• Al proceso de declarar variables de instancia con el modificador de acceso private se le conoce como ocultamiento
de datos.
106 Capítulo 3 Introducción a las clases y los objetos
• Un beneficio de los campos es que todos los métodos de la clase pueden usarlos. Otra diferencia entre un campo y
una variable local es que un campo tiene un valor inicial predeterminado, que Java proporciona cuando el progra-
mador no especifica el valor inicial del campo, pero una variable local no hace esto.
• El valor predeterminado para un campo de tipo String es null.
• Cuando se llama a un método que especifica un tipo de valor de retorno y completa su tarea, el método devuelve un
resultado al método que lo llamó.
• A menudo, las clases proporcionan métodos public para permitir que los clientes de la clase establezcan u obtengan
variables de instancia private. Los nombres de estos métodos no necesitan comenzar con establecer u obtener, pero
esta convención de nomenclatura es muy recomendada en Java, y requerida para ciertos componentes de software
de Java especiales, conocidos como JavaBeans.
• UML representa a las variables de instancia como atributos, listando el nombre del atributo, seguido de dos puntos
y el tipo del atributo.
• En UML, los atributos privados van precedidos por un signo menos (-).
• Para indicar el tipo de valor de retorno de una operación, UML coloca dos puntos y el tipo de valor de retorno
después de los paréntesis que siguen del nombre de la operación.
• Los diagramas de clases de UML no especifican tipos de valores de retorno para las operaciones que no devuelven
valores.
Sección 3.6 Comparación entre tipos primitivos y tipos por referencia
• En Java, los tipos se dividen en dos categorías: tipos primitivos y tipos por referencia (algunas veces conocidos
como tipos no primitivos). Los tipos primitivos son boolean, byte, char, short, int, long, float y double.
Todos los demás tipos son por referencia, por lo cual, las clases que especifican los tipos de los objetos, son tipos
por referencia.
• Una variable de tipo primitivo puede almacenar exactamente un valor de su tipo declarado, en un momento dado.
• Las variables de instancia de tipos primitivos se inicializan de manera predeterminada. Las variables de los tipos
byte, char, short, int, long, float y double se inicializan con 0. Las variables de tipo boolean se inicializan con
false.
• Los programas utilizan variables de tipos por referencia (llamadas referencias) para almacenar la ubicación de un
objeto en la memoria de la computadora. Dichas variables hacen referencia a los objetos en el programa. El objeto
al que se hace referencia puede contener muchas variables de instancia y métodos.
• Los campos de tipo por referencia se inicializan de manera predeterminada con el valor null.
• Para invocar a los métodos de instancia de un objeto, se requiere una referencia a éste. Una variable de tipo primitivo
no hace referencia a un objeto, por lo cual no puede usarse para invocar a un método.
Sección 3.7 Inicialización de objetos con constructores
• Un constructor puede usarse para inicializar un objeto de una clase, a la hora de crear este objeto.
• Los constructores pueden especificar parámetros, pero no tipos de valores de retorno.
• Si no se proporciona un constructor para una clase, el compilador proporciona un constructor predeterminado sin
parámetros.
• Cuando una clase sólo tiene el constructor predeterminado, sus variables de instancia se inicializan con sus valores
predeterminados. Las variables de los tipos char, byte, short, int, long, float y double se inicializan con 0, las
variables de tipo boolean se inicializan con false, y las variables de tipo por referencia se inicializan con null.
• Al igual que las operaciones, UML modela a los constructores en el tercer compartimiento de un diagrama de clases.
Para diferenciar a un constructor en base a las operaciones de una clase, UML coloca la palabra “constructor” entre
los signos « y » antes del nombre del constructor.
Sección 3.8 Números de punto flotante y el tipo double
• Un número de punto flotante es un número con un punto decimal, como 7.33, 0.0975 o 1000.12345. Java propor-
ciona dos tipos primitivos para almacenar números de punto flotante en la memoria –float y double. La principal
diferencia entre estos tipos es que las variables double pueden almacenar números con mayor magnitud y detalle (a
esto se le conoce como la precisión del número) que las variables float.
• Las variables de tipo float representan números de punto flotante de precisión simple, y tienen siete dígitos signifi-
cativos. Las variables de tipo double representan números de punto flotante de precisión doble. Éstos requieren el
doble de memoria que las variables float y proporcionan 15 dígitos significativos; tienen aproximadamente el doble
de precisión de las variables float.
• Los valores de punto flotante que aparecen en código fuente se conocen como literales de punto flotante, y son de
tipo double de manera predeterminada.
Resumen 107
• El método nextDouble de Scanner devuelve un valor double.
• El especificador de formato %f se utiliza para mostrar valores de tipo float o double. Puede especificarse una precisión
entre % y f para representar el número de posiciones decimales que deben mostrarse a la derecha del punto decimal,
en el número de punto flotante.
• El valor predeterminado para un campo de tipo double es 0.0, y el valor predeterminado para un campo de tipo int
es 0.
Terminología
108 Capítulo 3 Introducción a las clases y los objetos
%f,especificador de formato
« y » (UML)
agregación (UML)
atributo (UML)
campo
campo de texto (GUI)
clase
class, palabra clave
cliente de un objeto de una clase
compartimiento en un diagrama de clases (UML)
componente de interfaz gráfica de usuario (GUI)
composición (UML)
constructor
constructor predeterminado
crear un objeto
cuadro de diálogo (GUI)
cuadro de diálogo de entrada (GUI)
cuadro de diálogo de mensaje (GUI)
declaración de clase
declarar un método
diagrama con elementos omitidos (UML)
diagrama de clases de UML
diálogo (GUI)
diamante sólido (UML)
double, tipo primitivo
encabezado de un método
enviar un mensaje
establecer, método
expresión de creación de instancia de clase
float, tipo primitivo
instancia de clase
instancia de una clase (objeto)
instanciar (o crear) un objeto
interfaz gráfica de usuario (GUI)
invocar a un método
JOptionPane, clase (GUI)
lenguaje extensible
lista de parámetros
literal de punto flotante
llamada a un método
mensaje
método
método que hace la llamada
modificador de acceso
multiplicidad (UML)
new, palabra clave
next, método de la clase Scanner
nextDouble, método de la clase Scanner
nextLine, método de la clase Scanner
nombre de rol (UML)
null, palabra reservada
número de punto flotante
número de punto flotante de precisión doble
número de punto flotante de precisión simple
objeto (o instancia)
ocultamiento de datos
operación (UML)
paquete predeterminado
parámetro
precisión de un número de punto flotante con formato
precisión de un valor de punto flotante
private, modificador de acceso
public, método
public, modificador de acceso
punto (.), separador
referencia
referirse a un objeto
relación “tiene un”
relación de uno a varios (UML)
relación de varios a uno (UML)
relación de varios a varios (UML)
showInputDialog, método de la clase JOptionPane (GUI)
showMessageDialog, método de la clase JOptionPane
(GUI)
tipo de valor de retorno de un método
tipo por referencia
tipos no primitivos
valor inicial predeterminado
valor predeterminado
variable de instancia
variable local
void, palabra clave
Ejercicios de autoevaluación
3.1 Complete las siguientes oraciones:
a) Una casa es para un plano de construcción lo que un(a) ____________ para una clase.
b) Cada declaración de clase que empieza con la palabra clave ____________ debe almacenarse en un archivo
que tenga exactamente el mismo nombre de la clase, y que termine con la extensión de nombre de archi-
vo .java.
c) Cada declaración de clase contiene la palabra clave ____________, seguida inmediatamente por el nombre
de la clase.
d) La palabra clave ____________ crea un objeto de la clase especificada a la derecha de la palabra clave.
e) Cada parámetro debe especificar un(a) ____________ y un(a) ____________.
f) De manera predeterminada, se considera que las clases que se compilan en el mismo directorio están en el
mismo paquete, conocido como ____________.
g) Cuando cada objeto de una clase mantiene su propia copia de un atributo, el campo que representa a este
atributo se conoce también como ____________.
h) Java proporciona dos tipos primitivos para almacenar números de punto flotante en la memoria: _______
_____ y ____________.
i) Las variables de tipo double representan a los números de punto flotante ____________.
j) El método ____________ de la clase Scanner devuelve un valor double.
k) La palabra clave public es un(a) ____________.
l) El tipo de valor de retorno ____________ indica que un método realizará una tarea, pero no devolverá
información cuando complete su tarea.
m) El método ____________ de Scanner lee caracteres hasta encontrar una nueva línea, y después devuelve
esos caracteres como un objeto String.
n) La clase String está en el paquete ____________.
o) No se requiere un(a) ____________ si siempre hacemos referencia a una clase con su nombre de clase
completamente calificado.
p) Un ____________ es un número con un punto decimal, como 7.33, 0.0975 o 1000.12345.
q) Las variables de tipo float representan números de punto flotante ____________.
r) El especificador de formato ____________ se utiliza para mostrar valores de tipo float o double.
s) Los tipos en Java se dividen en dos categorías: tipos ____________ y tipos ____________.
3.2 Conteste con verdadero o falso a cada una de las siguientes proposiciones; en caso de ser falso, explique por qué.
a) Por convención, los nombres de los métodos empiezan con la primera letra en mayúscula y todas las pala-
bras subsiguientes en el nombre empiezan con la primera letra en mayúscula.
b) Una declaración import no es obligatoria cuando una clase en un paquete utiliza a otra clase en el mismo
paquete.
c) Los paréntesis vacíos que van después del nombre de un método en la declaración de un método indican
que éste no requiere parámetros para realizar su tarea.
d) Las variables o los métodos declarados con el modificador de acceso private son accesibles sólo para los
métodos de la clase en la que se declaran.
e) Una variable de tipo primitivo puede usarse para invocar un método.
f) Las variables que se declaran en el cuerpo de un método específico se conocen como variables de instancia,
y pueden utilizarse en todos los métodos de la clase.
g) El cuerpo de cada método está delimitado por llaves izquierda y derecha ({ y }).
h) Las variables locales de tipo primitivo se inicializan de manera predeterminada.
i) Las variables de instancia de tipo por referencia se inicializan de manera predeterminada con el valor null.
j) Cualquier clase que contenga public static void main( String args[] ) puede usarse para ejecutar una
aplicación.
k) El número de argumentos en la llamada a un método debe coincidir con el número de parámetros en la lista
de parámetros de la declaración del método.
l) Los valores de punto flotante que aparecen en código fuente se conocen como literales de punto flotante, y
son de tipo float de manera predeterminada.
3.3 ¿Cuál es la diferencia entre una variable local y un campo?
3.4 Explique el propósito de un parámetro de un método. ¿Cuál es la diferencia entre un parámetro y un argumento?
Ejercicios de autoevaluación 109
Respuestas a los ejercicios de autoevaluación
3.1 a) objeto. b) public. c) class. d) new. e) tipo, nombre. f) paquete predeterminado. g) varia-
ble de instancia. h) float, double. i) de precisión doble. j) nextDouble. k) modificador de acceso. l) void.
m) nextLine. n) java.lang. o) declaracion import. p) número de punto flotante. q) de precisión simple.
r) %f. s) primitivo, por referencia.
3.2 a) Falso. Por convención, los nombres de los métodos empiezan con una primera letra en minúscula y
todas las palabras subsiguientes en el nombre empiezan con una letra en mayúscula. b) Verdadero. c) Verdadero.
d) Verdadero. e) Falso. Una variable de tipo primitivo no puede usarse para invocar a un método; se requiere una
referencia a un objeto para invocar a los métodos de ese objeto. f) Falso. Dichas variables se llaman variables locales,
y sólo se pueden utilizar en el método en el que están declaradas. g) Verdadero. h) Falso. Las variables de instancia
de tipo primitivo se inicializan de manera predeterminada. A cada variable local se le debe asignar un valor de manera
explícita. i) Verdadero. j) Verdadero. k) Verdadero. l) Falso. Dichas literales son de tipo double de manera pre-
determinada.
3.3 Una variable local se declara en el cuerpo de un método, y sólo puede utilizarse desde el punto en el que se
declaró, hasta el final de la declaración del método. Un campo se declara en una clase, pero no en el cuerpo de alguno
de los métodos de la clase. Cada objeto (instancia) de una clase tiene una copia separada de los campos de la clase.
Además, los campos están accesibles para todos los métodos de la clase. (En el capítulo 8, Clases y objetos: un análisis
más detallado, veremos una excepción a esto).
3.4 Un parámetro representa la información adicional que requiere un método para realizar su tarea. Cada paráme-
tro requerido por un método está especificado en la declaración del método. Un argumento es el valor actual para un
parámetro del método. Cuando se llama a un método, los valores de los argumentos se pasan al método, para que éste
pueda realizar su tarea.
Ejercicios
3.5 ¿Cuál es el propósito de la palabra clave new? Explique lo que ocurre cuando se utiliza en una aplicación.
3.6 ¿Qué es un constructor predeterminado? ¿Cómo se inicializan las variables de instancia de un objeto, si una
clase sólo tiene un constructor predeterminado?
3.7 Explique el propósito de una variable de instancia.
3.8 La mayoría de las clases necesitan importarse antes de poder utilizarlas en una aplicación ¿Por qué cualquier
aplicación puede utilizar las clases System y String sin tener que importarlas primero?
3.9 Explique cómo utilizaría un programa la clase Scanner, sin importarla del paquete java.util.
3.10 Explique por qué una clase podría proporcionar un método establecer y un método obtener para una variable de
instancia.
3.11 Modifique la clase LibroCalificaciones (figura 3.10) de la siguiente manera:
a) Incluya una segunda variable de instancia String, que represente el nombre del instructor del curso.
b) Proporcione un método establecer para modificar el nombre del instructor, y un método obtener para obte-
ner el nombre.
c) Modifique el constructor para especificar dos parámetros: uno para el nombre del curso y otro para el nom-
bre del instructor.
d) Modifique el método mostrarMensaje, de tal forma que primero imprima el mensaje de bienvenida y el
nombre del curso, y que después imprima "Este curso es presentado por: ", seguido del nombre del
instructor.
Use su clase modificada en una aplicación de prueba que demuestre las nuevas capacidades de la clase.
3.12 Modifique la clase Cuenta (figura 3.13) para proporcionar un método llamado cargar, que retire dinero de
un objeto Cuenta. Asegure que el monto a cargar no exceda el saldo de Cuenta. Si lo hace, el saldo debe permanecer sin
cambio y el método debe imprimir un mensaje que indique "El monto a cargar excede el saldo de la cuenta".
Modifique la clase PruebaCuenta (figura 3.14) para probar el método cargar.
3.13 Cree una clase llamada Factura, que una ferretería podría utilizar para representar una factura para un artículo
vendido en la tienda. Una Factura debe incluir cuatro piezas de información como variables de instancia: un número
de pieza (tipo String), la descripción de la pieza (tipo String), la cantidad de artículos de ese tipo que se van a comprar
110 Capítulo 3 Introducción a las clases y los objetos
(tipo int) y el precio por artículo (double). Su clase debe tener un constructor que inicialice las cuatro variables de ins-
tancia. Proporcione un método establecer y un método obtener para cada variable de instancia. Además, proporcione un
método llamado obtenerMontoFactura, que calcule el monto de la factura (es decir, que multiplique la cantidad por el
precio por artículo) y después devuelva ese monto como un valor double. Si la cantidad no es positiva, debe establecerse
en 0. Si el precio por artículo no es positivo, debe establecerse a 0.0. Escriba una aplicación de prueba llamada Prueba-
Factura, que demuestre las capacidades de la clase Factura.
3.14 Cree una clase llamada Empleado, que incluya tres piezas de información como variables de instancia: un primer
nombre (tipo String), un apellido paterno (tipo String) y un salario mensual (double). Su clase debe tener un cons-
tructor que inicialice las tres variables de instancia. Proporcione un método establecer y un método obtener para cada
variable de instancia. Si el salario mensual no es positivo, establézcalo a 0.0. Escriba una aplicación de prueba llama-
da PruebaEmpleado, que demuestre las capacidades de cada Empleado. Cree dos objetos Empleado y muestre el salario
anual de cada objeto. Después, proporcione a cada Empleado un aumento del 10% y muestre el salario anual de cada
Empleado otra vez.
3.15 Cree una clase llamada Fecha, que incluya tres piezas de información como variables de instancia —un mes
(tipo int), un día (tipo int) y un año (tipo int). Su clase debe tener un constructor que inicialice las tres variables
de instancia, y debe asumir que los valores que se proporcionan son correctos. Proporcione un método establecer y un
método obtener para cada variable de instancia. Proporcione un método mostrarFecha, que muestre el mes, día y año,
separados por barras diagonales (/). Escriba una aplicación de prueba llamada PruebaFecha, que demuestre las capaci-
dades de la clase Fecha.
Ejercicios 111
Instrucciones
de control:
parte 1
OBJETIVO S
En este capítulo aprenderá a:
Comprender las técnicas básicas para solucionar problemas.
Desarrollar algoritmos mediante el proceso de refinamiento
de arriba a abajo, paso a paso, usando seudocódigo.
Utilizar las estructuras de selección if e if...else para elegir
entre distintas acciones alternativas.
Utilizar la estructura de repetición while para ejecutar
instrucciones de manera repetitiva dentro de un programa.
Comprender la repetición controlada por un contador
y la repetición controlada por un centinela.
Utilizar los operadores de asignación compuestos,
de incremento y decremento.
Conocer los tipos de datos primitivos.
Q
Q
Q
Q
Q
Q
Q
Desplacémonos un lugar.
—Lewis Carroll
La rueda se convirtió en un
círculo completo.
—William Shakespeare
¡Cuántas manzanas tuvieron
que caer en la cabeza
de Newton antes de que
entendiera el suceso!
—Robert Frost
Toda la evolución que
conocemos procede de lo
vago a lo definido.
—Charles Sanders Peirce
4
4.2 Algoritmos 113
4.1 Introducción
Antes de escribir un programa que dé solución a un problema, es imprescindible tener una comprensión detallada
de todo el problema, además de una metodología cuidadosamente planeada para resolverlo. Al escribir un progra-
ma, es igualmente esencial comprender los tipos de bloques de construcción disponibles, y emplear las técnicas
comprobadas para construir programas. En este capítulo y en el 5, Instrucciones de control: parte 2, hablaremos
sobre estas cuestiones cuando presentemos la teoría y los principios de la programación estructurada. Los concep-
tos aquí presentados son imprescindibles para crear clases y manipular objetos.
En este capítulo presentamos las instrucciones if...else y while de Java, tres de los bloques de construc-
ción que permiten a los programadores especificar la lógica requerida para que los métodos realicen sus tareas.
Dedicamos una parte de este capítulo (y de los capítulos 5 y 7) para desarrollar más la clase LibroCalificaciones
que presentamos en el capítulo 3. En especial, agregamos un método a la clase LibroCalificaciones que utiliza
instrucciones de control para calcular el promedio de un conjunto de calificaciones de estudiantes. Otro ejemplo
demuestra formas adicionales de combinar instrucciones de control para resolver un problema similar. Presenta-
mos los operadores de asignación compuestos de Java, y exploramos los operadores de incremento y decremento.
Estos operadores adicionales abrevian y simplifican muchas instrucciones de los programas. Por último, presenta-
mos las generalidades acerca de los tipos de datos primitivos que están disponibles para los programadores.
4.2 Algoritmos
Cualquier problema de computación puede resolverse ejecutando una serie de acciones en un orden específico.
Un procedimiento para resolver un problema en términos de:
1. las acciones a ejecutar y
2. el orden en el que se ejecutan estas acciones
se conoce como un algoritmo. El siguiente ejemplo demuestra que es importante especificar de manera correcta
el orden en el que se ejecutan las acciones.
Considere el “algoritmo para levantarse y arreglarse” que sigue un ejecutivo para levantarse de la cama e ir
a trabajar: (1) levantarse; (2) quitarse la pijama; (3) bañarse; (4) vestirse; (5) desayunar; (6) transportarse al tra-
bajo. Esta rutina logra que el ejecutivo llegue al trabajo bien preparado para tomar decisiones críticas. Suponga
4.1 Introducción
4.2 Algoritmos
4.3 Seudocódigo
4.4 Estructuras de control
4.5 Instrucción de selección simple if
4.6 Instrucción de selección doble if...else
4.7 Instrucción de repetición while
4.8 Cómo formular algoritmos: repetición controlada por un contador
4.9 Cómo formular algoritmos: repetición controlada por un centinela
4.10 Cómo formular algoritmos: instrucciones de control anidadas
4.11 Operadores de asignación compuestos
4.12 Operadores de incremento y decremento
4.13 Tipos primitivos
4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples
4.15 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de los atributos de las clases
4.16 Conclusión
Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios
Pla
n
g
e
ne
r
a
l
114 Capítulo 4 Instrucciones de control: parte 1
que los mismos pasos se realizan en un orden ligeramente distinto: (1) levantarse; (2) quitarse la pijama;
(3) vestirse; (4) bañarse; (5) desayunar; (6) transportarse al trabajo. En este caso nuestro ejecutivo llegará al
trabajo todo mojado.
Al proceso de especificar el orden en el que se ejecutan las instrucciones (acciones) en un programa, se le
llama control del programa. En este capítulo investigaremos el control de los programas mediante el uso de las
instrucciones de control de Java.
4.3 Seudocódigo
El seudocódigo es un lenguaje informal que ayuda a los programadores a desarrollar algoritmos sin tener que
preocuparse por los estrictos detalles de la sintaxis del lenguaje Java. El seudocódigo que presentaremos es espe-
cialmente útil para desarrollar algoritmos que se convertirán en porciones estructuradas de programas en Java. El
seudocódigo es similar al lenguaje cotidiano; es conveniente y amigable con el usuario, aunque no es realmente
un lenguaje de programación de computadoras. Empezaremos a utilizar el seudocódigo en la sección 4.5, y en la
figura 4.5 aparece un programa de seudocódigo de ejemplo.
El seudocódigo no se ejecuta en las computadoras. En vez de ello, ayuda al programador a “organizar” un
programa antes de que intente escribirlo en un lenguaje de programación como Java. Este capítulo presenta varios
ejemplos de cómo utilizar el seudocódigo para desarrollar programas en Java.
El estilo de seudocódigo que presentaremos consiste solamente en caracteres, de manera que los programa-
dores pueden escribir el seudocódigo, utilizando cualquier programa editor de texto. Un programa en seudocó-
digo preparado de manera cuidadosa puede convertirse fácilmente en su correspondiente programa en Java. En
muchos casos, esto requiere tan sólo reemplazar las instrucciones en seudocódigo con sus instrucciones equiva-
lentes en Java.
Por lo general, el seudocódigo describe sólo las instrucciones que representan las acciones que ocurren des-
pués de que un programador convierte un programa de seudocódigo a Java, y el programa se ejecuta en una
computadora. Dichas acciones podrían incluir la entrada, salida o un cálculo. Por lo general no incluimos las
declaraciones de variables en nuestro seudocódigo, pero algunos programadores optan por listar las variables y
mencionar sus propósitos al principio de su seudocódigo.
4.4 Estructuras de control
Generalmente, en un programa las instrucciones se ejecutan una después de otra, en el orden en que están escritas.
Este proceso se conoce como ejecución secuencial. Varias instrucciones en Java, que pronto veremos, permiten
al programador especificar que la siguiente instrucción a ejecutarse tal vez no sea la siguiente en la secuencia. Esto
se conoce como transferencia de control.
Durante la década de los sesenta, se hizo evidente que el uso indiscriminado de las transferencias de control
era el origen de muchas de las dificultades que experimentaban los grupos de desarrollo de software. A quien se
señaló como culpable fue a la instrucción goto (utilizada en la mayoría de los lenguajes de programación de esa
época), la cual permite al programador especificar la transferencia de control a uno de los muchos posibles desti-
nos dentro de un programa. La noción de la llamada programación estructurada se hizo casi un sinónimo de la
“eliminación del goto”. [Nota: Java no tiene una instrucción goto; sin embargo, la palabra goto está reservada
para Java y no debe usarse como identificador en los programas].
Las investigaciones de Bohm y Jacopini1 demostraron que los programas podían escribirse sin instrucciones
goto. El reto de la época para los programadores fue cambiar sus estilos a una “programación sin goto”. No fue
sino hasta la década de los setenta cuando los programadores tomaron en serio la programación estructurada. Los
resultados fueron impresionantes. Los grupos de desarrollo de software reportaron reducciones en los tiempos de
desarrollo, mayor incidencia de entregas de sistemas a tiempo y más proyectos de software finalizados sin salirse del
presupuesto. La clave para estos logros fue que los programas estructurados eran más claros, más fáciles de depurar
y modificar, y había más probabilidad de que estuvieran libres de errores desde el principio.
1. Bohm, C. y G. Jacopini, “Flow Diagrams, Turing Machines and Languages with Only Two Formation Rules”, Communications of the
ACM, vol. 9, núm. 5, mayo de 1966, páginas 336-371.
4.4 Estructuras de control 115
El trabajo de Bohm y Jacopini demostró que todos los programas podían escribirse en términos de tres
estructuras de control solamente: la estructura de secuencia, la estructura de selección y la estructura de
repetición. El término “estructuras de control” proviene del campo de las ciencias computacionales. Cuando
presentemos las implementaciones de las estructuras de control en Java, nos referiremos a ellas en la terminología
de la Especificación del lenguaje Java como “instrucciones de control”.
Estructura de secuencia en Java
La estructura de secuencia está integrada en Java. A menos que se le indique lo contrario, la computadora ejecuta
las instrucciones en Java una después de otra, en el orden en que estén escritas; es decir, en secuencia. El diagrama
de actividad de la figura 4.1 ilustra una estructura de secuencia típica, en la que se realizan dos cálculos en orden.
Java permite tantas acciones como deseemos en una estructura de secuencia. Como veremos pronto, en donde
quiera que se coloque una sola acción, podrán colocarse varias acciones en secuencia.
Los diagramas de actividad son parte de UML. Un diagrama de actividad modela el flujo de trabajo (tam-
bién conocido como la actividad) de una parte de un sistema de software. Dichos flujos de trabajo pueden incluir
una porción de un algoritmo, como la estructura de secuencia de la figura 4.1. Los diagramas de actividad están
compuestos por símbolos de propósito especial, como los símbolos de estado de acción (rectángulos cuyos lados
izquierdo y derecho se reemplazan con arcos hacia fuera), rombos (diamantes) y pequeños círculos. Estos sím-
bolos se conectan mediante flechas de transición, que representan el flujo de la actividad; es decir, el orden en el
que deben ocurrir las acciones.
Al igual que el seudocódigo, los diagramas de actividad ayudan a los programadores a desarrollar y represen-
tar algoritmos; sin embargo, muchos de ellos aún prefieren el seudocódigo. Los diagramas de actividad muestran
claramente cómo operan las estructuras de control.
Considere el diagrama de actividad para la estructura de secuencia de la figura 4.1. Este diagrama contiene
dos estados de acción que representan las acciones a realizar. Cada estado de acción contiene una expresión de
acción (por ejemplo, “sumar calificación a total” o “sumar 1 al contador”), que especifica una acción particular
a realizar. Otras acciones podrían incluir cálculos u operaciones de entrada/salida. Las flechas en el diagrama de
actividad representan transiciones, las cuales indican el orden en el que ocurren las acciones representadas por los
estados de acción. El programa que implementa las actividades ilustradas por el diagrama de la figura 4.1 primero
suma calificacion a total, y después suma 1 a contador.
El círculo relleno que se encuentra en la parte superior del diagrama de actividad representa el estado inicial
de la actividad: el inicio del flujo de trabajo antes de que el programa realice las actividades modeladas. El círculo
sólido rodeado por una circunferencia que aparece en la parte inferior del diagrama representa el estado final;
es decir, el final del flujo de trabajo después de que el programa realiza sus acciones.
La figura 4.1 también incluye rectángulos que tienen la esquina superior derecha doblada. En UML, a estos
rectángulos se les llama notas (como los comentarios en Java): comentarios con explicaciones que describen el
propósito de los símbolos en el diagrama. La figura 4.1 utiliza las notas de UML para mostrar el código en Java
asociado con cada uno de los estados de acción en el diagrama de actividad. Una línea punteada conecta cada
nota con el elemento que ésta describe. Los diagramas de actividad generalmente no muestran el código en
Java que implementa la actividad. En este libro utilizamos las notas con este propósito, para mostrar cómo se rela-
sumar 1 al contador
sumar calificación al total
Instrucción en Java correspondiente:
total = total + calificacion;
Instrucción en Java correspondiente:
contador = contador + 1;
Figura 4.1 | Diagrama de actividad de una estructura de secuencia.
116 Capítulo 4 Instrucciones de control: parte 1
ciona el diagrama con el código en Java. Para obtener más información sobre UML, vea nuestro ejemplo práctico
opcional, que aparece en las secciones tituladas Ejemplo práctico de Ingeniería de Software al final de los capítulos
1 al 8 y 10, o visite www.uml.org.
Instrucciones de selección en Java
Java tiene tres tipos de instrucciones de selección (las cuales se describen en este capítulo y en el siguiente). La
instrucción if realiza (selecciona) una acción si la condición es verdadera, o evita la acción si la condición es
falsa. La instrucción if...else realiza una acción si la condición es verdadera, o realiza una acción distinta si la
condición es falsa. La instrucción switch (capítulo 5) realiza una de entre varias acciones distintas, dependiendo
del valor de una expresión.
La instrucción if es una instrucción de selección simple, ya que selecciona o ignora una sola acción (o,
como pronto veremos, un solo grupo de acciones). La instrucción if...else se conoce como instrucción de
selección doble, ya que selecciona entre dos acciones distintas (o grupos de acciones). La instrucción switch es
una estructura de selección múltiple, ya que selecciona entre diversas acciones (o grupos de acciones).
Instrucciones de repetición en Java
Java cuenta con tres instrucciones de repetición (también llamadas instrucciones de ciclo) que permiten a los
programas ejecutar instrucciones en forma repetida, siempre y cuando una condición (llamada la condición de
continuación del ciclo) siga siendo verdadera. Las instrucciones de repetición se implementan con las instruc-
ciones while, do...while y for. (El capítulo 5 presenta las instrucciones do...while y for). Las instrucciones
while y for realizan la acción (o grupo de acciones) en sus cuerpos, cero o más veces; si la condición de conti-
nuación del ciclo es inicialmente falsa, no se ejecutará la acción (o grupo de acciones). La instrucción do...while
realiza la acción (o grupo de acciones) en su cuerpo, una o más veces.
Las palabras if, else, switch, while, do y for son palabras clave en Java; se utilizan para implementar varias
características de Java, como las instrucciones de control. Las palabras clave no pueden usarse como identificadores,
como los nombres de variables. En el apéndice C aparece una lista completa de las palabras clave en Java.
Resumen de las instrucciones de control en Java
Java sólo tiene tres tipos de estructuras de control, a las cuales nos referiremos de aquí en adelante como instruc-
ciones de control: la instrucción de secuencia, las instrucciones de selección (tres tipos) y las instrucciones de repe-
tición (tres tipos). Cada programa se forma combinando tantas instrucciones de secuencia, selección y repetición
como sea apropiado para el algoritmo que implemente el programa. Al igual que con la instrucción de secuencia
de la figura 4.1, podemos modelar cada una de las instrucciones de control como un diagrama de actividad. Cada
diagrama contiene un estado inicial y final, los cuales representan el punto de entrada y salida de la instrucción de
control, respectivamente. Las instrucciones de control de una sola entrada/una sola salida facilitan la creación
de programas; las instrucciones de control están “unidas” entre sí mediante la conexión del punto de salida de una
instrucción de control, al punto de entrada de la siguiente. Este procedimiento es similar a la manera en que un
niño apila los bloques de construcción, así que a esto le llamamos apilamiento de instrucciones de control. En
breve aprenderemos que sólo hay una manera alternativa de conectar las instrucciones de control: el anidamiento
de instrucciones de control, en el cual una instrucción de control aparece dentro de otra. Por lo tanto, los algo-
ritmos en los programas en Java se crean a partir de sólo tres principales tipos de instrucciones de control, que se
combinan sólo de dos formas. Ésta es la esencia de la simpleza.
4.5 Instrucción de selección simple if
Los programas utilizan instrucciones de selección para elegir entre los cursos alternativos de acción. Por ejemplo,
suponga que la calificación para aprobar un examen es 60. La instrucción en seudocódigo
Si la calificación del estudiante es mayor o igual a 60
Imprimir “Aprobado”
determina si la condición “la calificación del estudiante es mayor o igual a 60” es verdadera o falsa. Si la condición
es verdadera se imprime “Aprobado”, y se “ejecuta” en orden la siguiente instrucción en seudocódigo. (Recuerde
que el seudocódigo no es un verdadero lenguaje de programación). Si la condición es falsa se ignora la instrucción
Imprimir, y se ejecuta en orden la siguiente instrucción en seudocódigo. La sangría de la segunda línea de esta
instrucción de selección es opcional, pero se recomienda ya que enfatiza la estructura inherente de los programas
estructurados.
La instrucción anterior if en seudocódigo puede escribirse en Java de la siguiente manera:
if ( calificacionEstudiante >= 60 )
System.out.println( "Aprobado" );
Observe que el código en Java corresponde en gran medida con el seudocódigo. Ésta es una de las propiedades
que hace del seudocódigo una herramienta de desarrollo de programas tan útil.
La figura 4.2 muestra la instrucción if de selección simple. Esta figura contiene lo que quizá sea el símbolo
más importante en un diagrama de actividad: el rombo o símbolo de decisión, el cual indica que se tomará
una decisión. El flujo de trabajo continuará a lo largo de una ruta determinada por las condiciones de guardia
asociadas de ese símbolo, que pueden ser verdaderas o falsas. Cada flecha de transición que sale de un símbolo
de decisión tiene una condición de guardia (especificada entre corchetes, a un lado de la flecha de transición).
Si una condición de guardia es verdadera, el flujo de trabajo entra al estado de acción al que apunta la flecha de
transición. En la figura 4.2, si la calificación es mayor o igual a 60, el programa imprime “Aprobado” y luego se
dirige al estado final de esta actividad. Si la calificación es menor a 60, el programa se dirige inmediatamente al
estado final sin mostrar ningún mensaje.
La instrucción if es una instrucción de control de una sola entrada/una sola salida. Pronto veremos que los
diagramas de actividad para las instrucciones de control restantes también contienen estados iniciales, flechas
de transición, estados de acción que indican las acciones a realizar, símbolos de decisión (con sus condiciones de
guardia asociadas) que indican las decisiones a tomar, y estados finales. Esto es consistente con el modelo
de programación acción/decisión que hemos estado enfatizando.
Imagine siete cajones, en donde cada uno contiene sólo un tipo de instrucción de control de Java. Todas
las instrucciones de control están vacías. Su tarea es ensamblar un programa a partir de tantas instrucciones de
control de cada tipo como lo requiera el algoritmo, combinando esas instrucciones de control en sólo dos formas
posibles (apilando o anidando), y después llenando los estados de acción y las decisiones con expresiones de
acción y condiciones de guardia, en una manera que sea apropiada para el algoritmo. Hablaremos sobre la varie-
dad de formas en que pueden escribirse las acciones y las decisiones.
imprimir “Aprobado”
[calificacion >= 60]
[calificacion < 60]
Figura 4.2 | Diagrama de actividad en UML de la instrucción if de selección simple.
4.6 Instrucción de selección doble if...else
La instrucción if de selección simple realiza una acción indicada solamente cuando la condición es verdadera
(true); de no ser así, se evita dicha acción. La instrucción if...else de selección doble permite al programador
especificar una acción a realizar cuando la condición es verdadera, y otra distinta cuando la condición es falsa. Por
ejemplo, la instrucción en seudocódigo:
Si la calificación del estudiante es mayor o igual a 60
Imprimir “Aprobado”
De lo contrario
Imprimir “Reprobado”
4.6 Instrucción de selección doble if...else 117
118 Capítulo 4 Instrucciones de control: parte 1
imprime “Aprobado” si la calificación del estudiante es mayor o igual a 60, y, “Reprobado” si la calificación del
estudiante es menor a 60. En cualquier caso, después de que ocurre la impresión se “ejecuta”, según la secuencia,
la siguiente instrucción en seudocódigo.
La instrucción anterior if...else en seudocódigo puede escribirse en Java como
if ( calificacion >= 60 )
System.out.println( "Aprobado" );
else
System.out.println( "Reprobado" );
Observe que el cuerpo de la instrucción else también tiene sangría. Cualquiera que sea la convención de sangría
que usted elija, debe aplicarla consistentemente en todos sus programas. Es difícil leer programas que no obede-
cen las convenciones de espaciado uniformes.
Buena práctica de programación 4.1
Utilice sangría en ambos cuerpos de instrucciones de una estructura if...else.
Buena práctica de programación 4.2
Si hay varios niveles de sangría, en cada nivel debe aplicarse la misma cantidad de espacio adicional.
La figura 4.3 muestra el flujo de control en la instrucción if...else. Una vez más (además del estado
inicial, las flechas de transición y el estado final), los símbolos en el diagrama de actividad de UML representan
estados de acción y decisiones. Nosotros seguimos enfatizando este modelo de computación acción/decisión. Ima-
gine de nuevo un cajón profundo que contiene tantas instrucciones if...else vacías como sea necesario para
crear cualquier programa en Java. Su trabajo es ensamblar estas instrucciones if...else (apilando o anidando)
con cualquier otra estructura de control requerida por el algoritmo. Usted debe llenar los estados de acción y los
símbolos de decisión con expresiones de acción y condiciones de guardia que sean apropiadas para el algoritmo
que esté desarrollando.
Operador condicional (?:)
Java cuenta con el operador condicional (?:), que en ocasiones puede utilizarse en lugar de una instrucción
if...else. Éste es el único operador ternario en Java; es decir, que utiliza tres operandos. En conjunto, los
operandos y el símbolo ?: forman una expresión condicional. El primer operando (a la izquierda del ?) es una
expresión booleana (es decir, una condición que se evalúa a un valor booleano: true o false), el segundo
operando (entre el ? y :) es el valor de la expresión condicional si la expresión booleana es verdadera, y el ter-
cer operando (a la derecha de :) es el valor de la expresión condicional si la expresión booleana se evalúa como
false. Por ejemplo, la instrucción
System.out.println( calificacionEstudiante >= 60 ? "Aprobado" : "Reprobado" );
imprime el valor del argumento de println, que es una expresión condicional. La expresión condicional en esta
instrucción produce como resultado la cadena "Aprobado" si la expresión booleana calificacionEstudiante
>= 60 es verdadera, o produce como resultado la cadena "Reprobado" si la expresión booleana es falsa. Por lo
tanto, esta instrucción con el operador condicional realiza en esencia la misma función que la instrucción if...
else que se mostró anteriormente, en esta sección. La precedencia del operador condicional es baja, por lo que
toda la expresión condicional se coloca normalmente entre paréntesis. Pronto veremos que las expresiones condi-
cionales pueden usarse en algunas situaciones en las que no se pueden utilizar instrucciones if...else.
Buena práctica de programación 4.3
Las expresiones condicionales son más difíciles de leer que las instrucciones if...else, por lo cual deben usarse para
reemplazar sólo a las instrucciones if...else simples que seleccionan uno de dos valores.
Instrucciones if...else anidadas
Un programa puede evaluar varios casos colocando instrucciones if...else dentro de otras instrucciones if...
else, para crear instrucciones if...else anidadas. Por ejemplo, el siguiente seudocódigo representa una ins-
trucción if...else anidada que imprime A para las calificaciones de exámenes mayores o iguales a 90, B para las
calificaciones en el rango de 80 a 89, C para las calificaciones en el rango de 70 a 79, D para las calificaciones en el
rango de 60 a 69 y F para todas las demás calificaciones:
Si la calificación del estudiante es mayor o igual a 90
Imprimir “A”
de lo contrario
Si la calificación del estudiante es mayor o igual a 80
Imprimir “B”
de lo contrario
Si la calificación del estudiante es mayor o igual a 70
Imprimir “C”
de lo contrario
Si la calificación del estudiante es mayor o igual a 60
Imprimir “D”
de lo contrario
Imprimir “F”
Este seudocódigo puede escribirse en Java como
if ( calificacionEstudiante >= 90 )
System.out.println( "A" );
else
if ( calificacionEstudiante >= 80 )
System.out.println( "B" );
else
if ( calificacionEstudiante >= 70 )
System.out.println( "C" );
else
if ( calificacionEstudiante >= 60 )
System.out.println( "D" );
else
System.out.println( "F" );
Si calificacionEstudiante es mayor o igual a 90, las primeras cuatro condiciones serán verdaderas, pero sólo
se ejecutará la instrucción en la parte if de la primera instrucción if...else. Después de que se ejecute esa
instrucción, se evita la parte else de la instrucción if...else más “externa”. La mayoría de los programadores
en Java prefieren escribir la instrucción if...else anterior así:
if ( calificacionEstudiante >= 90 )
System.out.println( "A" );
else if ( calificacionEstudiante >= 80 )
System.out.println( "B" );
else if ( calificacionEstudiante >= 70 )
Imprimir “Aprobado”
imprimir “Reprobado”
[calificacion >= 60]
[calificacion < 60]
Figura 4.3 | Diagrama de actividad de UML de la instrucción if...else de selección doble.
4.6 Instrucción de selección doble if...else 119
120 Capítulo 4 Instrucciones de control: parte 1
System.out.println( "C" );
else if ( calificacionEstudiante >= 60 )
System.out.println( "D" );
else
System.out.println( "F" );
Las dos formas son idénticas, excepto por el espaciado y la sangría, que el compilador ignora. La segunda forma
es más popular ya que evita usar mucha sangría hacia la derecha en el código. Dicha sangría a menudo deja poco
espacio en una línea de código, forzando a que las líneas se dividan y empeorando la legibilidad del programa.
Problema del else suelto
El compilador de Java siempre asocia un else con el if que le precede inmediatamente, a menos que se le indique
otra cosa mediante la colocación de llaves ({ y }). Este comportamiento puede ocasionar lo que se conoce como
el problema del else suelto. Por ejemplo,
if ( x > 5 )
if ( y > 5 )
System.out.println( "x e y son > 5" );
else
System.out.println( "x es <= 5" );
parece indicar que si x es mayor que 5, la instrucción if anidada determina si y es también mayor que 5. De
ser así, se produce como resultado la cadena "x e y son > 5". De lo contrario, parece ser que si x no es mayor
que 5, la instrucción else que es parte del if...else produce como resultado la cadena "x es <= 5".
¡Cuidado! Esta instrucción if...else anidada no se ejecuta como parece ser. El compilador en realidad
interpreta la instrucción así:
if ( x > 5 )
if ( y > 5 )
System.out.println( "x e y son > 5" );
else
System.out.println( "x es <= 5" );
en donde el cuerpo del primer if es un if...else anidado. La instrucción if más externa evalúa si x es mayor
que 5. De ser así, la ejecución continúa evaluando si y es también mayor que 5. Si la segunda condición es verda-
dera, se muestra la cadena apropiada ("x e y son > 5"). No obstante, si la segunda condición es falsa se muestra
la cadena "x es <= 5", aun cuando sabemos que x es mayor que 5. Además, si la condición de la instrucción if
exterior es falsa, se omite la instrucción if...else interior y no se muestra nada en pantalla.
Para forzar a que la instrucción if...else anidada se ejecute como se tenía pensado originalmente, debe
escribirse de la siguiente manera:
if ( x > 5 )
{
if ( y > 5 )
System.out.println( "x e y son > 5" );
}
else
System.out.println( "x es <= 5" );
Las llaves ({}) indican al compilador que la segunda instrucción if se encuentra en el cuerpo del primer if, y
que el else está asociado con el primer if. Los ejercicios 4.27 y 4.28 analizan con más detalle el problema del
else suelto.
Bloques
La instrucción if normalmente espera sólo una instrucción en su cuerpo. Para incluir varias instrucciones en el
cuerpo de un if (o en el cuerpo del else en una instrucción if...else), encierre las instrucciones entre llaves
({ y }). A un conjunto de instrucciones contenidas dentro de un par de llaves se le llama bloque. Un bloque
puede colocarse en cualquier parte de un programa en donde pueda colocarse una sola instrucción.
El siguiente ejemplo incluye un bloque en la parte else de una instrucción if...else:
if ( calificacion >= 60 )
System.out.println( "Aprobado" );
else
{
System.out.println( "Reprobado." );
System.out.println( "Debe tomar este curso otra vez." );
}
En este caso, si calificacion es menor que 60, el programa ejecuta ambas instrucciones en el cuerpo del else
e imprime
Reprobado.
Debe tomar este curso otra vez.
Observe las llaves que rodean a las dos instrucciones en la cláusula else. Estas llaves son importantes. Sin ellas,
la instrucción
System.out.println ( "Debe tomar este curso otra vez." );
estaría fuera del cuerpo de la parte else de la instrucción if...else y se ejecutaría sin importar que la califica-
ción fuera menor a 60.
Los errores de sintaxis (como cuando se omite una llave en un bloque del programa) los atrapa el compilador.
Un error lógico (como cuando se omiten ambas llaves en un bloque del programa) tiene su efecto en tiempo de
ejecución. Un error lógico fatal hace que un programa falle y termine antes de tiempo. Un error lógico no fatal
permite que un programa siga ejecutándose, pero éste produce resultados incorrectos.
Error común de programación 4.1
Olvidar una o las dos llaves que delimitan un bloque puede provocar errores de sintaxis o errores lógicos en un
programa.
Buena práctica de programación 4.4
Colocar siempre las llaves en una instrucción if...else (o cualquier estructura de control) ayuda a evitar que se
omitan de manera accidental, en especial, cuando posteriormente se agregan instrucciones a una cláusula if o else.
Para evitar que esto suceda, algunos programadores prefieren escribir la llave inicial y la final de los bloques antes de
escribir las instrucciones individuales dentro de ellas.
Así como un bloque puede colocarse en cualquier parte en donde pueda colocarse una sola instrucción
individual, también es posible no tener instrucción alguna. En la sección 2.8 vimos que la instrucción vacía se
representa colocando un punto y coma (;) en donde normalmente iría una instrucción.
Error común de programación 4.2
Colocar un punto y coma después de la condición en una instrucción if...else produce un error lógico en las
instrucciones if de selección simple, y un error de sintaxis en las instrucciones if...else de selección doble (cuando
la parte del if contiene una instrucción en el cuerpo).
4.7 Instrucción de repetición while
Una instrucción de repetición (también llamada instrucción de ciclo, o un ciclo) permite al programador
especificar que un programa debe repetir una acción mientras cierta condición sea verdadera. La instrucción en
seudocódigo
Mientras existan más artículos en mi lista de compras
Comprar el siguiente artículo y quitarlo de mi lista
describe la repetición que ocurre durante una salida de compras. La condición “existan más artículos en mi lista de
compras” puede ser verdadera o falsa. Si es verdadera, entonces se realiza la acción “Comprar el siguiente artículo y
quitarlo de mi lista”. Esta acción se realizará en forma repetida mientras la condición sea verdadera. La instrucción
4.7 Instrucción de repetición while 121
122 Capítulo 4 Instrucciones de control: parte 1
(o instrucciones) contenida en la instrucción de repetición while constituye el cuerpo de esta estructura, el cual
puede ser una sola instrucción o un bloque. En algún momento, la condición será falsa (cuando el último artículo
de la lista de compras sea adquirido y eliminado de la lista). En este punto la repetición terminará y se ejecutará
la primera instrucción que esté después de la instrucción de repetición.
Como ejemplo de la instrucción de repetición while en Java, considere un segmento de programa diseñado
para encontrar la primera potencia de 3 que sea mayor a 100. Suponga que la variable producto de tipo int se
inicializa en 3. Cuando la siguiente instrucción while termine de ejecutarse, producto contendrá el resultado:
int producto = 3;
while ( producto <= 100 )
producto = 3 * producto;
Cuando esta instrucción while comienza a ejecutarse, el valor de la variable producto es 3. Cada iteración de la
instrucción while multiplica a producto por 3, por lo que producto toma los valores de 9, 27, 81 y 243, sucesi-
vamente. Cuando la variable producto se vuelve 243, la condición de la instrucción while (producto <= 1000)
se torna falsa. Esto termina la repetición, por lo que el valor final de producto es 243. En este punto, la ejecución
del programa continúa con la siguiente instrucción después de la instrucción while.
Error común de programación 4.3
Si no se proporciona, en el cuerpo de una instrucción while, una acción que ocasione que en algún momento la
condición de un while se torne falsa, por lo general, se producirá un error lógico conocido como ciclo infinito, en
el que el ciclo nunca terminará.
El diagrama de actividad de UML de la figura 4.4 muestra el flujo de control que corresponde a la instruc-
ción while anterior. Una vez más (aparte del estado inicial, las flechas de transición, un estado final y tres notas),
los símbolos en el diagrama representan un estado de acción y una decisión. Este diagrama también introduce
el símbolo de fusión. UML representa tanto al símbolo de fusión como al símbolo de decisión como rombos.
El símbolo de fusión une dos flujos de actividad en uno solo. En este diagrama, el símbolo de fusión une las
transiciones del estado inicial y del estado de acción, de manera que ambas fluyan en la decisión que determina si
el ciclo debe empezar a ejecutarse (o seguir ejecutándose). Los símbolos de decisión y de fusión pueden diferen-
ciarse por el número de flechas de transición “entrantes” y “salientes”. Un símbolo de decisión tiene una flecha de
transición que apunta hacia el rombo y dos o más flechas de transición que apuntan hacia fuera del rombo, para
indicar las posibles transiciones desde ese punto. Además, cada flecha de transición que apunta hacia fuera de un
símbolo de decisión tiene una condición de guardia junto a ella. Un símbolo de fusión tiene dos o más flechas
de transición que apuntan hacia el rombo, y sólo una flecha de transición que apunta hacia fuera del rombo,
para indicar múltiples flujos de actividad que se fusionan para continuar la actividad. Ninguna de las flechas de
transición asociadas con un símbolo de fusión tiene una condición de guardia.
triplicar valor de producto
Instrucción correspondiente en Java:
producto = 3 * producto;
Decisión
[producto <= 100]
[producto > 100]
Fusión
Figura 4.4 | Diagrama de actividad de UML de la instrucción de repetición while.
La figura 4.4 muestra claramente la repetición de la instrucción while que vimos antes en esta sección.
La flecha de transición que emerge del estado de acción apunta de regreso a la fusión, desde la cual el flujo del
programa regresa a la decisión que se evalúa al principio de cada iteración del ciclo. Éste ciclo sigue ejecutándose
hasta que la condición de guardia producto > 100 se vuelva verdadera. Entonces, la instrucción while termina
(llega a su estado final) y el control pasa a la siguiente instrucción en la secuencia del programa.
4.8 Cómo formular algoritmos: repetición controlada
por un contador
Para ilustrar la forma en que se desarrollan los algoritmos, modificamos la clase LibroCalificaciones del capítu-
lo 3, para resolver dos variantes de un problema que promedia las calificaciones de unos estudiantes. Analicemos
el siguiente enunciado del problema:
A una clase de diez estudiantes se les aplicó un examen. Las calificaciones (enteros en el rango de 0 a 100)
de este examen están disponibles para su análisis. Determine el promedio de la clase para este examen.
El promedio de la clase es igual a la suma de las calificaciones, dividida entre el número de estudiantes. El algoritmo
para resolver este problema en una computadora debe recibir como entrada cada una de las calificaciones, llevar el
registro del total de las calificaciones introducidas, realizar el cálculo para promediar e imprimir el resultado.
Algoritmo de seudocódigo con repetición controlada por un contador
Emplearemos seudocódigo para enlistar las acciones a ejecutar y especificar el orden en que deben ejecutarse. Usa-
remos una repetición controlada por contador para introducir las calificaciones, una por una. Esta técnica utiliza
una variable llamada contador (o variable de control) para controlar el número de veces que debe ejecutarse un
conjunto de instrucciones. A la repetición controlada por contador se le llama comúnmente repetición definida,
ya que el número de repeticiones se conoce antes de que el ciclo comience a ejecutarse. En este ejemplo, la repeti-
ción termina cuando el contador excede a 10. Esta sección presenta un algoritmo de seudocódigo (figura 4.5) com-
pletamente desarrollado, y una versión de la clase LibroCalificaciones (figura 4.6) que implementa el algoritmo
en un método de Java. Después presentamos una aplicación (figura 4.7) que demuestra el algoritmo en acción. En
la sección 4.9 demostraremos cómo utilizar el seudocódigo para desarrollar dicho algoritmo desde cero.
Observación de ingeniería de software 4.1
La experiencia ha demostrado que la parte más difícil para la resolución de un problema en una computadora es
desarrollar el algoritmo para la solución. Por lo general, una vez que se ha especificado el algoritmo correcto, el pro-
ceso de producir un programa funcional en Java a partir de dicho algoritmo es relativamente sencillo.
Observe las referencias en el algoritmo de la figura 4.5 para un total y un contador. Un total es una variable
que se utiliza para acumular la suma de varios valores. Un contador es una variable que se utiliza para contar; en este
caso, el contador de calificaciones indica cuál de las 10 calificaciones está a punto de escribir el usuario. Por lo gene-
ral, las variables que se utilizan para guardar totales deben inicializarse en cero antes de utilizarse en un programa.
Figura 4.5 | Algoritmo en seudocódigo que utiliza la repetición controlada por contador para resolver el problema del
promedio de una clase.
1 Asignar a total el valor de cero
2 Asignar al contador de calificaciones el valor de uno
3
4 Mientras que el contador de calificaciones sea menor o igual a diez
5 Pedir al usuario que introduzca la siguiente calificación
6 Obtener como entrada la siguiente calificación
7 Sumar la calificación al total
8 Sumar uno al contador de calificaciones
9
10 Asignar al promedio de la clase el total dividido entre diez
11 Imprimir el promedio de la clase
4.8 Cómo formular algoritmos: repetición controlada por un contador 123
124 Capítulo 4 Instrucciones de control: parte 1
Implementación de la repetición controlada por contador en la clase LibroCalificaciones
La clase LibroCalificaciones (figura 4.6) contiene un constructor (líneas 11-14) que asigna un valor a la varia-
ble de instancia nombreDelCurso (declarada en la línea 8) de la clase. Las líneas 17 a la 20, 23 a la 26 y 29 a la
34 declaran los métodos establecerNombreDelCurso, obtenerNombreDelCurso y mostrarMensaje, respecti-
vamente. Las líneas 37 a la 66 declaran el método determinarPromedioClase, el cual implementa el algoritmo
para sacar el promedio de la clase, descrito por el seudocódigo de la figura 4.5.
La línea 40 declara e inicializa la variable entrada de tipo Scanner, que se utiliza para leer los valores intro-
ducidos por el usuario. Las líneas 42 a 45 declaran las variables locales total, contadorCalif, calificacion y
promedio de tipo int. La variable calificacion almacena la entrada del usuario.
Observe que las declaraciones (en las líneas 42 a la 45) aparecen en el cuerpo del método determinar
PromedioClase. Recuerde que las variables declaradas en el cuerpo de un método son variables locales, y sólo
pueden utilizarse desde la línea de su declaración en el método, hasta la llave derecha de cierre (}) de la declaración
del método. La declaración de una variable local debe aparecer antes de que la variable se utilice en ese método.
Una variable local no puede utilizarse fuera del método en el que se declara.
En las versiones de la clase LibroCalificaciones en este capítulo, simplemente leemos y procesamos un
conjunto de calificaciones. El cálculo del promedio se realiza en el método determinarPromedioClase, usando
variables locales; no preservamos información acerca de las calificaciones de los estudiantes en variables de ins-
tancia de la clase. En versiones posteriores de la clase (en el capítulo 7, Arreglos), mantenemos las calificaciones
en memoria utilizando una variable de instancia que hace referencia a una estructura de datos conocida como
arreglo. Esto permite que un objeto LibroCalificaciones realice varios cálculos sobre el mismo conjunto de
calificaciones, sin requerir que el usuario escriba las calificaciones varias veces.
Buena práctica de programación 4.5
Separe las declaraciones de las otras instrucciones en los métodos con una línea en blanco, para mejorar la legibilidad.
Figura 4.6 | Repetición controlada por contador: Problema del promedio de una clase. (Parte 1 de 2).
1 // Fig. 4.6: LibroCalificaciones.java
2 // La clase LibroCalificaciones que resuelve el problema del promedio de
3 // la clase, usando la repetición controlada por un contador.
4 import java.util.Scanner; // el programa utiliza la clase Scanner
5
6 public class LibroCalificaciones
7 {
8 private String nombreDelCurso; // el nombre del curso que representa este
LibroCalificaciones
9
10 // el constructor inicializa a nombreDelCurso
11 public LibroCalificaciones( String nombre )
12 {
13 nombreDelCurso = nombre; // inicializa a nombreDelCurso
14 } // fin del constructor
15
16 // método para establecer el nombre del curso
17 public void establecerNombreDelCurso( String nombre )
18 {
19 nombreDelCurso = nombre; // almacena el nombre del curso
20 } // fin del método establecerNombreDelCurso
21
22 // método para obtener el nombre del curso
23 public String obtenerNombreDelCurso()
24 {
25 return nombreDelCurso;
26 } // fin del método obtenerNombreDelCurso
27
28 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones
Las asignaciones (en las líneas 48 y 49) inicializan total a 0 y contadorCalif a 1. Observe que estas ini-
cializaciones ocurren antes que se utilicen las variables en los cálculos. Las variables calificacion y promedio
(para la entrada del usuario y el promedio calculado, respectivamente) no necesitan inicializarse aquí; sus valores
se asignarán a medida que se introduzcan o calculen más adelante en el método.
Error común de programación 4.4
Leer el valor de una variable local antes de inicializarla produce un error de compilación. Todas las variables locales
deben inicializarse antes de leer sus valores en las expresiones.
Tip para prevenir errores 4.1
Inicialice cada contador y total, ya sea en su declaración o en una instrucción de asignación. Por lo general, los
totales se inicializan a 0. Los contadores comúnmente se inicializan a 0 o a 1, dependiendo de cómo se utilicen (más
adelante veremos ejemplos de cuándo usar 0 y cuándo usar 1).
Figura 4.6 | Repetición controlada por contador: Problema del promedio de una clase. (Parte 2 de 2).
29 public void mostrarMensaje()
30 {
31 // obtenerNombreDelCurso obtiene el nombre del curso
32 System.out.printf( "Bienvenido al libro de calificaciones paran%s!nn",
33 obtenerNombreDelCurso() );
34 } // fin del método mostrarMensaje
35
36 // determina el promedio de la clase, con base en las 10 calificaciones introducidas
por el usuario
37 public void determinarPromedioClase()
38 {
39 // crea objeto Scanner para obtener la entrada de la ventana de comandos
40 Scanner entrada = new Scanner( System.in );
41
42 int total; // suma de las calificaciones escritas por el usuario
43 int contadorCalif; // número de la siguiente calificación a introducir
44 int calificacion; // valor de la calificación escrita por el usuario
45 int promedio; // el promedio de las calificaciones
46
47 // fase de inicialización
48 total = 0; // inicializa el total
49 contadorCalif = 1; // inicializa el contador del ciclo
50
51 // fase de procesamiento
52 while ( contadorCalif <= 10 ) // itera 10 veces
53 {
54 System.out.print( "Escriba la calificación: " ); // indicador
55 calificacion = entrada.nextInt(); // lee calificación del usuario
56 total = total + calificacion; // suma calificación a total
57 contadorCalif = contadorCalif + 1; // incrementa contador en 1
58 } // fin de while
59
60 // fase de terminación
61 promedio = total / 10; // la división entera produce un resultado entero
62
63 // muestra el total y el promedio de las calificaciones
64 System.out.printf( "nEl total de las 10 calificaciones es %dn", total );
65 System.out.printf( "El promedio de la clase es %dn", promedio );
66 } // fin del método determinarPromedioClase
67
68 } // fin de la clase LibroCalificaciones
4.8 Cómo formular algoritmos: repetición controlada por un contador 125
126 Capítulo 4 Instrucciones de control: parte 1
La línea 52 indica que la instrucción while debe continuar ejecutando el ciclo (lo que también se conoce
como iterar), siempre y cuando el valor de contadorCalif sea menor o igual a 10. Mientras esta condición sea
verdadera, la instrucción while ejecutará en forma repetida las instrucciones entre las llaves que delimitan su
cuerpo (líneas 54 a la 57).
La línea 54 muestra el indicador "Escriba la calificacion: ". La línea 55 lee el dato escrito por el usua-
rio y lo asigna a la variable calificacion. Después, la línea 56 suma la nueva calificación escrita por el usuario al
total, y asigna el resultado a total, que sustituye su valor anterior.
La línea 57 suma 1 a contadorCalif para indicar que el programa ha procesado una calificación y está
listo para recibir la siguiente calificación del usuario. Al incrementar a contadorCalif en cada iteración, en un
momento dado su valor excederá a 10. En ese momento, el ciclo while termina debido a que su condición (línea
52) se vuelve falsa.
Cuando el ciclo termina, la línea 61 realiza el cálculo del promedio y asigna su resultado a la variable prome-
dio. La línea 64 utiliza el método printf de System.out para mostrar el texto "El total de las 10 cali-
ficaciones es ", seguido del valor de la variable total. Después, la línea 65 utiliza a printf para mostrar el
texto "El promedio de la clase es ", seguido del valor de la variable promedio. Después de llegar a la línea
66, el método determinarPromedioClase devuelve el control al método que hizo la llamada (es decir, a main en
PruebaLibroCalificaciones de la figura 4.7).
La clase PruebaLibroCalificaciones
La clase PruebaLibroCalificaciones (figura 4.7) crea un objeto de la clase LibroCalificaciones (figura 4.6)
y demuestra sus capacidades. Las líneas 10 y 11 de la figura 4.7 crean un nuevo objeto LibroCalificacio-
nes y lo asignan a la variable miLibroCalificaciones. El objeto String en la línea 11 se pasa al constructor
de LibroCalificaciones (líneas 11 a la 14 de la figura 4.6). La línea 13 llama al método mostrarMensaje de
miLibroCalificaciones para mostrar un mensaje de bienvenida al usuario. Después, la línea 14 llama al método
determinarPromedioClase de miLibroCalificaciones para permitir que el usuario introduzca 10 calificacio-
nes, para las cuales el método posteriormente calcula e imprime el promedio; el método ejecuta el algoritmo que
se muestra en la figura 4.5.
Observaciones acerca de la división de enteros y el truncamiento
El cálculo del promedio realizado por el método determinarPromedioClase, en respuesta a la llamada al méto-
do en la línea 14 de la figura 4.7, produce un resultado entero. La salida del programa indica que la suma de los
valores de las calificaciones en la ejecución de ejemplo es 846, que al dividirse entre 10, debe producir el número
de punto flotante 84.6. Sin embargo, el resultado del cálculo total / 10 (línea 61 de la figura 4.6) es el entero
1 // Fig. 4.7: PruebaLibroCalificaciones.java
2 // Crea un objeto LibroCalificaciones e invoca a su método obtenerPromedioClase.
3
4 public class PruebaLibroCalificaciones
5 {
6 public static void main( String args[] )
7 {
8 // crea objeto miLibroCalificaciones de la clase LibroCalificaciones y
9 // pasa el nombre del curso al constructor
10 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones(
11 "CS101 Introducción a la programación en Java" );
12
13 miLibroCalificaciones.mostrarMensaje(); // muestra mensaje de bienvenida
14 miLibroCalificaciones.determinarPromedioClase(); // encuentra el promedio de 10
calificaciones
15 } // fin de main
16
17 } // fin de la clase PruebaLibroCalificaciones
Figura 4.7 | La clase PruebaLibroCalificaciones crea un objeto de la clase LibroCalificaciones (figura 4.6) e
invoca a su método determinarPromedioClase. (Parte 1 de 2).
84, ya que total y 10 son enteros. Al dividir dos enteros se produce una división entera: se pierde cualquier
parte fraccionaria del cálculo (es decir, se trunca). En la siguiente sección veremos cómo obtener un resultado de
punto flotante a partir del cálculo del promedio.
Error común de programación 4.5
Suponer que la división entera redondea (en vez de truncar) puede producir resultados erróneos. Por ejemplo, 7 ÷ 4,
que produce 1.75 en la aritmética convencional, se trunca a 1 en la aritmética entera, en vez de redondearse a 2.
4.9 Cómo formular algoritmos:
repetición controlada por un centinela
Generalicemos el problema, de la sección 4.8, para los promedios de una clase. Considere el siguiente problema:
Desarrollar un programa que calcule el promedio de una clase y procese las calificaciones para un número
arbitrario de estudiantes cada vez que se ejecute.
En el ejemplo anterior del promedio de una clase, el enunciado del problema especificó el número de estudiantes
(10). En este ejemplo no se indica cuántas calificaciones introducirá el usuario durante la ejecución del programa.
El programa debe procesar un número arbitrario de calificaciones. ¿Cómo puede el programa determinar cuándo
terminar de introducir calificaciones? ¿Cómo sabrá cuándo calcular e imprimir el promedio de la clase?
Una manera de resolver este problema es utilizar un valor especial denominado valor centinela (también
llamado valor de señal, valor de prueba o valor de bandera) para indicar el “fin de la introducción de datos”.
El usuario escribe calificaciones hasta que se haya introducido el número correcto de ellas. Después, el usuario
escribe el valor centinela para indicar que no se van a introducir más calificaciones. A la repetición controlada por
centinela a menudo se le llama repetición indefinida, ya que el número de repeticiones no se conoce antes de
que comience la ejecución del ciclo.
Evidentemente, debe elegirse un valor centinela de tal forma que no pueda confundirse con un valor de
entrada permitido. Las calificaciones de un examen son enteros positivos, por lo que –1 es un valor centinela
aceptable para este problema. Por lo tanto, una ejecución del programa para promediar una clase podría procesar
una cadena de entradas como 95, 96, 75, 74, 89 y –1. El programa entonces calcularía e imprimiría el prome-
dio de la clase para las calificaciones 95, 96, 75, 74 y 89; como –1 es el valor centinela, no debe entrar en el cálculo
del promedio.
Error común de programación 4.6
Seleccionar un valor centinela que sea también un valor de datos permitido es un error lógico.
Figura 4.7 | La clase PruebaLibroCalificaciones crea un objeto de la clase LibroCalificaciones (figura 4.6) e
invoca a su método determinarPromedioClase. (Parte 2 de 2).
Bienvenido al libro de calificaciones para
CS101 Introduccion a la programacion en Java!
Escriba la calificacion: 67
Escriba la calificacion: 78
Escriba la calificacion: 89
Escriba la calificacion: 67
Escriba la calificacion: 87
Escriba la calificacion: 98
Escriba la calificacion: 93
Escriba la calificacion: 85
Escriba la calificacion: 82
Escriba la calificacion: 100
El total de las 10 calificaciones es 846
El promedio de la clase es 84
4.9 Cómo formular algoritmos: repetición controlada por un centinela 127
128 Capítulo 4 Instrucciones de control: parte 1
Desarrollo del algoritmo en seudocódigo con el método de refinamiento de arriba a abajo, paso a paso:
el primer refinamiento (cima)
Desarrollamos el programa para promediar clases con una técnica llamada refinamiento de arriba a abajo, paso
a paso, la cual es esencial para el desarrollo de programas bien estructurados. Comenzamos con una representa-
ción en seudocódigo de la cima, una sola instrucción que transmite la función del programa en general:
Determinar el promedio de la clase para el examen
La cima es, en efecto, la representación completa de un programa. Desafortunadamente, la cima pocas veces trans-
mite los detalles suficientes como para escribir un programa en Java. Por lo tanto, ahora comenzaremos el proceso
de refinamiento. Dividiremos la cima en una serie de tareas más pequeñas y las enlistaremos en el orden en el que
se van a realizar. Esto arroja como resultado el siguiente primer refinamiento:
Inicializar variables
Introducir, sumar y contar las calificaciones del examen
Calcular e imprimir el promedio de la clase
Esta mejora utiliza sólo la estructura de secuencia; los pasos aquí mostrados deben ejecutarse en orden, uno des-
pués del otro.
Observación de ingeniería de software 4.2
Cada mejora, así como la cima en sí, es una especificación completa del algoritmo; sólo varía el nivel del detalle.
Observación de ingeniería de software 4.3
Muchos programas pueden dividirse lógicamente en tres fases: de inicialización, en donde se inicializan las variables;
procesamiento, en donde se introducen los valores de los datos y se ajustan las variables del programa (como contado-
res y totales) según sea necesario; y una fase de terminación, que calcula y produce los resultados finales.
Cómo proceder al segundo refinamiento
La anterior Observación de ingeniería de software es a menudo todo lo que usted necesita para el primer refi-
namiento en el proceso de arriba a abajo. Para avanzar al siguiente nivel de refinamiento, es decir, el segundo
refinamiento, nos comprometemos a usar variables específicas. En este ejemplo necesitamos el total actual de los
números, una cuenta de cuántos números se han procesado, una variable para recibir el valor de cada calificación,
a medida que el usuario las vaya introduciendo, y una variable para almacenar el promedio calculado. La instruc-
ción en seudocódigo
Inicializar las variables
puede mejorarse como sigue:
Inicializar total en cero
Inicializar contador en cero
Sólo las variables total y contador necesitan inicializase antes de que puedan utilizarse. Las variables promedio y
calificacion (para el promedio calculado y la entrada del usuario, respectivamente) no necesitan inicializarse, ya
que sus valores se reemplazarán a medida que se calculen o introduzcan.
La instrucción en seudocódigo
Introducir, sumar y contar las calificaciones del examen
requiere una estructura de repetición (es decir, un ciclo) que introduzca cada calificación en forma sucesiva. No
sabemos de antemano cuántas calificaciones van a procesarse, por lo que utilizaremos la repetición controlada por
centinela. El usuario introduce las calificaciones una por una; después de introducir la última calificación, intro-
duce el valor centinela. El programa evalúa el valor centinela después de la introducción de cada calificación, y ter-
mina el ciclo cuando el usuario introduce el valor centinela. Entonces, la segunda mejora de la instrucción anterior
en seudocódigo sería
Pedir al usuario que introduzca la primera calificación
Recibir como entrada la primera calificación (puede ser el centinela)
Mientras el usuario no haya introducido aún el centinela
Sumar esta calificación al total actual
Sumar uno al contador de calificaciones
Pedir al usuario que introduzca la siguiente calificación
Recibir como entrada la siguiente calificación (puede ser el centinela)
En seudocódigo no utilizamos llaves alrededor de las instrucciones que forman el cuerpo de la estructura Mientras.
Simplemente aplicamos sangría a las instrucciones bajo el Mientras para mostrar que pertenecen a esta instruc-
ción. De nuevo, el seudocódigo es solamente una herramienta informal para desarrollar programas.
La instrucción en seudocódigo
Calcular e imprimir el promedio de la clase
puede mejorarse de la siguiente manera:
Si el contador no es igual a cero
Asignar al promedio el total dividido entre el contador
Imprimir el promedio
De lo contrario
Imprimir “No se introdujeron calificaciones”
Aquí tenemos cuidado de evaluar la posibilidad de una división entre cero; por lo general, esto es un error lógico
que, si no se detecta, haría que el programa fallara o produjera resultados inválidos. El segundo refinamiento
completo del seudocódigo para el problema del promedio de una clase se muestra en la figura 4.8.
Tip para prevenir errores 4.2
Al realizar una división entre una expresión cuyo valor pudiera ser cero, debe evaluar explícitamente esta posibilidad
y manejarla de manera apropiada en su programa (como imprimir un mensaje de error), en vez de permitir que
ocurra el error.
En las figuras 4.5 y 4.8 incluimos algunas líneas en blanco y sangría en el seudocódigo para facilitar su lec-
tura. Las líneas en blanco separan los algoritmos en seudocódigo en sus diversas fases y accionan las instrucciones
de control; la sangría enfatiza los cuerpos de las estructuras de control.
1 Inicializar total en cero
2 Inicializar contador en cero
3
4 Pedir al usuario que introduzca la primera calificación
5 Recibir como entrada la primera calificación (puede ser el centinela)
6
7 Mientras el usuario no haya introducido aún el centinela
8 Sumar esta calificación al total actual
9 Sumar uno al contador de calificaciones
10 Pedir al usuario que introduzca la siguiente calificación
11 Recibir como entrada la siguiente calificación (puede ser el centinela)
12
13 Si el contador no es igual a cero
14 Asignar al promedio el total dividido entre el contador
15 Imprimir el promedio
16 De lo contrario
17 Imprimir “No se introdujeron calificaciones”
Figura 4.8 | Algoritmo en seudocódigo del problema para promediar una clase, con una repetición controlada por
centinela.
4.9 Cómo formular algoritmos: repetición controlada por un centinela 129
130 Capítulo 4 Instrucciones de control: parte 1
El algoritmo en seudocódigo en la figura 4.8 resuelve el problema más general para promediar una clase. Este
algoritmo se desarrolló después de aplicar dos niveles de refinamiento. En ocasiones se requieren más niveles de
refinamiento.
Observación de ingeniería de software 4.4
Termine el proceso de refinamiento de arriba a abajo, paso a paso, cuando haya especificado el algoritmo en seudo-
código con el detalle suficiente como para poder convertir el seudocódigo en Java. Por lo general, la implementación
del programa en Java después de esto es mucho más sencilla.
Observación de ingeniería de software 4.5
Algunos programadores experimentados escriben programas sin utilizar herramientas de desarrollo de programas
como el seudocódigo. Estos programadores sienten que su meta final es resolver el problema en una computadora y
que el escribir seudocódigo simplemente retarda la producción de los resultados finales. Aunque este método pudiera
funcionar para problemas sencillos y conocidos, tiende a ocasionar graves errores y retrasos en proyectos grandes y
complejos.
Implementación de la repetición controlada por centinela en la clase LibroCalificaciones
La figura 4.9 muestra la clase de Java LibroCalificaciones que contiene el método determinarPromedio-
Clase, el cual implementa el algoritmo, de la figura 4.8, en seudocódigo. Aunque cada calificación es un valor
entero, existe la probabilidad de que el cálculo del promedio produzca un número con un punto decimal; en otras
palabras, un número real (es decir, de punto flotante). El tipo int no puede representar un número de este tipo,
por lo que esta clase utiliza el tipo double para ello.
En este ejemplo vemos que las estructuras de control pueden apilarse una encima de otra (en secuencia), al
igual que un niño apila bloques de construcción. La instrucción while (líneas 57 a 65) va seguida por una ins-
trucción if...else (líneas 69 a 80) en secuencia. La mayor parte del código en este programa es igual al código
de la figura 4.6, por lo que nos concentraremos en los nuevos conceptos.
La línea 45 declara la variable promedio de tipo double, la cual nos permite guardar el promedio de la clase
como un número de punto flotante. La línea 49 inicializa contadorCalif en 0, ya que todavía no se han intro-
ducido calificaciones. Recuerde que este programa utiliza la repetición controlada por centinela para recibir las
calificaciones que escribe el usuario. Para mantener un registro preciso del número de calificaciones introducidas,
el programa incrementa contadorCalif sólo cuando el usuario introduce un valor permitido para la califica-
ción.
1 // Fig. 4.9: LibroCalificaciones.java
2 // La clase LibroCalificaciones resuelve el problema del promedio de la clase
3 // usando la repetición controlada por un centinela.
4 import java.util.Scanner; // el programa usa la clase Scanner
5
6 public class LibroCalificaciones
7 {
8 private String nombreDelCurso; // el nombre del curso que representa este
LibroCalificaciones
9
10 // el constructor inicializa a nombreDelCurso
11 public LibroCalificaciones( String nombre )
12 {
13 nombreDelCurso = nombre; // inicializa a nombreDelCurso
14 } // fin del constructor
15
16 // método para establecer el nombre del curso
17 public void establecerNombreDelCurso( String nombre )
18 {
Figura 4.9 | Repetición controlada por centinela: problema del promedio de una clase. (Parte 1 de 3).
Figura 4.9 | Repetición controlada por centinela: problema del promedio de una clase. (Parte 2 de 3).
19 nombreDelCurso = nombre; // almacena el nombre del curso
20 } // fin del método establecerNombreDelCurso
21
22 // método para obtener el nombre del curso
23 public String obtenerNombreDelCurso()
24 {
25 return nombreDelCurso;
26 } // fin del método obtenerNombreDelCurso
27
28 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones
29 public void mostrarMensaje()
30 {
31 // obtenerNombreDelCurso obtiene el nombre del curso
32 System.out.printf( "Bienvenido al libro de calificaciones paran%s!nn",
33 obtenerNombreDelCurso() );
34 } // fin del método mostrarMensaje
35
36 // determina el promedio de un número arbitrario de calificaciones
37 public void determinarPromedioClase()
38 {
39 // crea objeto Scanner para obtener la entrada de la ventana de comandos
40 Scanner entrada = new Scanner( System.in );
41
42 int total; // suma de las calificaciones
43 int contadorCalif; // número de calificaciones introducidas
44 int calificacion; // valor de calificación
45 double promedio; // número con punto decimal para el promedio
46
47 // fase de inicialización
48 total = 0; // inicializa el total
49 contadorCalif = 0; // inicializa el contador del ciclo
50
51 // fase de procesamiento
52 // pide entrada y lee calificación del usuario
53 System.out.print( "Escriba calificacion o -1 para terminar: " );
54 calificacion = entrada.nextInt();
55
56 // itera hasta leer el valor centinela del usuario
57 while ( calificacion != -1 )
58 {
59 total = total + calificacion; // suma calificacion al total
60 contadorCalif = contadorCalif + 1; // incrementa el contador
61
62 // pide entrada y lee siguiente calificación del usuario
63 System.out.print( "Escriba calificacion o -1 para terminar: " );
64 calificacion = entrada.nextInt();
65 } // fin de while
66
67 // fase de terminación
68 // si el usuario introdujo por lo menos una calificación...
69 if ( contadorCalif != 0 )
70 {
71 // calcula el promedio de todas las calificaciones introducidas
72 promedio = (double) total / contadorCalif;
73
74 // muestra el total y el promedio (con dos dígitos de precisión)
75 System.out.printf( "nEl total de las %d calificaciones introducidas es %dn",
76 contadorCalif, total );
77 System.out.printf( "El promedio de la clase es %.2fn", promedio );
4.9 Cómo formular algoritmos: repetición controlada por un centinela 131
132 Capítulo 4 Instrucciones de control: parte 1
Figura 4.9 | Repetición controlada por centinela: problema del promedio de una clase. (Parte 3 de 3).
Comparación entre la lógica del programa para la repetición controlada por centinela, y la repetición
controlada por contador
Compare la lógica de esta aplicación para la repetición controlada por centinela con la repetición controlada por
contador en la figura 4.6. En la repetición controlada por contador, cada iteración de la instrucción while (líneas
52 a 58 de la figura 4.6) lee un valor del usuario, para el número especificado de iteraciones. En la repetición
controlada por centinela, el programa lee el primer valor (líneas 53 y 54 de la figura 4.9) antes de llegar al whi-
le. Este valor determina si el flujo de control del programa debe entrar al cuerpo del while. Si la condición del
while es falsa, el usuario introdujo el valor centinela, por lo que el cuerpo del while no se ejecuta (es decir, no
se introdujeron calificaciones). Si, por otro lado, la condición es verdadera, el cuerpo comienza a ejecutarse y el
ciclo suma el valor de calificacion al total (línea 59). Después, las líneas 63 y 64 en el cuerpo del ciclo reciben
el siguiente valor escrito por el usuario. A continuación, el control del programa se acerca a la llave derecha de
terminación (}) del cuerpo del ciclo en la línea 65, por lo que la ejecución continúa con la evaluación de la con-
dición del while (línea 57). La condición utiliza el valor más reciente de calificacion que acaba de introducir
el usuario, para determinar si el cuerpo de la instrucción while debe ejecutarse otra vez. Observe que el valor
de la variable calificacion siempre lo introduce el usuario inmediatamente antes de que el programa evalúe la
condición del while. Esto permite al programa determinar si el valor que acaba de introducir el usuario es el valor
centinela, antes de que el programa procese ese valor (es decir, que lo sume al total). Si el valor introducido es el
valor centinela, el ciclo termina y el programa no suma –1 al total.
Buena práctica de programación 4.6
En un ciclo controlado por centinela, los indicadores que solicitan la introducción de datos deben recordar explícita-
mente al usuario el valor que representa al centinela.
Una vez que termina el ciclo se ejecuta la instrucción if...else en las líneas 69 a 80. La condición en la
línea 69 determina si se introdujeron calificaciones o no. Si no se introdujo ninguna, se ejecuta la parte del else
(líneas 79 y 80) de la instrucción if...else y muestra el mensaje "No se introdujeron calificaciones", y
el método devuelve el control al método que lo llamó.
Observe el bloque de la instrucción while en la figura 4.9 (líneas 58 a 65). Sin las llaves, el ciclo considera-
ría que su cuerpo sólo consiste en la primera instrucción, que suma la calificacion al total. Las últimas tres
instrucciones en el bloque quedarían fuera del cuerpo del ciclo, ocasionando que la computadora interpretara el
código incorrectamente, como se muestra a continuación:
while ( calificacion != -1 )
total = total + calificacion; // suma calificación al total
contadorCalif = contadorCalif + 1; // incrementa el contador
// obtiene como entrada la siguiente calificación del usuario
System.out.print( "Escriba calificacion o –1 para terminar: " );
calificacion = entrada.nextInt();
El código anterior ocasionaría un ciclo infinito en el programa, si el usuario no introduce el centinela –1 como
valor de entrada en la línea 54 (antes de la instrucción while).
78 } // fin de if
79 else // no se introdujeron calificaciones, por lo que se imprime el mensaje
apropiado
80 System.out.println( "No se introdujeron calificaciones" );
81 } // fin del método determinarPromedioClase
82
83 } // fin de la clase LibroCalificaciones
Error común de programación 4.7
Omitir las llaves que delimitan a un bloque puede provocar errores lógicos, como ciclos infinitos. Para prevenir este
problema, algunos programadores encierran el cuerpo de todas las instrucciones de control con llaves, aun si el cuerpo
sólo contiene una instrucción.
Conversión explícita e implícita entre los tipos primitivos
Si se introdujo por lo menos una calificación, la línea 72 de la figura 4.9 calcula el promedio de las calificaciones.
En la figura 4.6 vimos que la división entera produce un resultado entero. Aun y cuando la variable promedio se
declara como double (línea 45), el cálculo
promedio = total / contadorCalif;
descarta la parte fraccionaria del cociente antes de asignar el resultado de la división a promedio. Esto ocurre de-
bido a que total y contadorCalif son enteros, y la división entera produce un resultado entero. Para realizar
un cálculo de punto flotante con valores enteros, debemos tratar temporalmente a estos valores como números
de punto flotante, para usarlos en el cálculo. Java cuenta con el operador unario de conversión de tipo para
llevar a cabo esta tarea. La línea 72 utiliza el operador de conversión de tipo (double) (un operador unario)
para crear una copia de punto flotante temporal de su operando total (que aparece a la derecha del operador).
Utilizar un operador de conversión de tipo de esta forma es un proceso que se denomina conversión explícita.
El valor almacenado en total sigue siendo un entero.
El cálculo ahora consiste de un valor de punto flotante (la versión temporal double de total) dividido
entre el entero contadorCalif. Java sabe cómo evaluar sólo expresiones aritméticas en las que los tipos de los
operandos sean idénticos. Para asegurar que los operandos sean del mismo tipo, Java realiza una operación lla-
mada promoción (o conversión implícita) en los operandos seleccionados. Por ejemplo, en una expresión que
contenga valores de los tipos int y double, los valores int son promovidos a valores double para utilizarlos en la
expresión. En este ejemplo, Java promueve el valor de contadorCalif al tipo double, después el programa realiza
la división de punto flotante y asigna el resultado del cálculo a promedio. Mientras que se aplique el operador de
conversión de tipo (double) a cualquier variable en el cálculo, éste producirá un resultado double. Más adelante
en el capítulo, hablaremos sobre todos los tipos primitivos. En la sección 6.7 aprenderá más acerca de las reglas
de promoción.
Error común de programación 4.8
El operador de conversión de tipo puede utilizarse para convertir entre los tipos numéricos primitivos, como int y
double, y para convertir entre los tipos de referencia relacionados (como lo describiremos en el capítulo 10, Progra-
mación orientada a objetos: polimorfismo). La conversión al tipo incorrecto puede ocasionar errores de compilación
o errores en tiempo de ejecución.
Los operadores de conversión de tipo están disponibles para cualquier tipo. El operador de conversión se
forma colocando paréntesis alrededor del nombre de un tipo. Este operador es un operador unario (es decir,
un operador que utiliza sólo un operando). En el capítulo 2 estudiamos los operadores aritméticos binarios. Java
también soporta las versiones unarias de los operadores de suma (+) y resta (-), por lo que el programador puede
escribir expresiones como –7 o +5. Los operadores de conversión de tipo se asocian de derecha a izquierda y tienen
la misma precedencia que los demás operadores unarios, como + y –. Esta precedencia es un nivel mayor que la de
los operadores de multiplicación *, / y %. (Consulte la tabla de precedencia de operadores en el apéndice A). En
nuestras tablas de precedencia, indicamos el operador de conversión de tipos con la notación (tipo) para indicar
que puede usarse cualquier nombre de tipo para formar un operador de conversión de tipo.
La línea 77 imprime el promedio de la clase, usando el método printf de System.out. En este ejemplo
mostramos el promedio de la clase redondeado a la centésima más cercana. El especificador de formato %.2f en
la cadena de control de formato de printf (línea 77) indica que el valor de la variable promedio debe mostrarse
con dos dígitos de precisión a la derecha del punto decimal; esto se indica mediante el .2 en el especificador de
formato. Las tres calificaciones introducidas durante la ejecución de ejemplo de la clase PruebaLibroCalifica-
ciones (figura 4.10) dan un total de 257, que produce el promedio de 85.666666…. El método printf utiliza
la precisión en el especificador de formato para redondear el valor al número especificado de dígitos. En este
programa, el promedio se redondea a la posición de las centésimas y se muestra como 85.67.
4.9 Cómo formular algoritmos: repetición controlada por un centinela 133
134 Capítulo 4 Instrucciones de control: parte 1
4.10 Cómo formular algoritmos: instrucciones de control anidadas
En el siguiente ejemplo formularemos una vez más un algoritmo utilizando seudocódigo y el refinamiento de
arriba a abajo, paso a paso, y después escribiremos el correspondiente programa en Java. Hemos visto que las ins-
trucciones de control pueden apilarse una encima de otra (en secuencia). En este ejemplo práctico examinaremos
la otra forma en la que pueden conectarse las instrucciones de control, a saber, mediante el anidamiento de una
instrucción de control dentro de otra.
Considere el siguiente enunciado de un problema:
Una universidad ofrece un curso que prepara a los estudiantes para el examen estatal de certificación del estado como corre-
dores de bienes raíces. El año pasado, diez de los estudiantes que completaron este curso tomaron el examen. La universidad
desea saber qué tan bien se desempeñaron sus estudiantes en el examen. A usted se le ha pedido que escriba un programa
para sintetizar los resultados. Se le dio una lista de estos 10 estudiantes. Junto a cada nombre hay un 1 escrito, si el estu-
diante aprobó el examen, o un 2 si lo reprobó.
Su programa debe analizar los resultados del examen de la siguiente manera:
1. Introducir cada resultado de la prueba (es decir, un 1 o un 2). Mostrar el mensaje “Escriba el resultado” en la pan-
talla, cada vez que el programa solicite otro resultado de la prueba.
2. Contar el número de resultados de la prueba, de cada tipo.
3. Mostrar un resumen de los resultados de la prueba, indicando el número de estudiantes que aprobaron y el número
de estudiantes que reprobaron.
4. Si más de ocho estudiantes aprobaron el examen, imprimir el mensaje “Aumentar la colegiatura”.
1 // Fig. 4.10: PruebaLibroCalificaciones.java
2 // Crea un objeto LibroCalificaciones e invoca a su método determinarPromedioClase.
3
4 public class PruebaLibroCalificaciones
5 {
6 public static void main( String args[] )
7 {
8 // crea objeto miLibroCalificaciones de LibroCalificaciones y
9 // pasa el nombre del curso al constructor
10 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones(
11 "CS101 Introduccion a la programacion en Java" );
12
13 miLibroCalificaciones.mostrarMensaje(); // muestra mensaje de bienvenida
14 miLibroCalificaciones.determinarPromedioClase(); // encuentra el promedio de las
calificaciones
15 } // fin de main
16
17 } // fin de la clase PruebaLibroCalificaciones
Figura 4.10 | La clase PruebaLibroCalificaciones crea un objeto de la clase LibroCalificaciones (figura 4.9) e
invoca al método determinarPromedioClase.
Bienvenido al libro de calificaciones para
CS101 Introduccion a la programacion en Java!
Escriba calificacion o -1 para terminar: 97
Escriba calificacion o -1 para terminar: 88
Escriba calificacion o -1 para terminar: 72
Escriba calificacion o -1 para terminar: -1
El total de las 3 calificaciones introducidas es 257
El promedio de la clase es 85.67
Después de leer el enunciado del programa cuidadosamente, hacemos las siguientes observaciones:
1. El programa debe procesar los resultados de la prueba para 10 estudiantes. Puede usarse un ciclo contro-
lado por contador, ya que el número de resultados de la prueba se conoce de antemano.
2. Cada resultado de la prueba tiene un valor numérico, ya sea 1 o 2. Cada vez que el programa lee un
resultado de la prueba, debe determinar si el número es 1 o 2. Nosotros evaluamos un 1 en nuestro
algoritmo. Si el número no es 1, suponemos que es un 2. (El ejercicio 4.24 considera las consecuencias
de esta suposición).
3. Dos contadores se utilizan para llevar el registro de los resultados del examen: uno para contar el número
de estudiantes que aprobaron el examen y uno para contar el número de estudiantes que reprobaron el
examen.
4. Una vez que el programa ha procesado todos los resultados, debe decidir si más de ocho estudiantes
aprobaron el examen.
Veamos ahora el refinamiento de arriba a abajo, paso a paso. Comencemos con la representación del seudo-
código de la cima:
Analizar los resultados del examen y decidir si debe aumentarse la colegiatura o no.
Una vez más, la cima es una representación completa del programa, pero es probable que se necesiten varios refi-
namientos antes de que el seudocódigo pueda evolucionar de manera natural en un programa en Java.
Nuestro primer refinamiento es
Inicializar variables
Introducir las 10 calificaciones del examen y contar los aprobados y reprobados
Imprimir un resumen de los resultados del examen y decidir si debe aumentarse la colegiatura
Aquí también, aun cuando tenemos una representación completa del programa, es necesario refinarla. Ahora nos
comprometemos con variables específicas. Se necesitan contadores para registrar los aprobados y reprobados; uti-
lizaremos un contador para controlar el proceso de los ciclos y necesitaremos una variable para guardar la entrada
del usuario. La variable en la que se almacenará la entrada del usuario no se inicializa al principio del algoritmo,
ya que su valor proviene del usuario durante cada iteración del ciclo.
La instrucción en seudocódigo
Inicializar variables
puede mejorarse de la siguiente manera:
Inicializar aprobados en cero
Inicializar reprobados en cero
Inicializar contador de estudiantes en cero
Observe que sólo se inicializan los contadores al principio del algoritmo.
La instrucción en seudocódigo
Introducir las 10 calificaciones del examen, y contar los aprobados y reprobados
requiere un ciclo en el que se introduzca sucesivamente el resultado de cada examen. Sabemos de antemano que
hay precisamente 10 resultados del examen, por lo que es apropiado utilizar un ciclo controlado por contador.
Dentro del ciclo (es decir, anidado dentro del ciclo), una estructura de selección doble determinará si cada
resultado del examen es aprobado o reprobado, e incrementará el contador apropiado. Entonces, la mejora al
seudocódigo anterior es
Mientras el contador de estudiantes sea menor o igual a 10
Pedir al usuario que introduzca el siguiente resultado del examen
Recibir como entrada el siguiente resultado del examen
Si el estudiante aprobó
Sumar uno a aprobados
4.10 Cómo formular algoritmos: instrucciones de control anidadas 135
136 Capítulo 4 Instrucciones de control: parte 1
De lo contrario
Sumar uno a reprobados
Sumar uno al contador de estudiantes
Nosotros utilizamos líneas en blanco para aislar la estructura de control Si...De lo contrario, lo cual mejora la
legibilidad.
La instrucción en seudocódigo
Imprimir un resumen de los resultados de los exámenes y decidir si debe aumentarse la colegiatura
puede mejorarse de la siguiente manera:
Imprimir el número de aprobados
Imprimir el número de reprobados
Si más de ocho estudiantes aprobaron
Imprimir “Aumentar la colegiatura”
Segundo refinamiento completo en seudocódigo y conversión a la clase Analisis
El segundo refinamiento completo aparece en la figura 4.11. Observe que también se utilizan líneas en blanco
para separar la estructura Mientras y mejorar la legibilidad del programa. Este seudocódigo está ahora lo suficien-
temente mejorado para su conversión a Java. La clase de Java que implementa el algoritmo en seudocódigo se
muestra en la figura 4.12, y en la figura 4.13 aparecen dos ejecuciones de ejemplo.
Las líneas 13 a 16 de la figura 4.12 declaran las variables que utiliza el método procesarResultadosExamen
de la clase Analisis para procesar los resultados del examen. Varias de estas declaraciones utilizan la habilidad de
Java para incorporar la inicialización de variables en las declaraciones (a aprobados se le asigna 0, a reprobados
se le asigna 0 y a contadorEstudiantes se le asigna 1). Los programas con ciclos pueden requerir de la iniciali-
zación al principio de cada repetición; por lo general, dicha reinicialización se realiza mediante instrucciones de
asignación, en vez de hacerlo en las declaraciones.
La instrucción while (líneas 19 a 33) itera 10 veces. Durante cada iteración, el ciclo recibe y procesa un
resultado del examen. Observe que la instrucción if...else (líneas 26 a 29) para procesar cada resultado se
anida en la instrucción while. Si resultado es 1, la instrucción if...else incrementa a aprobados; en caso
1 Inicializar aprobados en cero
2 Inicializar reprobados en cero
3 Inicializar contador de estudiantes en uno
4
5 Mientras el contador de estudiantes sea menor o igual a 10
6 Pedir al usuario que introduzca el siguiente resultado del examen
7 Recibir como entrada el siguiente resultado del examen
8
9 Si el estudiante aprobó
10 Sumar uno a aprobados
11 De lo contrario
12 Sumar uno a reprobados
13
14 Sumar uno al contador de estudiantes
15
16 Imprimir el número de aprobados
17 Imprimir el número de reprobados
18
19 Si más de ocho estudiantes aprobaron
20 Imprimir “Aumentar colegiatura”
Figura 4.11 | El seudocódigo para el problema de los resultados del examen.
contrario, asume que resultado es 2 e incrementa reprobados. La línea 32 incrementa contadorEstudiantes
antes de que se evalúe otra vez la condición del ciclo, en la línea 19. Después de introducir 10 valores, el ciclo
termina y la línea 36 muestra el número de aprobados y de reprobados. La instrucción if de las líneas 39 a 40
determina si más de ocho estudiantes aprobaron el examen y, de ser así, imprime el mensaje "Aumentar cole-
giatura".
Tip para prevenir errores 4.3
Inicializar las variables locales cuando se declaran ayuda al programador a evitar cualquier error de compilación
que pudiera surgir, debido a los intentos por utilizar datos sin inicializar. Aunque Java no requiere que se incorporen
las inicializaciones de variables locales en las declaraciones, sí requiere que se inicialicen las variables locales antes de
utilizar sus valores en una expresión.
1 // Fig. 4.12: Analisis.java
2 // Análisis de los resultados de un examen.
3 import java.util.Scanner; // esta clase utiliza la clase Scanner
4
5 public class Analisis
6 {
7 public void procesarResultadosExamen()
8 {
9 // crea objeto Scanner para obtener la entrada de la ventana de comandos
10 Scanner entrada = new Scanner( System.in );
11
12 // inicialización de las variables en declaraciones
13 int aprobados = 0; // número de aprobados
14 int reprobados = 0; // número de reprobados
15 int contadorEstudiantes = 1; // contador de estudiantes
16 int resultado; // un resultado del examen (obtiene el valor del usuario)
17
18 // procesa 10 estudiantes, usando ciclo controlado por contador
19 while ( contadorEstudiantes <= 10 )
20 {
21 // pide al usuario la entrada y obtiene el valor
22 System.out.print( "Escriba el resultado (1 = aprobado, 2 = reprobado): " );
23 resultado = entrada.nextInt();
24
25 // if...else anidado en while
26 if ( resultado == 1 ) // si resultado 1,
27 aprobados = aprobados + 1; // incrementa aprobados;
28 else // de lo contrario, resultado no es 1, por lo que
29 reprobados = reprobados + 1; // incrementa reprobados
30
31 // incrementa contadorEstudiantes, para que el ciclo termine en un momento dado
32 contadorEstudiantes = contadorEstudiantes + 1;
33 } // fin de while
34
35 // fase de terminación; prepara y muestra los resultados
36 System.out.printf( "Aprobados: %dnReprobados: %dn", aprobados, reprobados );
37
38 // determina si más de 8 estudiantes aprobaron
39 if ( aprobados > 8 )
40 System.out.println( "Aumentar colegiatura" );
41 } // fin del método procesarResultadosExamen
42
43 } // fin de la clase Analisis
Figura 4.12 | Estructuras de control anidadas: problema de los resultados del examen.
4.10 Cómo formular algoritmos: instrucciones de control anidadas 137
138 Capítulo 4 Instrucciones de control: parte 1
La clase PruebaAnalisis para demostrar la clase Analisis
La clase PruebaAnalisis (figura 4.13) crea un objeto Analisis (línea 8) e invoca al método procesarResul-
tadosExamen (línea 9) de ese objeto para procesar un conjunto de resultados de un examen, introducidos por
el usuario. La figura 4.13 muestra la entrada y salida de dos ejecuciones de ejemplo del programa. Durante la
primera ejecución de ejemplo, la condición en la línea 39 del método procesarResultadosExamen de la figura
4.12 es verdadera; más de ocho estudiantes aprobaron el examen, por lo que el programa imprime un mensaje
indicando que se debe aumentar la colegiatura.
1 // Fig. 4.13: PruebaAnalisis.java
2 // Programa de prueba para la clase Analisis.
3
4 public class PruebaAnalisis
5 {
6 public static void main( String args[] )
7 {
8 Analisis aplicacion = new Analisis(); // crea objeto Analisis
9 aplicacion.procesarResultadosExamen(); // llama al método para procesar los
resultados
10 } // fin de main
11
12 } // fin de la clase PruebaAnalisis
Figura 4.13 | Programa de prueba para la clase Analisis (figura 4.12).
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 2
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Aprobados: 9
Reprobados: 1
Aumentar colegiatura
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 2
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 2
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 2
Escriba el resultado (1 = aprobado, 2 = reprobado): 2
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Escriba el resultado (1 = aprobado, 2 = reprobado): 1
Aprobados: 6
Reprobados: 4
4.11 Operadores de asignación compuestos
Java cuenta con varios operadores de asignación compuestos para abreviar las expresiones de asignación. Cual-
quier instrucción de la forma
variable = variable operador expresión;
en donde operador es uno de los operadores binarios +, -, *, / o % (o alguno de los otros que veremos más adelan-
te en el libro), puede escribirse de la siguiente forma:
variable operador= expresión;
Por ejemplo, puede abreviar la instrucción
c = c + 3;
mediante el operador de asignación compuesto de suma, +=, de la siguiente manera:
c += 3;
El operador += suma el valor de la expresión que está a la derecha del operador, al valor de la variable que está
a la izquierda del operador, y almacena el resultado en la variable que está a la izquierda del operador. Por lo
tanto, la expresión de asignación c += 3 suma 3 a c. La figura 4.14 muestra los operadores de asignación aritmé-
ticos compuestos, algunas expresiones de ejemplo en las que se utilizan los operadores y las explicaciones de lo
que estos operadores hacen.
Operador de
asignación
Expresión de
ejemplo Explicación Asigna
Suponer que: int c = 3, d = 5, e = 4, f = 6, g = 12;
+= c += 7 c = c + 7 10 a c
-= d -= 4 d = d – 4 1 a d
*= e *= 5 e = e * 5 20 a e
/= f /= 3 f = f / 3 2 a f
%= g %= 9 g = g % 9 3 a g
Figura 4.14 | Operadores de asignación aritméticos.
4.12 Operadores de incremento y decremento
Java proporciona dos operadores unarios para sumar 1, o restar 1, al valor de una variable numérica. Estos
operadores son el operador de incremento unario, ++, y el operador de decremento unario, --, los cuales se
sintetizan en la figura 4.15. Un programa puede incrementar en 1 el valor de una variable llamada c, utilizando
el operador de incremento, ++, en lugar de usar la expresión c = c + 1 o c += 1. A un operador de incremento
o decremento que se coloca antes de una variable se le llama operador de preincremento o predecremento,
respectivamente. A un operador de incremento o decremento que se coloca después de una variable se le llama
operador de postincremento o postdecremento, respectivamente.
Al proceso de utilizar el operador de preincremento (o postdecremento) para sumar (o restar) 1 a una varia-
ble, se le conoce como preincrementar (o predecrementar) la variable. Al preincrementar (o predecrementar)
una variable, ésta se incrementa (o decrementa) en 1, y después el nuevo valor de la variable se utiliza en la expre-
sión en la que aparece. Al proceso de utilizar el operador de preincremento (o postdecremento) para sumar (o
restar) 1 a una variable, se le conoce como postincrementar (o postdecrementar) la variable. Al postincrementar
(o postdecrementar) una variable, el valor actual de la variable se utiliza en la expresión en la que aparece y después
el valor de la variable se incrementa (o decrementa) en 1.
Buena práctica de programación 4.7
A diferencia de los operadores binarios, los operadores unarios de incremento y decremento deben colocarse enseguida
de sus operandos, sin espacios entre ellos.
4.12 Operadores de incremento y decremento 139
140 Capítulo 4 Instrucciones de control: parte 1
La figura 4.16 demuestra la diferencia entre la versión de preincremento y la versión de predecremento del
operador de incremento ++. El operador de decremento (--) funciona de manera similar. Observe que este ejem-
plo sólo contiene una clase, en donde el método main realiza todo el trabajo de ésta. En éste y en el capítulo 3,
usted ha visto ejemplos que consisten en dos clases: una clase contiene los métodos que realizan tareas útiles, y la
otra contiene el método main, que crea un objeto de la otra clase y hace llamadas a sus métodos. En este ejemplo
simplemente queremos mostrarle la mecánica del operador ++, por lo que sólo usaremos una declaración de clase
que contiene el método main. Algunas veces, cuando no tenga sentido tratar de crear una clase reutilizable para
demostrar un concepto simple, utilizaremos un ejemplo “mecánico” contenido completamente dentro del méto-
do main de una sola clase.
La línea 11 inicializa la variable c con 5, y la línea 12 imprime el valor inicial de c. La línea 13 imprime el
valor de la expresión c++. Esta expresión postincrementa la variable c, por lo que se imprime el valor original de
c (5), y después el valor de c se incrementa (a 6). Por ende, la línea 13 imprime el valor inicial de c (5) otra vez.
La línea 14 imprime el nuevo valor de c (6) para demostrar que, sin duda, se incrementó el valor de la variable en
la línea 13.
La línea 19 restablece el valor de c a 5, y la línea 20 imprime el valor de c. La línea 21 imprime el valor de la
expresión ++c. Esta expresión preincrementa a c, por lo que su valor se incrementa y después se imprime el nuevo
valor (6). La línea 22 imprime el valor de c otra vez, para mostrar que sigue siendo 6 después de que se ejecuta la
línea 21.
Los operadores de asignación compuestos aritméticos y los operadores de incremento y decremento pueden
utilizarse para simplificar las instrucciones de los programas. Por ejemplo, las tres instrucciones de asignación de
la figura 4.12 (líneas 27, 29 y 32)
aprobados = aprobados + 1;
reprobados = reprobados + 1;
contadorEstudiantes = contadorEstudiantes + 1;
pueden escribirse en forma más concisa con operadores de asignación compuestos, de la siguiente manera:
aprobados += 1;
reprobados += 1;
contadorEstudiantes += 1;
con operadores de preincremento de la siguiente forma:
++aprobados;
++reprobados;
++contadorEstudiantes;
o con operadores de postincremento de la siguiente forma:
aprobados++;
reprobados++;
contadorEstudiantes++;
Operador
Operador
Llamado
Expresión
de ejemplo Explicación
++ Preincremento ++a Incrementar a en 1, después utilizar el nuevo valor de a en la
expresión en que esta variable reside.
++ Postincremento a++ Usar el valor actual de a en la expresión en la que esta varia-
ble reside, después incrementar a en 1.
-- Predecremento --b Decrementar b en 1, después utilizar el nuevo valor de b en
la expresión en que esta variable reside.
-- Postdecremento b-- Usar el valor actual de b en la expresión en la que esta varia-
ble reside, después decrementar b en 1.
Figura 4.15 | Los operadores de incremento y decremento.
Al incrementar o decrementar una variable que se encuentre en una instrucción por sí sola, las formas pre-
incremento y postincremento tienen el mismo efecto, al igual que las formas predecremento y postdecremento.
Solamente cuando una variable aparece en el contexto de una expresión más grande es cuando los operadores
preincremento y postdecremento tienen distintos efectos (y lo mismo se aplica a los operadores de predecremento
y postdecremento).
Error común de programación 4.9
Tratar de usar el operador de incremento o decremento en una expresión a la que no se le pueda asignar un valor es
un error de sintaxis. Por ejemplo, escribir ++(x + 1) es un error de sintaxis, ya que (x + 1) no es una variable.
La figura 4.17 muestra la precedencia y la asociatividad de los operadores que se han presentado hasta este
punto. Los operadores se muestran de arriba a abajo, en orden descendente de precedencia. La segunda columna
describe la asociatividad de los operadores en cada nivel de precedencia. El operador condicional (?:), los opera-
dores unarios de incremento (++), decremento (--), suma (+) y resta (-), los operadores de conversión de tipo y
los operadores de asignación =, +=, -=, *=, /= y %= se asocian de derecha a izquierda. Todos los demás operado-
res en la tabla de precedencia de operadores de la figura 4.17 se asocian de izquierda a derecha. La tercera columna
enlista el tipo de cada grupo de operadores.
1 // Fig. 4.16: Incremento.java
2 // Operadores de preincremento y postincremento.
3
4 public class Incremento
5 {
6 public static void main( String args[] )
7 {
8 int c;
9
10 // demuestra el operador de preincremento
11 c = 5; // asigna 5 a c
12 System.out.println( c ); // imprime 5
13 System.out.println( c++ ); // imprime 5, después postincrementa
14 System.out.println( c ); // imprime 6
15
16 System.out.println(); // omite una línea
17
18 // demuestra el operador de postincremento
19 c = 5; // asigna 5 a c
20 System.out.println( c ); // imprime 5
21 System.out.println( ++c ); // preincrementa y después imprime 6
22 System.out.println( c ); // imprime 6
23
24 } // fin de main
25
26 } // fin de la clase Incremento
Figura 4.16 | Preincrementar y postincrementar.
5
5
6
5
6
6
4.12 Operadores de incremento y decremento 141
142 Capítulo 4 Instrucciones de control: parte 1
4.13 Tipos primitivos
La tabla del apéndice D, Tipos primitivos, enlista los ocho tipos primitivos en Java. Al igual que sus lenguajes
antecesores C y C++, Java requiere que todas las variables tengan un tipo. Es por esta razón que Java se conoce
como un lenguaje fuertemente tipificado.
En C y C++, los programadores frecuentemente tienen que escribir versiones independientes de los progra-
mas, ya que no se garantiza que los tipos primitivos sean idénticos de computadora en computadora. Por ejemplo,
un valor int en un equipo podría representarse mediante 16 bits (2 bytes) de memoria, mientras que un valor
int en otro equipo podría representarse mediante 32 bits (4 bytes) de memoria. En Java, los valores int siempre
son de 32 bits (4 bytes).
Tip de portabilidad 4.1
A diferencia de C y C++, los tipos primitivos en Java son portables en todas las plataformas con soporte para Java.
Cada uno de los tipos del apéndice D se enlista con su tamaño en bits (hay ocho bits en un byte) y su rango
de valores. Como los diseñadores de Java desean que sea lo más portable posible, utilizan estándares reconocidos
internacionalmente tanto para los formatos de caracteres (Unicode; para más información, visite www.unicode.
org) como para los números de punto flotante (IEEE 754; para más información, visite grouper.ieee.org/
groups/754/).
En la sección 3.5 vimos que a las variables de tipos primitivos que se declaran fuera de un método, como
campos de una clase, se les asignan valores predeterminados, a menos que se inicialicen en forma explícita. Las
variables de los tipos char, byte, short, int, long, float y double reciben el valor 0 de manera predeterminada.
Las variables de tipo boolean reciben el valor false de manera predeterminada. Las variables de instancia de tipo
por referencia se inicializan de manera predeterminada con el valor null.
4.14 (Opcional) Ejemplo práctico de GUI y gráficos:
creación de dibujos simples
Una de las características interesantes de Java es su soporte para gráficos, el cual permite a los programadores
mejorar visualmente sus aplicaciones. Esta sección presenta una de las capacidades gráficas de Java: dibujar líneas.
También cubre los aspectos básicos acerca de cómo crear una ventana para mostrar un dibujo en la pantalla de la
computadora.
Para dibujar en Java, debe comprender su sistema de coordenadas (figura 4.18), un esquema para identificar
cada uno de los puntos en la pantalla. De manera predeterminada, la esquina superior izquierda de un compo-
nente de la GUI tiene las coordenadas (0, 0). Un par de coordenadas está compuesto por una coordenada x (la
Operadores Asociatividad Tipo
++ -- derecha a izquierda postfijo unario
++ -- + - ( tipo ) derecha a izquierda prefijo unario
* / % izquierda a derecha multiplicativo
+ - izquierda a derecha aditivo
< <= > >= izquierda a derecha relacional
== != izquierda a derecha igualdad
?: derecha a izquierda condicional
= += -= *= /= %= derecha a izquierda asignación
Figura 4.17 | Precedencia y asociatividad de los operadores vistos hasta ahora.
coordenada horizontal) y una coordenada y (la coordenada vertical). La coordenada x es la ubicación horizon-
tal que se desplaza de izquierda a derecha. La coordenada y es la ubicación vertical que se desplaza de arriba hacia
abajo. El eje x describe cada una de las coordenadas horizontales, y el eje y describe cada una de las coordenadas
verticales.
Las coordenadas indican en dónde deben mostrarse los gráficos en una pantalla. Las unidades de las coorde-
nadas se miden en píxeles. Un píxel es la unidad de resolución más pequeña de una pantalla. (El término píxel
significa “elemento de imagen”).
Nuestra primera aplicación de dibujo simplemente dibuja dos líneas. La clase PanelDibujo (figura 4.19)
realiza el dibujo en sí, mientras que la clase PruebaPanelDibujo (figura 4.20) crea una ventana para mostrar
el dibujo. En la clase PanelDibujo, las instrucciones import de las líneas 3 y 4 nos permiten utilizar la clase
Graphics (del paquete java.awt), que proporciona varios métodos para dibujar texto y figuras en la pantalla, y
la clase JPanel (del paquete javax.swing), que proporciona un área en la que podemos dibujar.
La línea 6 utiliza la palabra clave extends para indicar que la clase PanelDibujo es un tipo mejorado de
JPanel. La palabra clave extends representa algo que se denomina relación de herencia, en la cual nuestra nueva
clase PanelDibujo empieza con los miembros existentes (datos y métodos) de la clase JPanel. La clase de la cual
(0, 0)
(x, y)
+y
+x
eje y
eje x
Figura 4.18 | Sistema de coordenadas de Java. Las unidades se miden en píxeles.
1 // Fig. 4.19: PanelDibujo.java
2 // Uso de drawLine para conectar las esquinas de un panel.
3 import java.awt.Graphics;
4 import javax.swing.JPanel;
5
6 public class PanelDibujo extends JPanel
7 {
8 // dibuja una x desde las esquinas del panel
9 public void paintComponent( Graphics g )
10 {
11 // llama a paintComponent para asegurar que el panel se muestre correctamente
12 super.paintComponent( g );
13
14 int anchura = getWidth(); // anchura total
15 int altura = getHeight(); // altura total
16
17 // dibuja una línea de la esquina superior izquierda a la esquina inferior derecha
18 g.drawLine( 0, 0, anchura, altura );
19
20 // dibuja una línea de la esquina inferior izquierda a la esquina superior derecha
21 g.drawLine( 0, altura, anchura, 0 );
22 } // fin del método paintComponent
23 } // fin de la clase PanelDibujo
Figura 4.19 | Uso de drawLine para conectar las esquinas de un panel.
4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples 143
144 Capítulo 4 Instrucciones de control: parte 1
PanelDibujo hereda, JPanel, aparece a la derecha de la palabra clave extends. En esta relación de herencia, a
JPanel se le conoce como la superclase y PanelDibujo es la subclase. Esto produce una clase PanelDibujo que
tiene los atributos (datos) y comportamientos (métodos) de la clase JPanel, así como las nuevas características
que agregaremos en nuestra declaración de la clase PanelDibujo; específicamente, la habilidad de dibujar dos líneas
a lo largo de las diagonales del panel. En el capítulo 9 explicaremos detalladamente el concepto de herencia.
Todo JPanel, incluyendo nuestro PanelDibujo, tiene un método paintComponent (líneas 9 a 22), que
el sistema llama automáticamente cada vez que necesita mostrar el objeto JPanel. El método paintComponent
debe declararse como se muestra en la línea 9; de no ser así, el sistema no llamará al método. Este método se
llama cuando se muestra un objeto JPanel por primera vez en la pantalla, cuando una ventana en la pantalla
lo cubre y después lo descubre, y cuando la ventana en la que aparece cambia su tamaño. El método paint-
Component requiere un argumento, un objeto Graphics, que el sistema proporciona por usted cuando llama a
paintComponent.
La primera instrucción en cualquier método paintComponent que cree debe ser siempre:
super.paintComponent( g );
la cual asegura que el panel se despliegue apropiadamente en la pantalla, antes de empezar a dibujar en él. A con-
tinuación, las líneas 14 y 15 llaman a dos métodos que la clase PanelDibujo hereda de la clase JPanel. Como
PanelDibujo extiende a JPanel, PanelDibujo puede usar cualquier método public que esté declarado en JPa-
nel. Los métodos getWidth y getHeight devuelven la anchura y la altura del objeto JPanel, respectivamente.
Las líneas 14 y 15 almacenan estos valores en las variables locales anchura y altura. Por último, las líneas 18 y
21 utilizan la referencia g de la clase Graphics para llamar al método drawLine, y que dibuje las dos líneas. Los
primeros dos argumentos son las coordenadas x y y para uno de los puntos finales de la línea, y los últimos dos
argumentos son las coordenadas para el otro punto final. Si cambia de tamaño la ventana, las líneas se escalaran de
manera acorde, ya que los argumentos se basan en la anchura y la altura del panel. Al cambiar el tamaño de la ven-
tana en esta aplicación, el sistema llama a paintComponent para volver a dibujar el contenido de PanelDibujo.
Para mostrar el PanelDibujo en la pantalla, debemos colocarlo en una ventana. Usted debe crear una ven-
tana con un objeto de la clase JFrame. En PruebaPanelDibujo.java (figura 4.20), la línea 3 importa la clase
JFrame del paquete javax.swing. La línea 10 en el método main de la clase PruebaPanelDibujo crea una
instancia de la clase PanelDibujo, la cual contiene nuestro dibujo, y la línea 13 crea un nuevo objeto JFrame
que puede contener y mostrar nuestro panel. La línea 16 llama al método setDefaultCloseOperation con el
argumento JFrame.EXIT_ON_CLOSE, para indicar que la aplicación debe terminar cuando el usuario cierre la
ventana. La línea 18 utiliza el método add de JFrame para adjuntar el objeto PanelDibujo, que contiene nues-
tro dibujo, al objeto JFrame. La línea 19 establece el tamaño del objeto JFrame. El método setSize recibe dos
parámetros: la anchura del objeto JFrame y la altura. Por último, la línea 20 muestra el objeto JFrame. Cuando
se muestra este objeto, se hace la llamada al método paintComponent de PanelDibujo (líneas 9 a 22 de la figura
4.19) y se dibujan las dos líneas (vea los resultados de ejemplo de la figura 4.20). Cambie el tamaño de la ventana,
para que vea que las líneas siempre se dibujan con base en la anchura y altura actuales de la ventana.
Ejercicios del ejemplo práctico de GUI y gráficos
4.1 Utilizar ciclos e instrucciones de control para dibujar líneas puede producir muchos diseños interesantes.
a) Cree el diseño que se muestra en la captura de pantalla izquierda de la figura 4.21. Este diseño dibuja líneas que
parten desde la esquina superior izquierda, y se despliegan hasta cubrir la mitad superior izquierda del panel. Un
método es dividir la anchura y la altura en un número equivalente de pasos (nosotros descubrimos que 15 pasos
es una buena cantidad). El primer punto final de una línea siempre estará en la esquina superior izquierda (0,0).
El segundo punto final puede encontrarse partiendo desde la esquina inferior izquierda, y avanzando un paso
vertical hacia arriba, y un paso horizontal hacia la derecha. Dibuje una línea entre los dos puntos finales. Con-
tinúe avanzando hacia arriba y a la derecha, para encontrar cada punto final sucesivo. La figura deberá escalarse
apropiadamente, a medida que se cambie el tamaño de la ventana.
b) Modifique su respuesta en la parte (a) para hacer que las líneas se desplieguen a partir de las cuatro esquinas,
como se muestra en la captura de pantalla derecha de la figura 4.21. Las líneas de esquinas opuestas deberán
intersecarse a lo largo de la parte media.
4.2 La figura 4.22 muestra dos diseños adicionales, creados mediante el uso de ciclos while y drawLine.
a) Cree el diseño de la captura de pantalla izquierda de la figura 4.22. Empiece por dividir cada flanco en un núme-
ro equivalente de incrementos (elegimos 15 de nuevo). La primera línea empieza en la esquina superior izquierda
y termina un paso a la derecha, en el flanco inferior. Para cada línea sucesiva, avance hacia abajo un incremento
en el flanco izquierdo, y un incremento a la derecha en el flanco inferior. Continúe dibujando líneas hasta llegar a
la esquina inferior derecha. La figura deberá escalarse a medida que se cambie el tamaño de la ventana, de manera
que los puntos finales siempre toquen los flancos.
1 // Fig. 4.20: PruebaPanelDibujo.java
2 // Aplicación que muestra un PanelDibujo.
3 import javax.swing.JFrame;
4
5 public class PruebaPanelDibujo
6 {
7 public static void main( String args[] )
8 {
9 // crea un panel que contiene nuestro dibujo
10 PanelDibujo panel = new PanelDibujo();
11
12 // crea un nuevo marco para contener el panel
13 JFrame aplicacion = new JFrame();
14
15 // establece el marco para salir cuando se cierre
16 aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
17
18 aplicacion.add( panel ); // agrega el panel al marco
19 aplicacion.setSize( 250, 250 ); // establece el tamaño del marco
20 aplicacion.setVisible( true ); // hace que el marco sea visible
21 } // fin de main
22 } // fin de la clase PruebaPanelDibujo
Figura 4.20 | Creación de un objeto JFrame para mostrar el objeto PanelDibujo.
Figura 4.21 | Despliegue de líneas desde una esquina.
4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples 145
146 Capítulo 4 Instrucciones de control: parte 1
b) Modifique su respuesta en la parte (a) para reflejar el diseño en las cuatro esquinas, como se muestra en la captura
de pantalla derecha de la figura 4.22.
4.15 (Opcional) Ejemplo práctico de Ingeniería de Software:
identificación de los atributos de las clases
En la sección 3.10 empezamos la primera etapa de un diseño orientado a objetos (DOO) para nuestro sistema
ATM: analizar el documento de requerimientos e identificar las clases necesarias para implementar el sistema.
Enlistamos los sustantivos y las frases nominales en el documento de requerimientos e identificamos una clase
separada para cada uno de ellos, que desempeña un papel importante en el sistema ATM. Después modelamos las
clases y sus relaciones en un diagrama de clases de UML (figura 3.24). Las clases tienen atributos (datos) y ope-
raciones (comportamientos). En los programas en Java, los atributos de las clases se implementan como campos,
y las operaciones de las clases se implementan como métodos. En esta sección determinaremos muchos de los
atributos necesarios en el sistema ATM. En el capítulo 5 examinaremos cómo esos atributos representan el estado
de un objeto. En el capítulo 6 determinaremos las operaciones de las clases.
Identificación de los atributos
Considere los atributos de algunos objetos reales: los atributos de una persona incluyen su altura, peso y si es
zurdo, diestro o ambidiestro. Los atributos de un radio incluyen la estación, el volumen y si está en AM o FM.
Los atributos de un automóvil incluyen las lecturas de su velocímetro y odómetro, la cantidad de gasolina en su
tanque y la velocidad de marcha en la que se encuentra. Los atributos de una computadora personal incluyen
su fabricante (por ejemplo, Dell, Sun, Apple o IBM), el tipo de pantalla (por ejemplo, LCD o CRT), el tamaño
de su memoria principal y el de su disco duro.
Podemos identificar muchos atributos de las clases en nuestro sistema, analizando las palabras y frases des-
criptivas en el documento de requerimientos. Para cada palabra o frase que descubramos desempeña un rol
importante en el sistema ATM, creamos un atributo y lo asignamos a una o más de las clases identificadas en la
sección 3.10. También creamos atributos para representar los datos adicionales que pueda necesitar una clase, ya
que dichas necesidades se van aclarando a lo largo del proceso de diseño.
La figura 4.23 lista las palabras o frases del documento de requerimientos que describen a cada una de las
clases. Para formar esta lista, leemos el documento de requerimientos e identificamos cualquier palabra o frase
que haga referencia a las características de las clases en el sistema. Por ejemplo, el documento de requerimien-
tos describe los pasos que se llevan a cabo para obtener un “monto de retiro”, por lo que listamos “monto” enseguida
de la clase Retiro.
La figura 4.23 nos conduce a crear un atributo de la clase ATM. Esta clase mantiene información acerca del
estado del ATM. La frase “el usuario es autenticado” describe un estado del ATM (en la sección 5.11 hablaremos
con detalle sobre los estados), por lo que incluimos usuarioAutenticado como un atributo Boolean (es decir,
un atributo que tiene un valor de true o false) en la clase ATM. Observe que el atributo tipo Boolean en UML
Figura 4.22 | Arte lineal con ciclos y drawLine.
es equivalente al tipo boolean en Java. Este atributo indica si el ATM autenticó con éxito al usuario actual o no;
usuarioAutenticado debe ser true para que el sistema permita al usuario realizar transacciones y acceder a la
información de la cuenta. Este atributo nos ayuda a cerciorarnos de la seguridad de los datos en el sistema.
Las clases SolicitudSaldo, Retiro y Deposito comparten un atributo. Cada transacción requiere un
“número de cuenta” que corresponde a la cuenta del usuario que realiza la transacción. Asignamos el atributo
entero numeroCuenta a cada clase de transacción para identificar la cuenta a la que se aplica un objeto de la
clase.
Las palabras y frases descriptivas en el documento de requerimientos también sugieren ciertas diferencias en
los atributos requeridos por cada clase de transacción. El documento de requerimientos indica que para retirar
efectivo o depositar fondos, los usuarios deben introducir un “monto” específico de dinero para retirar o depositar,
respectivamente. Por ende, asignamos a las clases Retiro y Deposito un atributo llamado monto para almacenar
el valor suministrado por el usuario. Los montos de dinero relacionados con un retiro y un depósito son carac-
terísticas que definen estas transacciones, que el sistema requiere para que se lleven a cabo. Sin embargo, la clase
SolicitudSaldo no necesita datos adicionales para realizar su tarea; sólo requiere un número de cuenta para
indicar la cuenta cuyo saldo hay que obtener.
La clase Cuenta tiene varios atributos. El documento de requerimientos establece que cada cuenta de banco
tiene un “número de cuenta” y un “NIP”, que el sistema utiliza para identificar las cuentas y autentificar a los
usuarios. A la clase Cuenta le asignamos dos atributos enteros: numeroCuenta y nip. El documento de requeri-
mientos también especifica que una cuenta debe mantener un “saldo” del monto de dinero que hay en la cuenta,
y que el dinero que el usuario deposita no estará disponible para su retiro sino hasta que el banco verifique la can-
tidad de efectivo en el sobre de depósito y cualquier cheque que contenga. Sin embargo, una cuenta debe registrar
de todas formas el monto de dinero que deposita un usuario. Por lo tanto, decidimos que una cuenta debe repre-
sentar un saldo utilizando dos atributos: saldoDisponible y saldoTotal. El atributo saldoDisponible rastrea
el monto de dinero que un usuario puede retirar de la cuenta. El atributo saldoTotal se refiere al monto total
de dinero que el usuario tiene “en depósito” (es decir, el monto de dinero disponible, más el monto de depósitos
en efectivo o la cantidad de cheques esperando a ser verificados). Por ejemplo, suponga que un usuario del ATM
deposita $50.00 en efectivo, en una cuenta vacía. El atributo saldoTotal se incrementaría a $50.00 para registrar
el depósito, pero el saldoDisponible permanecería en $0. [Nota: estamos suponiendo que el banco actualiza el
atributo saldoDisponible de una Cuenta poco después de que se realiza la transacción del ATM, en respuesta a
la confirmación de que se encontró un monto equivalente a $50.00 en efectivo o cheques en el sobre de depósito.
Asumimos que esta actualización se realiza a través de una transacción que realiza el empleado del banco mediante
Clase Palabras y frases descriptivas
ATM el usuario es autenticado
SolicitudSaldo número de cuenta
Retiro número de cuenta
monto
Deposito número de cuenta
monto
BaseDatosBanco [no hay palabras o frases descriptivas]
Cuenta número de cuenta
NIP
saldo
Pantalla [no hay palabras o frases descriptivas]
Teclado [no hay palabras o frases descriptivas]
DispensadorEfectivo empieza cada día cargado con 500 billetes de $20
RanuraDeposito [no hay palabras o frases descriptivas]
Figura 4.23 | Palabras y frases descriptivas del documento de requerimientos
del ATM.
4.15 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de los atributos de las clases 147
148 Capítulo 4 Instrucciones de control: parte 1
el uso de un sistema bancario distinto al del ATM. Por ende, no hablaremos sobre esta transacción en nuestro
ejemplo práctico].
La clase DispensadorEfectivo tiene un atributo. El documento de requerimientos establece que el dispen-
sador de efectivo “empieza cada día cargado con 500 billetes de $20”. El dispensador de efectivo debe llevar el
registro del número de billetes que contiene para determinar si hay suficiente efectivo disponible para satisfacer
la demanda de los retiros. Asignamos a la clase DispensadorEfectivo el atributo entero conteo, el cual se esta-
blece al principio en 500.
Para los verdaderos problemas en la industria, no existe garantía alguna de que el documento de requeri-
mientos será lo suficientemente robusto y preciso como para que el diseñador de sistemas orientados a objetos
determine todos los atributos, o inclusive todas las clases. La necesidad de clases, atributos y comportamientos
adicionales puede irse aclarando a medida que avance el proceso de diseño. A medida que progresemos a través de
este ejemplo práctico, nosotros también seguiremos agregando, modificando y eliminando información acerca
de las clases en nuestro sistema.
Modelado de los atributos
El diagrama de clases de la figura 4.24 enlista algunos de los atributos para las clases en nuestro sistema; las
palabras y frases descriptivas en la figura 4.23 nos llevan a identificar estos atributos. Por cuestión de simpleza,
la figura 4.24 no muestra las asociaciones entre las clases; en la figura 3.24 mostramos estas asociaciones. Ésta es
una práctica común de los diseñadores de sistemas, a la hora de desarrollar los diseños. En la sección 3.10 vimos
que en UML, los atributos de una clase se colocan en el compartimiento intermedio del rectángulo de la clase.
Listamos el nombre de cada atributo y su tipo, separados por un signo de dos puntos (:), seguido en algunos casos
de un signo de igual (=) y de un valor inicial.
Considere el atributo usuarioAutenticado de la clase ATM:
usuarioAutenticado : Boolean = false
La declaración de este atributo contiene tres piezas de información acerca del atributo. El nombre del atributo
es usuarioAutenticado. El tipo del atributo es Boolean. En Java, un atributo puede representarse mediante
un tipo primitivo, como boolean, int o double, o por un tipo de referencia como una clase (como vimos en el
capítulo 3). Hemos optado por modelar sólo los atributos de tipo primitivo en la figura 4.24; en breve hablaremos
sobre el razonamiento detrás de esta decisión. [Nota: los tipos de los atributos en la figura 4.24 están en notación
de UML. Asociaremos los tipos Boolean, Integer y Double en el diagrama de UML con los tipos primitivos
boolean, int y double en Java, respectivamente].
También podemos indicar un valor inicial para un atributo. El atributo usuarioAutenticado en la clase ATM
tiene un valor inicial de false. Esto indica que al principio el sistema no considera que el usuario está autenti-
cado. Si no se especifica un valor inicial para un atributo, sólo se muestran su nombre y tipo (separados por dos
puntos). Por ejemplo, el atributo numeroCuenta de la clase SolicitudSaldo es un entero. Aquí no mostramos
un valor inicial, ya que el valor de este atributo es un número que todavía no conocemos. Este número se deter-
minará en tiempo de ejecución, con base en el número de cuenta introducido por el usuario actual del ATM.
La figura 4.24 no incluye atributos para las clases Pantalla, Teclado y RanuraDeposito. Éstos son com-
ponentes importantes de nuestro sistema, para los cuales nuestro proceso de diseño aún no ha revelado ningún
atributo. No obstante, tal vez descubramos algunos en las fases restantes de diseño, o cuando implementemos
estas clases en Java. Esto es perfectamente normal.
Observación de ingeniería de software 4.6
En las primeras fases del proceso de diseño, a menudo las clases carecen de atributos (y operaciones). Sin embargo, esas
clases no deben eliminarse, ya que los atributos (y las operaciones) pueden hacerse evidentes en las fases posteriores de
diseño e implementación.
Observe que la figura 4.24 tampoco incluye atributos para la clase BaseDatosBanco. En el capítulo 3
vimos que en Java, los atributos pueden representarse mediante los tipos primitivos o los tipos por referencia.
Hemos optado por incluir sólo los atributos de tipo primitivo en el diagrama de clases de la figura 4.24 (y en
los diagramas de clases similares a lo largo del ejemplo práctico). Un atributo de tipo por referencia se modela
con más claridad como una asociación (en particular, una composición) entre la clase que contiene la referencia
y la clase del objeto al que apunta la referencia. Por ejemplo, el diagrama de clases de la figura 3.24 indica que
la clase BaseDatosBanco participa en una relación de composición con cero o más objetos Cuenta. De esta
composición podemos determinar que, cuando implementemos el sistema ATM en Java, tendremos que crear un
atributo de la clase BaseDatosBanco para almacenar cero o más objetos Cuenta. De manera similar, podemos
determinar los atributos de tipo por referencia de la clase ATM que correspondan a sus relaciones de composición
con las clases Pantalla, Teclado, DispensadorEfectivo y RanuraDeposito. Estos atributos basados en com-
posiciones serían redundantes si los modeláramos en la figura 4.24, ya que las composiciones modeladas en la
figura 3.24 transmiten de antemano el hecho de que la base de datos contiene información acerca de cero o más
cuentas, y que un ATM está compuesto por una pantalla, un teclado, un dispensador de efectivo y una ranura
para depósitos. Por lo general, los desarrolladores de software modelan estas relaciones de todo/parte como asocia-
ciones de composición, en vez de modelarlas como atributos requeridos para implementar las relaciones.
El diagrama de clases de la figura 4.24 proporciona una base sólida para la estructura de nuestro modelo, pero
no está completo. En la sección 5.11 identificaremos los estados y las actividades de los objetos en el modelo, y
en la sección 6.14 identificaremos las operaciones que realizan los objetos. A medida que presentemos más acerca
de UML y del diseño orientado a objetos, continuaremos reforzando la estructura de nuestro modelo.
Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
4.1 Por lo general, identificamos los atributos de las clases en nuestro sistema mediante el análisis de ____________
en el documento de requerimientos.
a) Los sustantivos y las frases nominales.
b) Las palabras y frases descriptivas.
c) Los verbos y las frases verbales.
d) Todo lo anterior.
4.2 ¿Cuál de los siguientes no es un atributo de un aeroplano?
a) Longitud.
b) Envergadura.
Figura 4.24 | Clases con atributos.
ATM
usuarioAutenticado : Boolean = false
SolicitudSaldo
numeroCuenta : Integer
DispensadorEfectivo
conteo : Integer = 500
RanuraDeposito
Pantalla
Teclado
Retiro
numeroCuenta : Integer
monto : Double
BaseDatosBanco
Deposito
numeroCuenta : Integer
monto : Double
Cuenta
numeroCuenta : Integer
nip : Integer
saldoDisponible : Double
saldoTotal : Double
4.15 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de los atributos de las clases 149
c) Volar.
d) Número de asientos.
4.3 Describa el significado de la siguiente declaración de un atributo de la clase DispensadorEfectivo en el diagrama
de clases de la figura 4.24:
conteo : Integer = 500
Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
4.1 b.
4.2 c. Volar es una operación o comportamiento de un aeroplano, no un atributo.
4.3 Esta declaración indica que el atributo conteo es de tipo Integer, con un valor inicial de 500. Este atributo lleva la
cuenta del número de billetes disponibles en el DispensadorEfectivo, en cualquier momento dado.
4.16 Conclusión
Este capítulo presentó las estrategias básicas de solución de problemas, que los programadores utilizan para crear
clases y desarrollar métodos para estas clases. Demostramos cómo construir un algoritmo (es decir, una metodo-
logía para resolver un problema), y después cómo refinar el algoritmo a través de diversas fases de desarrollo de
seudocódigo, lo cual produce código en Java que puede ejecutarse como parte de un método. El capítulo demos-
tró cómo utilizar el método de refinamiento de arriba a abajo, paso a paso, para planear las acciones específicas
que debe realizar un método, y el orden en el que debe realizar estas acciones.
Sólo se requieren tres tipos de estructuras de control (secuencia, selección y repetición) para desarrollar
cualquier algoritmo para solucionar un problema. Específicamente, en este capítulo demostramos el uso de
la instrucción de selección simple if, la instrucción de selección doble if…else y la instrucción de repetición
while. Estas instrucciones son algunos de los bloques de construcción que se utilizan para construir soluciones
para muchos problemas. Utilizamos el apilamiento de instrucciones de control para calcular el total y el promedio
de un conjunto de calificaciones de estudiantes, mediante la repetición controlada por un contador y controlada
por un centinela, y utilizamos el anidamiento de instrucciones de control para analizar y tomar decisiones con
base en un conjunto de resultados de un examen. Presentamos los operadores de asignación compuestos de Java,
así como sus operadores de incremento y decremento. Por último, hablamos sobre los tipos primitivos disponibles
para los programadores de Java. En el capítulo 5, Instrucciones de control: parte 2, continuaremos nuestra discu-
sión acerca de las instrucciones de control, en donde presentaremos las instrucciones for, do…while y switch.
Resumen
Sección 4.1 Introducción
• Antes de escribir un programa para resolver un problema, debe tener una comprensión detallada acerca del proble-
ma y una metodología cuidadosamente planeada para resolverlo. También debe comprender los bloques de cons-
trucción disponibles, y emplear las técnicas probadas para construir programas.
Sección 4.2 Algoritmos
• Cualquier problema de cómputo puede resolverse mediante la ejecución de una serie de acciones, en un orden espe-
cífico.
• Un procedimiento para resolver un problema, en términos de las acciones a ejecutar y el orden en el que se ejecutan,
se denomina algoritmo.
• El proceso de especificar el orden en el que se ejecutan las instrucciones en un programa se denomina control del
programa.
Sección 4.3 Seudocódigo
• El seudocódigo es un lenguaje informal, que ayuda a los programadores a desarrollar algoritmos sin tener que pre-
ocuparse por los estrictos detalles de la sintaxis del lenguaje Java.
• El seudocódigo es similar al lenguaje cotidiano; es conveniente y amigable para el usuario, pero no es un verdadero
lenguaje de programación de computadoras.
150 Capítulo 4 Instrucciones de control: parte 1
• El seudocódigo ayuda al programador a “idear” un programa antes de intentar escribirlo en un lenguaje de progra-
mación.
• El seudocódigo cuidadosamente preparado puede convertirse con facilidad en su correspondiente programa en
Java.
Sección 4.4 Estructuras de control
• Por lo general, las instrucciones en un programa se ejecutan, una después de la otra, en el orden en el que están
escritas. A este proceso se le conoce como ejecución secuencial.
• Varias instrucciones de Java permiten al programador especificar que la siguiente instrucción a ejecutar no es nece-
sariamente la siguiente en la secuencia. A esto se le conoce como transferencia de control.
• Bohm y Jacopini demostraron que todos los programas podían escribirse en términos de sólo tres estructuras de
control: la estructura de secuencia, la estructura de selección y la estructura de repetición.
• El término “estructuras de control” proviene del campo de las ciencias computacionales. La Especificación del lengua-
je Java se refiere a las “estructuras de control” como “instrucciones de control”.
• La estructura de secuencia está integrada en Java. A menos que se indique lo contrario, la computadora ejecuta las
instrucciones de Java, una después de la otra, en el orden en el que están escritas; es decir, en secuencia.
• En cualquier parte en donde pueda colocarse una sola acción, pueden colocarse varias acciones en secuencia.
• Los diagramas de actividad forman parte de UML. Un diagrama de actividad modela el flujo de trabajo (también
conocido como la actividad) de una parte de un sistema de software.
• Los diagramas de actividad se componen de símbolos de propósito especial, como los símbolos de estados de acción,
rombos y pequeños círculos. Estos símbolos se conectan mediante flechas de transición, las cuales representan el
flujo de la actividad.
• Los estados de acción representan las acciones a realizar. Cada estado de acción contiene una expresión de acción, la
cual especifica una acción específica a realizar.
• Las flechas en un diagrama de actividad representan las transiciones, que indican el orden en el que ocurren las
acciones representadas por los estados de acción.
• El círculo relleno que se encuentra en la parte superior de un diagrama de actividad representa el estado inicial de la
actividad: el comienzo del flujo de trabajo antes de que el programa realice las acciones modeladas.
• El círculo sólido rodeado por una circunferencia, que aparece en la parte inferior del diagrama, representa el estado
final: el término del flujo de trabajo después de que el programa realiza sus acciones.
• Los rectángulos con las esquinas superiores derechas dobladas se llaman notas en UML: comentarios que describen
el propósito de los símbolos en el diagrama.
• Java tiene tres tipos de instrucciones de selección. La instrucción if realiza una acción si una condición es verdadera,
o evita la acción si la condición es falsa. La instrucción if...else realiza una acción si una condición es verda-
dera, y realiza una acción distinta si la condición es falsa. La instrucción switch realiza una de varias acciones
distintas, dependiendo del valor de una expresión.
• La instrucción if es una instrucción de selección simple, ya que selecciona o ignora una sola acción, o un solo grupo
de acciones.
• La instrucción if...else se denomina instrucción de selección doble, ya que selecciona una de dos acciones dis-
tintas, o grupos de acciones.
• La instrucción switch se llama instrucción de selección múltiple, ya que selecciona una de varias acciones distintas,
o grupos de acciones.
• Java cuenta con las instrucciones de repetición (ciclos) while, do...while y for, las cuales permiten a los progra-
mas ejecutar instrucciones en forma repetida, siempre y cuando una condición de continuación de ciclo siga siendo
verdadera.
• Las instrucciones while y for realizan la(s) acción(es) en sus cuerpos, cero o más veces; si al principio la condición
de continuación de ciclo es falsa, la(s) acción(es) no se ejecutará(n). La instrucción do...while lleva a cabo la(s)
acción(es) que contiene en su cuerpo, una o más veces.
• Las palabras if, else, switch, while, do y for son palabras claves en Java. Las palabras clave no pueden utilizarse
como identificadores, como los nombres de variables.
• Cada programa se forma mediante una combinación de todas las instrucciones de secuencia, selección y repetición
que sean apropiadas para el algoritmo que implementa ese programa.
• Las instrucciones de control de una sola entrada/una sola salida facilitan la construcción de los programas; “adjun-
tamos” una instrucción de control a otra mediante la conexión del punto de salida de una al punto de entrada de la
siguiente. A esto se le conoce como apilamiento de instrucciones de control.
• Sólo hay una forma alterna en la que pueden conectarse las instrucciones de control (anidamiento de instrucciones
de control), en la cual una instrucción de control aparece dentro de otra instrucción de control.
Resumen 151
152 Capítulo 4 Instrucciones de control: parte 1
Sección 4.5 Instrucción de selección simple if
• Los programas utilizan instrucciones de selección para elegir entre los cursos alternativos de acción.
• El diagrama de actividad de una instrucción if de selección simple contiene el rombo, o símbolo de decisión, el cual
indica que se tomará una decisión. El flujo de trabajo continuará a lo largo de una ruta determinada por las condi-
ciones de guardia asociadas al símbolo, que pueden ser verdaderas o falsas. Cada flecha de transición que emerge de
un símbolo de decisión tiene una condición de guardia. Si una condición de guardia es verdadera, el flujo de trabajo
entra al estado de acción al que apunta la flecha de transición.
• La instrucción if es una instrucción de control de una sola entrada/una sola salida.
Sección 4.6 Instrucción de selección doble if...else
• La instrucción if de selección simple realiza una acción indicada sólo cuando la condición es verdadera.
• La instrucción if...else de selección doble realiza una acción cuando la condición es verdadera, y otra acción
distinta cuando la condición es falsa.
• El operador condicional (?:) puede usarse en lugar de una instrucción if...else. Éste es el único operador ternario
de Java: recibe tres operandos. En conjunto, los operandos y el símbolo ?: forman una expresión condicional.
• Un programa puede evaluar varios casos, colocando instrucciones if...else dentro de otras instrucciones if...
else, para crear instrucciones if...else anidadas.
• El compilador de Java siempre asocia un else con el if que lo precede inmediatamente, a menos que se le indique
otra cosa mediante la colocación de llaves ({ y }). Este comportamiento puede conducir a lo que se conoce como el
problema del else suelto.
• Por lo general, la instrucción if espera sólo una instrucción en su cuerpo. Para incluir varias instrucciones en el
cuerpo de un if (o en el cuerpo de un else para una instrucción if...else), encierre las instrucciones entre llaves
({ y }).
• A un conjunto de instrucciones contenidas dentro de un par de llaves se le llama bloque. Un bloque puede colocarse
en cualquier parte de un programa, en donde se pueda colocar una sola instrucción.
• El compilador atrapa los errores de sintaxis.
• Un error lógico tiene su efecto en tiempo de ejecución. Un error lógico fatal hace que un programa falle y termine
antes de tiempo. Un error lógico no fatal permite que un programa continúe ejecutándose, pero hace que el progra-
ma produzca resultados erróneos.
• Así como podemos colocar un bloque en cualquier parte en la que pueda colocarse una sola instrucción, también
podemos usar una instrucción vacía, que se representa colocando un punto y coma (;) en donde normalmente
estaría una instrucción.
Sección 4.7 Instrucción de repetición while
• La instrucción de repetición while permite al programador especificar que un programa debe repetir una acción,
mientras cierta condición siga siendo verdadera.
• El símbolo de fusión de UML combina dos flujos de actividad en uno.
• Los símbolos de decisión y de fusión pueden diferenciarse en base al número de flechas de transición “entrantes” y
“salientes”. Un símbolo de decisión tiene una flecha de transición que apunta hacia el rombo, y dos o más flechas de
transición que apuntan hacia fuera del rombo, para indicar las posibles transiciones desde ese punto. Cada flecha
de transición que apunta hacia fuera de un símbolo de decisión tiene una condición de guardia. Un símbolo de
fusión tiene dos o más flechas de transición que apuntan hacia el rombo, y sólo una flecha de transición que apunta
hacia fuera del rombo, para indicar que se fusionarán varios flujos de actividad para continuar con la actividad.
Ninguna de las flechas de transición asociadas con un símbolo de fusión tiene una condición de guardia.
Sección 4.8 Cómo formular algoritmos: repetición controlada por un contador
• La repetición controlada por un contador utiliza una variable llamada contador (o variable de control), para contro-
lar el número de veces que se ejecuta un conjunto de instrucciones.
• A la repetición controlada por contador se le conoce comúnmente como repetición definida, ya que el número de
repeticiones se conoce desde antes que empiece a ejecutarse el ciclo.
• Un total es una variable que se utiliza para acumular la suma de varios valores. Por lo general, las variables que se
utilizan para almacenar totales se inicializan en cero antes de usarlas en un programa.
• La declaración de una variable local debe aparecer antes de usarla en el método en el que está declarada. Una variable
local no puede utilizarse fuera del método en el que se declaró.
• Al dividir dos enteros se produce una división entera; la parte fraccionaria del cálculo se trunca.
Sección 4.9 Cómo formular algoritmos: repetición controlada por un centinela
• En la repetición controlada por un centinela, se utiliza un valor especial, conocido como valor centinela (valor de
señal, valor de prueba o valor de bandera) para indicar el “fin de la entrada de datos”.
• Debe elegirse un valor centinela que no pueda confundirse con un valor de entrada aceptable.
• El método de refinamiento de arriba a abajo, paso a paso, es esencial para el desarrollo de programas bien estructu-
rados.
• Por lo general, la división entre cero es un error lógico que, si no se detecta, hace que el programa falle o que pro-
duzca resultados inválidos.
• Para realizar un cálculo de punto flotante con valores enteros, convierta uno de los enteros al tipo double. El uso de
un operador de conversión de tipos de esta forma se denomina conversión explícita.
• Java sabe cómo evaluar sólo las expresiones aritméticas en las que los tipos de los operandos son idénticos. Para ase-
gurar que los operandos sean del mismo tipo, Java realiza una operación conocida como promoción (o conversión
implícita) sobre los operandos seleccionados. En una expresión que contiene valores de los tipos int y double, los
valores int se promueven a valores double para usarlos en la expresión.
• Hay operadores de conversión de tipos disponibles para cualquier tipo. El operador de conversión de tipos se forma
mediante la colocación de paréntesis alrededor del nombre de un tipo. Este operador es unario.
Sección 4.11 Operadores de asignación compuestos
• Java cuenta con varios operadores de asignación compuestos para abreviar las expresiones de asignación. Cualquier
instrucción de la forma
variable = variable operador expresión;
en donde operador es uno de los operadores binarios +, -, *, / o %, puede escribirse en la forma
variable operador= expresión;
• El operador += suma el valor de la expresión que está a la derecha del operador, con el valor de la variable que está a
la izquierda del operador, y almacena el resultado en la variable que está a la izquierda del operador.
Sección 4.12 Operadores de incremento y decremento
• Java cuenta con dos operadores unarios para sumar 1, o restar 1, al valor de una variable numérica. Éstos son el
operador de incremento unario, ++, y el operador de decremento unario, --.
• Un operador de incremento o decremento que se coloca antes de una variable es el operador de preincremento o
predecremento, respectivamente. Un operador de incremento o decremento que se coloca después de una variable
es el operador de postincremento o postdecremento, respectivamente.
• El proceso de usar el operador de preincremento o predecremento para sumar o restar 1 se conoce como preincre-
mentar o predecrementar, respectivamente.
• Al preincrementar o predecrementar una variable, ésta se incrementa o decrementa por 1, y después se utiliza el
nuevo valor de la variable en la expresión en la que aparece.
• El proceso de usar el operador de postincremento o postdecremento para sumar o restar 1 se conoce como postin-
crementar o postdecrementar, respectivamente.
• Al postincrementar o postdecrementar una variable, el valor actual de ésta se utiliza en la expresión en la que aparece,
y después el valor de la variable se incrementa o decrementa por 1.
• Cuando se incrementa o decrementa una variable en una instrucción por sí sola, las formas de preincremento y
postincremento tienen el mismo efecto, y las formas de predecremento y postdecremento tienen el mismo efecto.
Sección 4.13 Tipos primitivos
• Java requiere que todas las variables tengan un tipo. Por ende, Java se conoce como un lenguaje fuertemente tipifi-
cado.
• Como los diseñadores de Java desean que sea lo más portable posible, utilizan estándares reconocidos internacional-
mente para los formatos de caracteres (Unicode) y números de punto flotante (IEEE 754).
Sección 4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples
• El sistema de coordenadas de Java proporciona un esquema para identificar cada punto en la pantalla. De manera
predeterminada, la esquina superior izquierda de un componente de la GUI tiene las coordenadas (0, 0).
Resumen 153
154 Capítulo 4 Instrucciones de control: parte 1
• Un par de coordenadas se compone de una coordenada x (la coordenada horizontal) y una coordenada y (la coorde-
nada vertical). La coordenada x es la ubicación horizontal que avanza de izquierda a derecha. La coordenada y es la
ubicación vertical que avanza de arriba hacia abajo.
• El eje x describe a todas las coordenadas horizontales, y el eje y a todas las coordenadas verticales.
• Las unidades de las coordenadas se miden en píxeles. Un píxel es la unidad más pequeña de resolución de una pan-
talla.
• La clase Graphics (del paquete java.awt) proporciona varios métodos para dibujar texto y figuras en la pantalla.
• La clase JPanel (del paquete javax.swing) proporciona un área en la que un programa puede hacer dibujos.
• La palabra clave extends indica que una clase hereda de otra clase. La nueva clase empieza con los miembros exis-
tentes (datos y métodos) de la clase existente.
• La clase a partir de la cual la nueva clase hereda se conoce como la superclase, y la nueva clase se llama subclase.
• Todo objeto JPanel tiene un método paintComponent, que el sistema llama automáticamente cada vez que necesita
mostrar el objeto JPanel: cuando se muestra un JPanel por primera vez en la pantalla, cuando una ventana en la
pantalla lo cubre y luego lo descubre, y cuando se cambia el tamaño de la ventana en la que aparece este objeto.
• El método paintComponent requiere un argumento (un objeto Graphics), que el sistema proporciona por usted
cuando llama a paintComponent.
• La primera instrucción en cualquier método paintComponent que usted vaya a crear debe ser siempre
super.paintComponent( g );
Esto asegura que el panel se despliegue de manera apropiada en la pantalla, antes de empezar a dibujar en él.
• Los métodos getWidth y getHeight de JPanel devuelven la anchura y la altura de un objeto JPanel, respectiva-
mente.
• El método drawLine de Graphics dibuja una línea entre dos puntos representados por sus cuatro argumentos. Los
primeros dos argumentos son las coordenadas x y y para un punto final de la línea, y los últimos dos argumentos son
las coordenadas para el otro punto final de la línea.
• Para mostrar un objeto JPanel en la pantalla, debe colocarlo en una ventana. Para crear una ventana, utilice un
objeto de la clase JFrame, del paquete javax.swing.
• El método setDefaultCloseOperation de JFrame con el argumento JFrame.EXIT_ON_CLOSE indica que la apli-
cación debe terminar cuando el usuario cierre la ventana.
• El método add de JFrame adjunta un componente de la GUI a un objeto JFrame.
• El método setSize de JFrame establece la anchura y la altura del objeto JFrame.
Terminología
--, operador
?:, operador
++, operador
+=, operador
acción
actividad (en UML)
add, método de la clase JFrame (GUI)
algoritmo
anidamiento de estructuras de control
apilamiento de estructuras de control
bloque
boolean, expresión
boolean, tipo primitivo
ciclo
ciclo infinito
cima
círculo relleno (en UML)
circunferencia (en UML)
condición de continuación de ciclo
condición de guardia (en UML)
contador
contador de ciclo
control del programa
conversión explícita
conversión implícita
coordenada horizontal (GUI)
coordenada vertical (GUI)
coordenada x
coordenada y
cuerpo de un ciclo
decisión
diagrama de actividad (en UML)
división entera
drawLine, método de la clase Graphics (GUI)
eje x
eje y
ejecución secuencial
error de sintaxis
error fatal
error lógico
error lógico fatal
error lógico no fatal
estado de acción (en UML)
estado final (en UML)
estado inicial (en UML)
estructura de repetición
estructura de secuencia
estructura de selección
expresión condicional
expresión de acción (en UML)
extends
false
flecha de transición (en UML)
flujo de trabajo
getHeight, método de la clase JPanel (GUI)
getWidth, método de la clase JPanel (GUI)
goto, instrucción
Graphics, clase (GUI)
heredar de una clase existente
if, instrucción de selección simple
if...else, instrucción de selección doble
inicialización
instrucción de ciclo
instrucción de control
instrucción de repetición
instrucción de selección
instrucción de selección doble
instrucción de selección múltiple
instrucción de selección simple
instrucciones de control anidadas
instrucciones de control apiladas
instrucciones de control de una sola entrada/una sola
salida
instrucciones if...else anidadas
iteración
JComponent, clase (GUI)
JFrame, clase (GUI)
JPanel, clase (GUI)
lenguaje fuertemente tipificado
línea punteada (en UML)
modelo de programación acción/decisión
nota (en UML)
operador condicional (?:)
operador de asignación compuesto
operador de asignación compuesto de suma (+=)
operador de conversión de tipos, (double)
operador de conversión de tipos, (tipo)
operador de conversión de tipos unario
operador de decremento (--)
operador de incremento (++)
operador de multiplicación
operador de postdecremento
operador de postincremento
operador de predecremento
operador de preincremento
operador ternario
operadores de asignación compuestos aritméticos: +=, -=,
*=, /= y %=
orden en el que deben ejecutarse las acciones
paintComponent, método de la clase JComponent (GUI)
pequeño círculo (en UML)
píxel (GUI)
postdecrementar una variable
postincrementar una variable
predecrementar una variable
preincrementar una variable
primer refinamiento
problema del else suelto
procedimiento para resolver un problema
programación estructurada
promoción
refinamiento de arriba a abajo, paso a paso
repetición
repetición controlada por centinela
repetición controlada por un contador
repetición definida
repetición indefinida
rombo (en UML)
segundo refinamiento
setDefaultCloseOperation, método de la clase JFrame
(GUI)
setSize, método de la clase JFrame (GUI)
seudocódigo
símbolo de decisión (en UML)
símbolo de estado de acción (en UML)
símbolo de fusión (en UML)
sistema de coordenadas (GUI)
tipos primitivos
total
transferencia de control
transición (en UML)
true
truncar la parte fraccionaria de un cálculo
valor centinela
valor de bandera
valor de prueba
valor de señal
variable de control
while, instrucción de repetición
Ejercicios de autoevaluación
4.1 Complete los siguientes enunciados:
a) Todos los programas pueden escribirse en términos de tres tipos de estructuras de control: __________,
__________ y __________.
b) La instrucción __________ se utiliza para ejecutar una acción cuando una condición es verdadera, y otra
acción cuando esa condición es falsa.
Ejercicios de autoevaluación 155
156 Capítulo 4 Instrucciones de control: parte 1
c) Al proceso de repetir un conjunto de instrucciones un número específico de veces se le llama repetición
__________.
d) Cuando no se sabe de antemano cuántas veces se repetirá un conjunto de instrucciones, se puede usar un
valor __________ para terminar la repetición.
e) La estructura __________ está integrada en Java; de manera predeterminada, las instrucciones se ejecutan
en el orden en el que aparecen.
f) Todas las variables de instancia de los tipos char, byte, short, int, long, float y double reciben el valor
__________ de manera predeterminada.
g) Java es un lenguaje __________; requiere que todas las variables tengan un tipo.
h) Si el operador de incremento se __________ de una variable, ésta se incrementa en 1 primero, y después
su nuevo valor se utiliza en la expresión.
4.2 Conteste con verdadero o falso a cada una de las siguientes proposiciones; en caso de ser falso, explique
por qué.
a) Un algoritmo es un procedimiento para resolver un problema en términos de las acciones a ejecutar y el
orden en el que se ejecutan.
b) Un conjunto de instrucciones contenidas dentro de un par de paréntesis se denomina bloque.
c) Una instrucción de selección especifica que una acción se repetirá, mientras cierta condición siga siendo
verdadera.
d) Una instrucción de control anidada aparece en el cuerpo de otra instrucción de control.
e) Java cuenta con los operadores de asignación compuestos aritméticos +=, -=, *=, /= y %= para abreviar las
expresiones de asignación.
f) Los tipos primitivos (boolean, char, byte, short, int, long, float y double) son portables sólo en las
plataformas Windows.
g) Al proceso de especificar el orden en el que se ejecutan las instrucciones (acciones) en un programa se deno-
mina control del programa.
h) El operador de conversión de tipos unario (double) crea una copia entera temporal de su operando.
i) Las variables de instancia de tipo boolean reciben el valor true de manera predeterminada.
j) El seudocódigo ayuda a un programador a idear un programa, antes de tratar de escribirlo en un lenguaje
de programación.
4.3 Escriba cuatro instrucciones distintas en Java, en donde cada una sume 1 a la variable entera x.
4.4 Escriba instrucciones en Java para realizar cada una de las siguientes tareas:
a) Asignar la suma de x e y a z, e incrementar x en 1 después del cálculo. Use sólo una instrucción.
b) Evaluar si la variable cuenta es mayor que 10. De ser así, imprimir "Cuenta es mayor que 10".
c) Decrementar la variable x en 1, luego restarla a la variable total. Use sólo una instrucción.
d) Calcular el residuo después de dividir q entre divisor, y asignar el resultado a q. Escriba esta instrucción
de dos maneras distintas.
4.5 Escriba una instrucción en Java para realizar cada una de las siguientes tareas:
a) Declarar las variables suma y x como de tipo int.
b) Asignar 1 a la variable x.
c) Asignar 0 a la variable suma.
d) Sumar la variable x a suma y asignar el resultado a la variable suma.
e) Imprimir la cadena "La suma es: ", seguida del valor de la variable suma.
4.6 Combine las instrucciones que escribió en el ejercicio 4.5 para formar una aplicación en Java que calcule e
imprima la suma de los enteros del 1 al 10. Use una instrucción while para iterar a través de las instrucciones de cálculo
e incremento. El ciclo debe terminar cuando el valor de x se vuelva 11.
4.7 Determine el valor de las variables en la siguiente instrucción, después de realizar el cálculo. Suponga que,
cuando se empieza a ejecutar la instrucción, todas las variables son de tipo int y tienen el valor 5.
producto *= x++;
4.8 Identifique y corrija los errores en cada uno de los siguientes fragmentos de código:
a) while ( c <= 5 )
{
producto *= c;
++c;
b) if ( genero == 1 )
System.out.println( "Mujer" );
else;
System.out.println( "Hombre" );
4.9 ¿Qué está mal en la siguiente instrucción while?
while ( z >= 0 )
suma += z;
Respuestas a los ejercicios de autoevaluación
4.1 a) secuencia, selección, repetición. b) if...else. c) controlada por contador (o definida). d) centinela, de
señal, de prueba o de bandera. e) secuencia. f) 0 (cero). g) fuertemente tipificado. h) coloca antes.
4.2 a) Verdadero. b) Falso. Un conjunto de instrucciones contenidas dentro de un par de llaves ({ y }) se
denomina bloque. c) Falso. Una instrucción de repetición especifica que una acción se repetirá mientras que cierta
condición siga siendo verdadera. d) Verdadero. e) Verdadero. f) Falso. Los tipos primitivos (boolean, char, byte,
short, int, long, float y double) son portables a través de todas las plataformas de computadora que soportan Java.
g) Verdadero. h) Falso. El operador de conversión de tipos unario (double) crea una copia temporal de punto flotante
de su operando. i) Falso. Las variables de instancia de tipo boolean reciben el valor false de manera predeterminada.
j) Verdadero.
4.3 x = x + 1;
x += 1;
++x;
x++;
4.4 a) z = x++ + y;
b) if ( cuenta > 10 )
System.out.println( "Cuenta es mayor que 10" );
c) total -= --x;
d) q %= divisor;
q = q % divisor;
4.5 a) int suma, x;
b) x = 1;
c) suma = 0;
d) suma += x; o suma = suma + x;
e) System.out.printf( "La suma es: %dn", suma );
4.6 El programa se muestra a continuación:
1 // Calcula la suma de los enteros del 1 al 10
2 public class Calcular
3 {
4 public static void main( String args[] )
5 {
6 int suma;
7 int x;
8
9 x = 1; // inicializa x en 1 para contar
10 suma = 0; // inicializa suma en 0 para el total
11
12 while ( x <= 10 ) // mientras que x sea menor o igual que 10
13 {
14 suma += x; // suma x a suma
15 ++x; // incrementa x
Respuestas a los ejercicios de autoevaluación 157
158 Capítulo 4 Instrucciones de control: parte 1
16 } // fin de while
17
18 System.out.printf( "La suma es: %dn", suma );
19 } // fin de main
20
21 } // fin de la clase Calcular
La suma es: 55
4.7 producto = 25, x = 6
4.8 a) Error: falta la llave derecha de cierre del cuerpo de la instrucción while.
Corrección: Agregar una llave derecha de cierre después de la instrucción ++c;.
b) Error: El punto y coma después de else produce un error lógico. La segunda instrucción de salida siempre
se ejecutará.
Corrección: Quitar el punto y coma después de else.
4.9 El valor de la variable z nunca se cambia en la instrucción while. Por lo tanto, si la condición de continuación
de ciclo ( z >= 0 ) es verdadera, se crea un ciclo infinito. Para evitar que ocurra un ciclo infinito, z debe decrementar-
se de manera que eventualmente se vuelva menor que 0.
Ejercicios
4.10 Compare y contraste la instrucción if de selección simple y la instrucción de repetición while. ¿Cuál es la
similitud en las dos instrucciones? ¿Cuál es su diferencia?
4.11 Explique lo que ocurre cuando un programa en Java trata de dividir un entero entre otro. ¿Qué ocurre con la
parte fraccionaria del cálculo? ¿Cómo puede un programador evitar ese resultado?
4.12 Describa las dos formas en las que pueden combinarse las instrucciones de control.
4.13 ¿Qué tipo de repetición sería apropiada para calcular la suma de los primeros 100 enteros positivos? ¿Qué tipo
de repetición sería apropiada para calcular la suma de un número arbitrario de enteros positivos? Describa brevemente
cómo podría realizarse cada una de estas tareas.
4.14 ¿Cuál es la diferencia entre preincrementar y postincrementar una variable?
4.15 Identifique y corrija los errores en cada uno de los siguientes fragmentos de código. [Nota: puede haber más de
un error en cada fragmento de código].
a) if ( edad >= 65 );
System.out.println( "Edad es mayor o igual que 65" );
else
System.out.println( "Edad es menor que 65 )";
b) int x = 1, total;
while ( x <= 10 )
{
total += x;
++x;
}
c) while ( x <= 100 )
total += x;
++x;
d) while ( y > 0 )
{
System.out.println( y );
++y;
4.16 ¿Qué es lo que imprime el siguiente programa?
1 public class Misterio
2 {
3 public static void main( String args[] )
4 {
5 int y;
6 int x = 1;
7 int total = 0;
8
9 while ( x <= 10 )
10 {
11 y = x * x;
12 System.out.println( y );
13 total += y;
14 ++x;
15 } // fin de while
16
17 System.out.printf( "El total es %dn", total );
18 } // fin de main
19
20 } // fin de la clase Misterio
Para los ejercicios 4.17 a 4.20, realice cada uno de los siguientes pasos:
a) Lea el enunciado del problema.
b) Formule el algoritmo utilizando seudocódigo y el proceso de refinamiento de arriba a abajo, paso a paso.
c) Escriba un programa en Java.
d) Pruebe, depure y ejecute el programa en Java.
e) Procese tres conjuntos completos de datos.
4.17 Los conductores se preocupan acerca del kilometraje de sus automóviles. Un conductor ha llevado el registro de
varios reabastecimientos de gasolina, registrando los kilómetros conducidos y los litros usados en cada reabastecimiento.
Desarrolle una aplicación en Java que reciba como entrada los kilómetros conducidos y los litros usados (ambos como
enteros) por cada reabastecimiento. El programa debe calcular y mostrar los kilómetros por litro obtenidos en cada
reabastecimiento, y debe imprimir el total de kilómetros por litro obtenidos en todos los reabastecimientos hasta este
punto. Todos los cálculos del promedio deben producir resultados en números de punto flotante. Use la clase Scanner
y la repetición controlada por centinela para obtener los datos del usuario.
4.18 Desarrolle una aplicación en Java que determine si alguno de los clientes de una tienda de departamentos se ha
excedido del límite de crédito en una cuenta. Para cada cliente se tienen los siguientes datos:
a) El número de cuenta.
b) El saldo al inicio del mes.
c) El total de todos los artículos cargados por el cliente en el mes.
d) El total de todos los créditos aplicados a la cuenta del cliente en el mes.
e) El límite de crédito permitido.
El programa debe recibir como entrada cada uno de estos datos en forma de números enteros, debe calcular el nuevo
saldo (= saldo inicial + cargos – créditos), mostrar el nuevo balance y determinar si éste excede el límite de crédito del
cliente. Para los clientes cuyo límite de crédito sea excedido, el programa debe mostrar el mensaje "Se excedió el
límite de su crédito".
4.19 Una empresa grande paga a sus vendedores mediante comisiones. Los vendedores reciben $200 por semana,
más el 9% de sus ventas brutas durante esa semana. Por ejemplo, un vendedor que vende $5000 de mercancía en una
semana, recibe $200 más el 9% de 5000, o un total de $650. Usted acaba de recibir una lista de los artículos vendidos
por cada vendedor. Los valores de estos artículos son los siguientes:
Artículo Valor
1 239.99
2 129.75
3 99.95
4 350.89
Ejercicios 159
160 Capítulo 4 Instrucciones de control: parte 1
Desarrolle una aplicación en Java que reciba como entrada los artículos vendidos por un vendedor durante la última
semana, y que calcule y muestre los ingresos de ese vendedor. No hay límite en cuanto al número de artículos que un
vendedor puede vender.
4.20 Desarrolle una aplicación en Java que determine el sueldo bruto para cada uno de tres empleados. La empresa
paga la cuota normal en las primeras 40 horas de trabajo de cada empleado, y paga cuota y media en todas las horas
trabajadas que excedan de 40. Usted recibe una lista de los empleados de la empresa, el número de horas que trabajó
cada empleado la semana pasada y la tarifa por horas de cada empleado. Su programa debe recibir como entrada esta
información para cada empleado, debe determinar y mostrar el sueldo bruto de cada empleado. Utilice la clase Scanner
para introducir los datos.
4.21 El proceso de encontrar el valor más grande (es decir, el máximo de un grupo de valores) se utiliza frecuen-
temente en aplicaciones de computadora. Por ejemplo, un programa para determinar el ganador de un concurso de
ventas recibe como entrada el número de unidades vendidas por cada vendedor. El vendedor que haya vendido más
unidades es el que gana el concurso. Escriba un programa en seudocódigo y después una aplicación en Java que reciba
como entrada una serie de 10 números enteros, y que determine e imprima el mayor de los números. Su programa debe
utilizar cuando menos las siguientes tres variables:
a) contador: un contador para contar hasta 10 (es decir, para llevar el registro de cuántos números se han
introducido, y para determinar cuando se hayan procesado los 10 números).
b) numero: el entero más reciente introducido por el usuario.
c) mayor: el número más grande encontrado hasta ahora.
4.22 Escriba una aplicación en Java que utilice ciclos para imprimir la siguiente tabla de valores:
N
1
2
3
4
5
10*N
10
20
30
40
50
100*N
100
200
300
400
500
1000*N
1000
2000
3000
4000
5000
4.23 Utilizando una metodología similar a la del ejercicio 4.21, encuentre los dos valores más grandes de los 10 que
se introdujeron. [Nota: puede introducir cada número sólo una vez].
4.24 Modifique el programa de la figura 4.12 para validar sus entradas. Para cualquier entrada, si el valor introducido
es distinto de 1 o 2, debe seguir iterando hasta que el usuario introduzca un valor correcto.
4.25 ¿Qué es lo que imprime el siguiente programa?
1 public class Misterio2
2 {
3 public static void main( String args[] )
4 {
5 int cuenta = 1;
6
7 while ( cuenta <= 10 )
8 {
9 System.out.println( cuenta % 2 == 1 ? "****" : "++++++++" );
10 ++cuenta;
11 } // fin de while
12 } // fin de main
13
14 } // fin de la clase Misterio2
4.26 ¿Qué es lo que imprime el siguiente programa?
1 public class Misterio3
2 {
3 public static void main( String args[] )
4 {
5 int fila = 10;
6 int columna;
7
8 while ( fila >= 1 )
9 {
10 columna = 1;
11
12 while ( columna <= 10 )
13 {
14 System.out.print( fila % 2 == 1 ? "<" : ">" );
15 ++columna;
16 } // fin de while
17
18 --fila;
19 System.out.println();
20 } // fin de while
21 } // fin de main
22
23 } // fin de la clase Misterio3
4.27 (Problema del else suelto) Determine la salida de cada uno de los siguientes conjuntos de código, cuando x es
9 y y es 11, y cuando x es 11 y y es 9. Observe que el compilador ignora la sangría en un programa en Java. Además, el
compilador de Java siempre asocia un else con el if que le precede inmediatamente, a menos que se le indique de otra
forma mediante la colocación de llaves ({}). A primera vista, el programador tal vez no esté seguro de cuál if correspon-
de a cuál else; esta situación se conoce como el “problema del else suelto”. Hemos eliminado la sangría del siguiente
código para hacer el problema más retador. [Sugerencia: aplique las convenciones de sangría que ha aprendido].
a) if ( x < 10 )
if ( y > 10 )
System.out.println( "*****" );
else
System.out.println( "#####" );
System.out.println( "$$$$$" );
b) if ( x < 10 )
{
if ( y > 10 )
System.out.println( "*****" );
}
else
{
System.out.println( "#####" );
System.out.println( "$$$$$" );
}
4.28 (Otro problema de else suelto) Modifique el código dado para producir la salida que se muestra en cada parte
del problema. Utilice las técnicas de sangría apropiadas. No haga modificaciones en el código, sólo inserte llaves o
modifique la sangría del código. El compilador ignora la sangría en un programa en Java. Hemos eliminado la sangría
en el código dado, para hacer el problema más retador. [Nota: es posible que no se requieran modificaciones en algunas
de las partes].
if ( y == 8 )
if ( x == 5 )
System.out.println( "@@@@@" );
else
System.out.println( "#####" );
System.out.println( "$$$$$" );
System.out.println( "&&&&&" );
Ejercicios 161
162 Capítulo 4 Instrucciones de control: parte 1
a) Suponiendo que x = 5 y y = 8, se produce la siguiente salida:
@@@@@
$$$$$
&&&&&
b) Suponiendo que x = 5 y y = 8, se produce la siguiente salida:
@@@@@
c) Suponiendo que x = 5 y y = 8, se produce la siguiente salida:
@@@@@
&&&&&
d) Suponiendo que x = 5 y y = 7, se produce la siguiente salida. [Nota: las tres últimas instrucciones de salida
después del else forman parte de un bloque.]
#####
$$$$$
&&&&&
4.29 Escriba una aplicación que pida al usuario que introduzca el tamaño del lado de un cuadrado y que muestre un
cuadrado hueco de ese tamaño, compuesto de asteriscos. Su programa debe funcionar con cuadrados que tengan lados
de todas las longitudes entre 1 y 20.
4.30 (Palíndromos) Un palíndromo es una secuencia de caracteres que se lee igual al derecho y al revés. Por ejemplo,
cada uno de los siguientes enteros de cinco dígitos es un palíndromo: 12321, 55555, 45554 y 11611. Escriba una
aplicación que lea un entero de cinco dígitos y determine si es un palíndromo. Si el número no es de cinco dígitos, el
programa debe mostrar un mensaje de error y permitir al usuario que introduzca un nuevo valor.
4.31 Escriba una aplicación que reciba como entrada un entero que contenga sólo 0s y 1s (es decir, un entero bina-
rio), y que imprima su equivalente decimal. [Sugerencia: use los operadores residuo y división para elegir los dígitos del
número binario uno a la vez, de derecha a izquierda. En el sistema numérico decimal, el dígito más a la derecha tiene
un valor posicional de 1 y el siguiente dígito a la izquierda tiene un valor posicional de 10, después 100, después 1000,
etcétera. El número decimal 234 puede interpretarse como 4 * 1 + 3 * 10 + 2 * 100. En el sistema numérico binario, el
dígito más a la derecha tiene un valor posicional de 1, el siguiente dígito a la izquierda tiene un valor posicional de 2,
luego 4, luego 8, etcétera. El equivalente decimal del número binario 1101 es 1 * 1 + 0 * 2 + 1 * 4 + 1 * 8, o 1 + 0 + 4
+ 8, o 13].
4.32 Escriba una aplicación que utilice sólo las instrucciones de salida
System.out.print( "* " );
System.out.print( " " );
System.out.println();
para mostrar el patrón de tablero de damas que se muestra a continuación. Observe que una llamada al método Sys-
tem.out.println sin argumentos hace que el programa imprima un solo carácter de nueva línea. [Sugerencia: se
requieren estructuras de repetición].
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
4.33 Escriba una aplicación que muestre en la ventana de comandos los múltiplos del entero 2 (es decir, 2, 4, 8, 16,
32, 64, etcétera). Su ciclo no debe terminar (es decir, debe crear un ciclo infinito). ¿Qué ocurre cuando ejecuta este
programa?
4.34 ¿Qué está mal en la siguiente instrucción? Proporcione la instrucción correcta para sumar uno a la suma
de x e y.
System.out.println( ++(x + y) );
4.35 Escriba una aplicación que lea tres valores distintos de cero introducidos por el usuario, y que determine e
imprima si podrían representar los lados de un triángulo.
4.36 Escriba una aplicación que lea tres enteros distintos de cero, determine e imprima si estos enteros podrían
representar los lados de un triángulo rectángulo.
4.37 Una compañía desea transmitir datos a través del teléfono, pero le preocupa que sus teléfonos puedan estar
intervenidos. Le ha pedido a usted que escriba un programa que cifre sus datos, de manera que éstos puedan trans-
mitirse con más seguridad. Todos los datos se transmiten como enteros de cuatro dígitos. Su aplicación debe leer un
entero de cuatro dígitos introducido por el usuario y cifrarlo de la siguiente manera: reemplace cada dígito con el
resultado de sumar 7 al dígito y obtener el residuo después de dividir el nuevo valor entre 10. Luego intercambie
el primer dígito con el tercero, e intercambie el segundo dígito con el cuarto. Después imprima el entero cifrado.
Escriba una aplicación separada que reciba como entrada un entero de cuatro dígitos cifrado, y que lo descifre para
formar el número original.
4.38 El factorial de un entero n no negativo se escribe como n! y se define de la siguiente manera:
n! = n · (n – 1) · (n – 2) · ... · 1 (para valores de n mayores o iguales a 1)
y
n! = 1 (para n = 0)
Por ejemplo, 5! = 5 · 4 · 3 · 2 · 1, que es 120.
a) Escriba una aplicación que lea un entero no negativo, y calcule e imprima su factorial.
b) Escriba una aplicación que estime el valor de la constante matemática e, utilizando la fórmula
e 1 1
1!
----
-
1
2!
----
- 1
3!
----
-
+ + + +
=
c) Escriba una aplicación que calcule el valor de ex, utilizando la fórmula
e
x
1 x
1!
----
-
x
2
2!
----
- x
3
3!
----
-
+ + + +
=
Ejercicios 163
Instrucciones
de control:
parte 2
OBJETIVO S
En este capítulo aprenderá a:
Conocer los fundamentos acerca de la repetición controlada
por un contador.
Utilizar las instrucciones de repetición for y do...while para
ejecutar instrucciones de manera repetitiva en un programa.
Comprender la selección múltiple utilizando la instrucción de
selección switch.
Utilizar las instrucciones de control de programa break y
continue para alterar el flujo de control.
Utilizar los operadores lógicos para formar expresiones
condicionales complejas en instrucciones de control.
Q
Q
Q
Q
Q
No todo lo que puede
contarse cuenta, y no todo lo
que cuenta puede contarse.
—Albert Einstein
¿Quién puede controlar su
destino?
—William Shakespeare
La llave usada siempre es
brillante.
—Benjamin Franklin
La inteligencia... es la
facultad de hacer objetos
artificiales, especialmente
herramientas para hacer
herramientas.
—Henri Bergson
Cada ventaja en el pasado
se juzga a la luz de la
cuestión final.
—Demóstenes
5
5.1 Introducción
El capítulo 4 nos introdujo a los tipos de bloques de construcción disponibles para solucionar problemas. Utiliza-
mos dichos bloques de construcción para emplear las técnicas, ya comprobadas, de la construcción de programas.
En este capítulo continuaremos nuestra presentación de la teoría y los principios de la programación estructurada,
presentando el resto de las instrucciones de control en Java. Las instrucciones de control que estudiaremos aquí y
las que vimos en el capítulo 4 son útiles para crear y manipular objetos.
En este capítulo demostraremos las instrucciones for, do...while y switch de Java. A través de una serie de
ejemplos cortos en los que utilizaremos las instrucciones while y for, exploraremos los fundamentos acerca
de la repetición controlada por contador. Dedicaremos una parte de este capítulo (y del capítulo 7) a expandir
la clase LibroCalificaciones que presentamos en los capítulos 3 y 4. En especial, crearemos una versión de la
clase LibroCalificaciones que utiliza una instrucción switch para contar el número de calificaciones equiva-
lentes de A, B, C, D y F, en un conjunto de calificaciones numéricas introducidas por el usuario. Presentaremos
las instrucciones de control de programa break y continue. Hablaremos sobre los operadores lógicos de Java,
que nos permiten utilizar expresiones condicionales más complejas en las instrucciones de control. Por último,
veremos un resumen de las instrucciones de control de Java y las técnicas ya probadas de solución de problemas
que presentamos en éste y en el capítulo 4.
5.2 Fundamentos de la repetición controlada por contador
Esta sección utiliza la instrucción de repetición while, presentada en el capítulo 4, para formalizar los elementos
requeridos y llevar a cabo la repetición controlada por contador. Este tipo de repetición requiere:
1. Una variable de control (o contador de ciclo).
2. El valor inicial de la variable de control.
3. El incremento (o decremento) con el que se modifica la variable de control cada vez que pasa por el
ciclo (lo que también se conoce como cada iteración del ciclo).
4. La condición de continuación de ciclo, que determina si el ciclo debe continuar o no.
Para ver estos elementos de la repetición controlada por contador, considere la aplicación de la figura 5.1, que
utiliza un ciclo para mostrar los números del 1 al 10. Observe que esta figura sólo contiene un método, main,
que realiza todo el trabajo de la clase. Para la mayoría de las aplicaciones, en los capítulos 3 y 4 hemos preferido el
uso de dos archivos separados: uno que declara una clase reutilizable (por ejemplo, Cuenta) y otro que instancia
uno o más objetos de esa clase (por ejemplo, PruebaCuenta) y demuestra su funcionalidad. Sin embargo, en
algunas ocasiones es más apropiado crear sólo una clase, cuyo método main ilustra en forma concisa un concepto
básico. A lo largo de este capítulo, utilizaremos varios ejemplos de una clase, como el de la figura 5.1, para demos-
trar la mecánica de las instrucciones de control de Java.
5.1 Introducción
5.2 Fundamentos de la repetición controlada por contador
5.3 Instrucción de repetición for
5.4 Ejemplos sobre el uso de la instrucción for
5.5 Instrucción de repetición do...while
5.6 Instrucción de selección múltiple switch
5.7 Instrucciones break y continue
5.8 Operadores lógicos
5.9 Resumen sobre programación estructurada
5.10 (Opcional) Ejemplo práctico de GUI y gráficos: dibujo de rectángulos y óvalos
5.11 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo identificar los estados y actividades de
los objetos
5.12 Conclusión
Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios
Pla
n
g
e
ne
r
a
l
5.2 Fundamentos de la repetición controlada por contador 165
166 Capítulo 5 Instrucciones de control: parte 2
En la figura 5.1, los elementos de la repetición controlada por contador se definen en las líneas 8, 10 y 13.
La línea 8 declara la variable de control (contador) como un int, reserva espacio para esta variable en memoria
y establece su valor inicial en 1. La variable contador también podría haberse declarado e inicializado con la
siguientes instrucciones de declaración y asignación de variables locales:
int contador; // declara contador
contador = 1; // inicializa contador en 1
La línea 12 muestra el valor de la variable de control contador durante cada iteración del ciclo. La línea 13
incrementa la variable de control en 1, para cada iteración del ciclo. La condición de continuación de ciclo en la
instrucción while (línea 10) evalúa si el valor de la variable de control es menor o igual que 10 (el valor final para
el que la condición es true). Observe que el programa ejecuta el cuerpo de este while aun y cuando la variable
de control sea 10. El ciclo termina cuando la variable de control es mayor a 10 (es decir, cuando contador se
convierte en 11).
Error común de programación 5.1
Debido a que los valores de punto flotante pueden ser aproximados, controlar los ciclos con variables de punto flotante
puede producir valores imprecisos del contador y pruebas de terminación imprecisas.
Tip para prevenir errores 5.1
Controle los ciclos de contador con enteros.
Buena práctica de programación 5.1
Coloque líneas en blanco por encima y debajo de las instrucciones de control de repetición y selección, y aplique
sangría a los cuerpos de las instrucciones para mejorar la legibilidad.
El programa de la figura 5.1 puede hacerse más conciso si inicializamos contador en 0 en la línea 8, y pre-
incrementamos contador en la condición while de la siguiente forma:
while ( ++contador <= 10 ) // condición de continuación de ciclo
System.out.printf( “%d “, contador );
1 // Fig. 5.1: ContadorWhile.java
2 // Repetición controlada con contador, con la instrucción de repetición while.
3
4 public class ContadorWhile
5 {
6 public static void main( String args[] )
7 {
8 int contador = 1; // declara e inicializa la variable de control
9
10 while ( contador <= 10 ) // condición de continuación de ciclo
11 {
12 System.out.printf( “%d “, contador );
13 ++contador; // incrementa la variable de control en 1
14 } // fin de while
15
16 System.out.println(); // imprime una nueva línea
17 } // fin de main
18 } // fin de la clase ContadorWhile
1 2 3 4 5 6 7 8 9 10
Figura 5.1 | Repetición controlada con contador, con la instrucción de repetición while.
5.3 Instrucción de repetición for 167
Este código ahorra una instrucción (y elimina la necesidad de usar llaves alrededor del cuerpo del ciclo), ya que la
condición de while realiza el incremento antes de evaluar la condición. (En la sección 4.12 vimos que la prece-
dencia de ++ es mayor que la de <=). La codificación en esta forma tan condensada requiere de práctica y puede
hacer que el código sea más difícil de leer, depurar, modificar y mantener, así que en general, es mejor evitarla.
Observación de ingeniería de software 5.1
“Mantener las cosas simples” es un buen consejo para la mayoría del código que usted escriba.
5.3 Instrucción de repetición for
La sección 5.2 presentó los aspectos esenciales de la repetición controlada por contador. La instrucción while
puede utilizarse para implementar cualquier ciclo controlado por un contador. Java también cuenta con la ins-
trucción de repetición for, que especifica los detalles de la repetición controlada por contador en una sola línea
de código. La figura 5.2 reimplementa la aplicación de la figura 5.1, usando la instrucción for.
El método main de la aplicación opera de la siguiente manera: cuando la instrucción for (líneas 10 y 11)
comienza a ejecutarse, la variable de control contador se declara e inicializa en 1 (en la sección 5.2 vimos que
los primeros dos elementos de la repetición controlada por un contador son la variable de control y su valor
inicial). A continuación, el programa verifica la condición de continuación de ciclo, contador <= 10, la cual se
encuentra entre los dos signos de punto y coma requeridos. Como el valor inicial de contador es 1, al principio
la condición es verdadera. Por lo tanto, la instrucción del cuerpo (línea 11) muestra el valor de la variable de
control contador, que es 1. Después de ejecutar el cuerpo del ciclo, el programa incrementa a contador en la
expresión contador++, la cual aparece a la derecha del segundo signo de punto y coma. Después, la prueba de
continuación de ciclo se ejecuta de nuevo para determinar si el programa debe continuar con la siguiente iteración
del ciclo. En este punto, el valor de la variable de control es 2, por lo que la condición sigue siendo verdadera (el
valor final no se excede); así, el programa ejecuta la instrucción del cuerpo otra vez (es decir, la siguiente iteración
del ciclo). Este proceso continúa hasta que se muestran en pantalla los números del 1 al 10 y el valor de contador
se vuelve 11, con lo cual falla la prueba de continuación de ciclo y termina la repetición (después de 10 repeticio-
nes del cuerpo del ciclo en la línea 11). Después, el programa ejecuta la primera instrucción después del for; en
este caso, la línea 13.
Observe que la figura 5.2 utiliza (en la línea 10) la condición de continuación de ciclo contador <= 10.
Si usted especificara por error contador < 10 como la condición, el ciclo sólo iteraría nueve veces. A este error
lógico común se le conoce como error por desplazamiento en 1.
1 // Fig. 5.2: ContadorFor.java
2 // Repetición controlada con contador, con la instrucción de repetición for.
3
4 public class ContadorFor
5 {
6 public static void main( String args[] )
7 {
8 // el encabezado de la instrucción for incluye la inicialización,
9 // la condición de continuación de ciclo y el incremento
10 for ( int contador = 1; contador <= 10; contador++ )
11 System.out.printf( "%d ", contador );
12
13 System.out.println(); // imprime una nueva línea
14 } // fin de main
15 } // fin de la clase ContadorFor
1 2 3 4 5 6 7 8 9 10
Figura 5.2 | Repetición controlada con contador, con la instrucción de repetición for.
168 Capítulo 5 Instrucciones de control: parte 2
Error común de programación 5.2
Utilizar un operador relacional incorrecto o un valor final incorrecto de un contador de ciclo en la condición de
continuación de ciclo de una instrucción de repetición puede producir un error por desplazamiento en 1.
Buena práctica de programación 5.2
Utilizar el valor final en la condición de una instrucción while o for con el operador relacional <= nos ayuda a evi-
tar los errores por desplazamiento en 1. Para un ciclo que imprime los valores del 1 al 10, la condición de continua-
ción de ciclo debe ser contador <= 10, en vez de contador < 10 (lo cual produce un error por desplazamiento en
uno) o contador <11 (que es correcto). Muchos programadores prefieren el llamado conteo con base cero, en el cual
para contar 10 veces, contador se inicializaría en cero y la prueba de continuación de ciclo sería contador < 10.
La figura 5.3 analiza con más detalle la instrucción for de la figura 5.2. A la primera línea del for (incluyen-
do la palabra clave for y todo lo que está entre paréntesis después de ésta), la línea 10 de la figura 5.2, se le llama
algunas veces encabezado de la instrucción for, o simplemente encabezado del for. Observe que el encabezado
del for “se encarga de todo”: especifica cada uno de los elementos necesarios para la repetición controlada por
un contador con una variable de control. Si hay más de una instrucción en el cuerpo del for, se requieren llaves
({ y }) para definir el cuerpo del ciclo.
Valor inicial de la
variable de control Condición de continuación
de ciclo
Incremento de la
variable de control
Palabra clave
for
Variable de
control
Separador de
punto y coma
requerido
Separador de
punto y coma
requerido
for ( int contador = 1; contador <= 10; contador++ )
Figura 5.3 | Componentes del encabezado de la instrucción for.
El formato general de la instrucción for es
for ( inicialización; condiciónDeContinuaciónDeCiclo; incremento )
instrucción
en donde la expresión inicialización nombra a la variable de control de ciclo y proporciona su valor inicial, la con-
diciónDeContinuaciónDeCiclo es la condición que determina si el ciclo debe seguir ejecutándose, y el incremento
modifica el valor de la variable de control (ya sea un incremento o un decremento), de manera que la condición
de continuación de ciclo se vuelva falsa en un momento dado. Los dos signos de punto y coma en el encabezado
del for son requeridos.
Error común de programación 5.3
Utilizar comas en vez de los dos signos de punto y coma requeridos en el encabezado de una instrucción for es un
error de sintaxis.
En la mayoría de los casos, la instrucción for puede representarse con una instrucción while equivalente,
de la siguiente manera:
inicialización;
while ( condiciónDeContinuaciónDeCiclo )
{
instrucción
incremento;
}
En la sección 5.7 veremos un caso para el cual no es posible representar una instrucción for con una instrucción
while equivalente.
Por lo general, las instrucciones for se utilizan para la repetición controlada por un contador, y las instruc-
ciones while se utilizan para la repetición controlada por un centinela. No obstante, while y for pueden utili-
zarse para cualquiera de los dos tipos de repetición.
Si la expresión de inicialización en el encabezado del for declara la variable de control (es decir, si el tipo de
la variable de control se especifica antes del nombre de la variable, como en la figura 5.2), la variable de control
puede utilizarse sólo en esa instrucción for; no existirá fuera de esta instrucción. Este uso restringido del nombre
de la variable de control se conoce como el alcance de la variable. El alcance de una variable define en dónde
puede utilizarse en un programa. Por ejemplo, una variable local sólo puede utilizarse en el método que declara
a esa variable, y sólo a partir del punto de declaración, hasta el final del método. En el capítulo 6, Métodos: un
análisis más detallado, veremos con detalle el concepto de alcance.
Error común de programación 5.4
Cuando se declara la variable de control de una instrucción for en la sección de inicialización del encabezado del
for, si se utiliza la variable de control fuera del cuerpo del for se produce un error de compilación.
Las tres expresiones en un encabezado for son opcionales. Si se omite la condiciónDeContinuaciónDeCiclo,
Java asume que esta condición siempre será verdadera, con lo cual se crea un ciclo infinito. Podríamos omitir
la expresión de inicialización si el programa inicializa la variable de control antes del ciclo. Podríamos omitir la
expresión de incremento si el programa calcula el incremento mediante instrucciones dentro del cuerpo del ciclo,
o si no se necesita un incremento. La expresión de incremento en un for actúa como si fuera una instrucción
independiente al final del cuerpo del for. Por lo tanto, las expresiones
contador = contador + 1
contador += 1
++contador
contador++
son expresiones de incremento equivalentes en una instrucción for. Muchos programadores prefieren conta-
dor++, ya que es concisa y además un ciclo for evalúa su expresión de incremento después de la ejecución de su
cuerpo. Por ende, la forma de postincremento parece más natural. En este caso, la variable que se incrementa no
aparece en una expresión más grande, por lo que los operadores de preincremento y postdecremento tienen en
realidad el mismo efecto.
Tip de rendimiento 5.1
Hay una ligera ventaja de rendimiento al utilizar el operador de preincremento, pero si elije el operador de postincre-
mento debido a que parece ser más natural (como en el encabezado de un for), los compiladores con optimización
generarán código byte de Java que utilice la forma más eficiente, de todas maneras.
Buena práctica de programación 5.3
En muchos casos, los operadores de preincremento y postincremento se utilizan para sumar 1 a una variable en una
instrucción por sí sola. En estos casos el efecto es idéntico, sólo que el operador de preincremento tiene una ligera
ventaja de rendimiento. Dado que el compilador por lo general optimiza el código que usted escribe para ayudarlo
a obtener el mejor rendimiento, puede usar cualquiera de los dos operadores (preincremento o postincremento) con el
que se sienta más cómodo en estas situaciones.
Error común de programación 5.5
Colocar un punto y coma inmediatamente a la derecha del paréntesis derecho del encabezado de un for convierte el
cuerpo de ese for en una instrucción vacía. Por lo general esto es un error lógico.
5.3 Instrucción de repetición for 169
170 Capítulo 5 Instrucciones de control: parte 2
Tip para prevenir errores 5.2
Los ciclos infinitos ocurren cuando la condición de continuación de ciclo en una instrucción de repetición nunca se
vuelve false. Para evitar esta situación en un ciclo controlado por un contador, debe asegurarse que la variable de
control se incremente (o decremente) durante cada iteración del ciclo. En un ciclo controlado por centinela, asegúrese
que el valor centinela se introduzca en algún momento dado.
Las porciones correspondientes a la inicialización, la condición de continuación de ciclo y el incremento de
una instrucción for pueden contener expresiones aritméticas. Por ejemplo, suponga que x = 2 y y = 10; si x y
y no se modifican en el cuerpo del ciclo, entonces la instrucción
for ( int j = x; j <= 4 * x * y; j += y / x )
es equivalente a la instrucción
for ( int j = 2; j <= 80; j += 5 )
El incremento de una instrucción for también puede ser negativo, en cuyo caso sería un decremento y el ciclo
contaría en orden descendente.
Si al principio la condición de continuación de ciclo es false, el programa no ejecutará el cuerpo de la ins-
trucción for, sino que la ejecución continuará con la instrucción que siga inmediatamente después del for.
Con frecuencia, los programas muestran en pantalla el valor de la variable de control o lo utilizan en cálculos
dentro del cuerpo del ciclo, pero este uso no es obligatorio. Por lo general, la variable de control se utiliza para
controlar la repetición sin que se le mencione dentro del cuerpo del for.
Tip para prevenir errores 5.3
Aunque el valor de la variable de control puede cambiarse en el cuerpo de un ciclo for, evite hacerlo, ya que esta
práctica puede llevarlo a cometer errores sutiles.
El diagrama de actividad de UML de la instrucción for es similar al de la instrucción while (figura 4.4).
La figura 5.4 muestra el diagrama de actividad de la instrucción for de la figura 5.2. El diagrama hace evidente que
la inicialización ocurre sólo una vez antes de evaluar la condición de continuación de ciclo por primera vez, y que el
incremento ocurre cada vez que se realiza una iteración, después de que se ejecuta la instrucción del cuerpo.
Determina si el ciclo
debe continuar System.out.printf( “%d ”, contador );
[contador > 10]
[contador <= 10]
int contador = 1
contador++
Muestra en pantalla
el valor del contador
Inicializa la
variable de control
Incrementa la
variable de control
Figura 5.4 | Diagrama de actividad de UML para la instrucción for de la figura 5.2.
5.4 Ejemplos sobre el uso de la instrucción for
Los siguientes ejemplos muestran técnicas para modificar la variable de control en una instrucción for. En cada
caso, escribimos el encabezado for apropiado. Observe el cambio en el operador relacional para los ciclos que
decrementan la variable de control.
a) Modificar la variable de control de 1 a 100 en incrementos de 1.
for ( int i = 1; i <= 100; i++ )
b) Modificar la variable de control de 100 a 1 en decrementos de 1.
for ( int i = 100; i >= 1; i-- )
c) Modificar la variable de control de 7 a 77 en incrementos de 7.
for ( int i = 7; i <= 77; i += 7 )
d) Modificar la variable de control de 20 a 2 en decrementos de 2.
for ( int i = 20; i >= 2; i -= 2 )
e) Modificar la variable de control con la siguiente secuencia de valores: 2, 5, 8, 11, 14, 17, 20.
for ( int i = 2; i <= 20; i += 3 )
f) Modificar la variable de control con la siguiente secuencia de valores: 99, 88, 77, 66, 55, 44, 33,
22, 11, 0.
for ( int i = 99; i >= 0; i -= 11 )
Error común de programación 5.6
No utilizar el operador relacional apropiado en la condición de continuación de un ciclo que cuente en forma
regresiva (como usar i <= 1 en lugar de i >= 1 en un ciclo que cuente en forma regresiva hasta llegar a 1) es
generalmente un error lógico.
Aplicación: sumar los enteros pares del 2 al 20
Ahora consideremos dos aplicaciones de ejemplo que demuestran usos simples de la instrucción for. La aplica-
ción de la figura 5.5 utiliza una instrucción for para sumar los enteros pares del 2 al 20 y guardar el resultado en
una variable int llamada total.
1 // Fig. 5.5: Suma.java
2 // Sumar enteros con la instrucción for.
3
4 public class Suma
5 {
6 public static void main( String args[] )
7 {
8 int total = 0; // inicializa el total
9
10 // total de los enteros pares del 2 al 20
11 for ( int numero = 2; numero <= 20; numero += 2 )
12 total += numero;
13
14 System.out.printf( “La suma es %dn”, total ); // muestra los resultados
15 } // fin de main
16 } // fin de la clase Suma
La suma es 110
Figura 5.5 | Sumar enteros con la instrucción for.
5.4 Ejemplos sobre el uso de la instrucción for 171
172 Capítulo 5 Instrucciones de control: parte 2
Las expresiones de inicialización e incremento pueden ser listas separadas por comas de expresiones que nos
permitan utilizar varias expresiones de inicialización, o varias expresiones de incremento. Por ejemplo, aunque
esto no se recomienda, el cuerpo de la instrucción for en las líneas 11 y 12 de la figura 5.5 podría mezclarse con
la porción del incremento del encabezado for mediante el uso de una coma, como se muestra a continuación:
for ( int numero = 2; numero <= 20; total += numero, numero += 2 )
; // instrucción vacía
Buena práctica de programación 5.4
Limite el tamaño de los encabezados de las instrucciones de control a una sola línea, si es posible.
Aplicación: cálculo del interés compuesto
La siguiente aplicación utiliza la instrucción for para calcular el interés compuesto. Considere el siguiente pro-
blema:
Una persona invierte $1000.00 en una cuenta de ahorro que produce el 5% de interés. Supo-
niendo que todo el interés se deposita en la cuenta, calcule e imprima el monto de dinero en
la cuenta al final de cada año, durante 10 años. Use la siguiente fórmula para determinar los
montos:
c = p(1 + r)n
en donde
p es el monto que se invirtió originalmente (es decir, el monto principal)
r es la tasa de interés anual (por ejemplo, use 0.05 para el 5%)
n es el número de años
c es la cantidad depositada al final del n-ésimo año.
Este problema implica el uso de un ciclo que realiza los cálculos indicados para cada uno de los 10 años que
el dinero permanece depositado. La solución es la aplicación que se muestra en la figura 5.6. Las líneas 8 a 10
en el método main declaran las variables double llamadas monto, principal y tasa, e inicializan principal
con 1000.0 y tasa con 0.05. Java trata a las constantes de punto flotante, como 1000.0 y 0.05, como de tipo
double. De manera similar, Java trata a las constantes de números enteros, como 7 y -22, como de tipo int.
La línea 13 imprime en pantalla los encabezados para las dos columnas de resultados de esta aplicación. La
primera columna muestra el año y la segunda, la cantidad depositada al final de ese año. Observe que utilizamos
el especificador de formato %20s para mostrar en pantalla el objeto String “Monto en deposito”. El entero
20 después del % y el carácter de conversión s indica que el valor a imprimir debe mostrarse con una anchura
de campo de 20; esto es, printf debe mostrar el valor con al menos 20 posiciones de caracteres. Si el valor a
imprimir tiene una anchura menor a 20 posiciones de caracteres (en este ejemplo son 17 caracteres), el valor se
justifica a la derecha en el campo de manera predeterminada. Si el valor anio a imprimir tuviera una anchura
mayor a cuatro posiciones de caracteres, la anchura del campo se extendería a la derecha para dar cabida a todo
el valor; esto desplazaría al campo monto a la derecha, con lo que se desacomodarían las columnas ordenadas de
nuestros resultados tabulares. Para indicar que los valores deben imprimirse justificados a la izquierda, sólo hay
que anteponer a la anchura de campo la bandera de formato de signo negativo (–).
La instrucción for (líneas 16 a 23) ejecuta su cuerpo 10 veces, con lo cual la variable de control anio varía
de 1 a 10, en incrementos de 1. Este ciclo termina cuando la variable de control anio se vuelve 11 (observe que
anio representa a la n en el enunciado del problema).
Las clases proporcionan métodos que realizan tareas comunes sobre los objetos. De hecho, la mayoría de los
métodos a llamar deben pertenecer a un objeto específico. Por ejemplo, para imprimir texto en la figura 5.6, la
línea 13 llama al método printf en el objeto System.out. Muchas clases también cuentan con métodos que
realizan tareas comunes y no requieren objetos. En la sección 3.9 vimos que a estos métodos se les llama static.
Por ejemplo, Java no incluye un operador de exponenciación, por lo que los diseñadores de la clase Math definie-
ron el método static llamado pow para elevar un valor a una potencia. Para llamar a un método static debe
especificar el nombre de la clase, seguido de un punto (.) y el nombre del método, como en
NombreClase.nombreMétodo ( argumentos )
En el capítulo 6 aprenderá a implementar métodos static en sus propias clases.
Utilizamos el método static pow de la clase Math para realizar el cálculo del interés compuesto en la figura
5.6. Math.pow(x, y) calcula el valor de x elevado a la y-ésima potencia. El método recibe dos argumentos double
y devuelve un valor double. La línea 19 realiza el cálculo c = p(1 + r)n, en donde c es monto, p es principal, r
es tasa y n es anio.
Después de cada cálculo, la línea 22 imprime en pantalla el año y el monto depositado al final de ese año.
El año se imprime en una anchura de campo de cuatro caracteres (según lo especificado por %4d). El monto se
imprime como un número de punto flotante con el especificador de formato %,20.2f. La bandera de formato
coma (,) indica que el valor debe imprimirse con un separador de agrupamiento. El separador que se utiliza real-
mente es específico de la configuración regional del usuario (es decir, el país). Por ejemplo, en los Estados Unidos,
el número se imprimirá usando comas para separar cada tres dígitos, y un punto decimal para separar la parte
fraccionaria del número, como en 1,234.45. El número 20 en la especificación de formato indica que el valor
debe imprimirse justificado a la derecha, con una anchura de campo de 20 caracteres. El .2 especifica la precisión del
número con formato; en este caso, el número se redondea a la centésima más cercana y se imprime con dos dígitos
a la derecha del punto decimal.
1 // Fig. 5.6: Interes.java
2 // Cálculo del interés compuesto con for.
3
4 public class Interes
5 {
6 public static void main( String args[] )
7 {
8 double monto; // Monto depositado al final de cada año
9 double principal = 1000.0; // monto inicial antes de los intereses
10 double tasa = 0.05; // tasa de interés
11
12 // muestra los encabezados
13 System.out.printf( "s%20sn", "Anio", "Monto en deposito" );
14
15 // calcula el monto en deposito para cada uno de diez años
16 for ( int anio = 1; anio <= 10; anio++ )
17 {
18 // calcula el nuevo monto para el año especificado
19 monto = principal * Math.pow( 1.0 + tasa, anio );
20
21 // muestra el año y el monto
22 System.out.printf( "%4d%,20.2fn", anio, monto );
23 } // fin de for
24 } // fin de main
25 } // fin de la clase Interes
Anio Monto en deposito
1 1,050.00
2 1,102.50
3 1,157.63
4 1,215.51
5 1,276.28
6 1,340.10
7 1,407.10
8 1,477.46
9 1,551.33
10 1,628.89
Figura 5.6 | Cálculo del interés compuesto con for.
5.4 Ejemplos sobre el uso de la instrucción for 173
174 Capítulo 5 Instrucciones de control: parte 2
En este ejemplo declaramos las variables monto, capital y tasa de tipo double. Estamos tratando con
partes fraccionales de dólares y, por ende, necesitamos un tipo que permita puntos decimales en sus valores. Por
desgracia, los números de punto flotante pueden provocar problemas. He aquí una sencilla explicación de lo que
puede salir mal al utilizar double (o float) para representar montos en dólares (suponiendo que los montos en
dólares se muestran con dos dígitos a la derecha del punto decimal): dos montos en dólares tipo double alma-
cenados en la máquina podrían ser 14.234 (que por lo general se redondea a 14.23 para fines de mostrarlo en
pantalla) y 18.673 (que por lo general se redondea a 18.67 para fines de mostrarlo en pantalla). Al sumar estos
montos, producen una suma interna de 32.907, que por lo general se redondea a 32.91 para fines de mostrarlo
en pantalla. Por lo tanto, sus resultados podrían aparecer como
14.23
+ 18.67
-------
32.91
pero una persona que sume los números individuales, como se muestran, esperaría que la suma fuera de 32.90.
¡Ya ha sido advertido!
Buena práctica de programación 5.5
No utilice variables de tipo double (o float) para realizar cálculos monetarios precisos. La imprecisión de los núme-
ros de punto flotante puede provocar errores. En los ejercicios usaremos enteros para realizar cálculos monetarios.
Algunos distribuidores independientes venden bibliotecas de clase que realizan cálculos monetarios precisos.
Además, la API de Java cuenta con la clase java.math.BigDecimal para realizar cálculos con valores de punto
flotante y precisión arbitraria.
Observe que el cuerpo de la instrucción for contiene el cálculo 1.0 + tasa, el cual aparece como argumento
para el método Math.pow. De hecho, este cálculo produce el mismo resultado cada vez que se realiza una itera-
ción en el ciclo, por lo que repetir el cálculo en todas las iteraciones del ciclo es un desperdicio.
Tip de rendimiento 5.2
En los ciclos, evite cálculos para los cuales el resultado nunca cambia; dichos cálculos, por lo general, deben colocarse
antes del ciclo. [Nota: actualmente, muchos de los compiladores con optimización colocan dichos cálculos fuera de
los ciclos en el código compilado].
5.5 Instrucción de repetición do...while
La instrucción de repetición do...while es similar a la instrucción while, ya que el programa evalúa la condi-
ción de continuación de ciclo al principio, antes de ejecutar el cuerpo del ciclo. Si la condición es falsa, el cuerpo
nunca se ejecuta. La instrucción do...while evalúa la condición de continuación de ciclo después de ejecutar el
cuerpo del ciclo; por lo tanto, el cuerpo siempre se ejecuta por lo menos una vez. Cuando termina una instrucción
do...while, la ejecución continúa con la siguiente instrucción en la secuencia. La figura 5.7 utiliza una instruc-
ción do...while para imprimir los números del 1 al 10.
La línea 8 declara e inicializa la variable de control contador. Al entrar a la instrucción do...while, la
línea 12 imprime el valor de contador y la 13 incrementa a contador. Después, el programa evalúa la prueba de
continuación de ciclo al final del mismo (línea 14). Si la condición es verdadera, el ciclo continúa a partir de la
primera instrucción del cuerpo en la instrucción do...while (línea 12). Si la condición es falsa, el ciclo termina
y el programa continúa con la siguiente instrucción después del ciclo.
La figura 5.8 contiene el diagrama de actividad de UML para la instrucción do...while. Este diagrama hace
evidente que la condición de continuación de ciclo no se evalúa sino hasta después que el ciclo ejecuta el estado de
acción, por lo menos una vez. Compare este diagrama de actividad con el de la instrucción while (figura 4.4).
No es necesario utilizar llaves en la estructura de repetición do...while si sólo hay una instrucción en el
cuerpo. Sin embargo, la mayoría de los programadores incluyen las llaves para evitar la confusión entre las ins-
trucciones while y do...while. Por ejemplo:
while ( condición )
generalmente es la primera línea de una instrucción while. Una instrucción do...while sin llaves, alrededor de
un cuerpo con una sola instrucción, aparece así:
do
instrucción
while ( condición );
1 // Fig. 5.7: PruebaDoWhile.java
2 // La instrucción de repetición do...while.
3
4 public class PruebaDoWhile
5 {
6 public static void main( String args[] )
7 {
8 int contador = 1; // inicializa contador
9
10 do
11 {
12 System.out.printf( "%d ", contador );
13 ++contador;
14 } while ( contador <= 10 ); // fin de do...while
15
16 System.out.println(); // imprime una nueva línea
17 } // fin de main
18 } // fin de la clase PruebaDoWhile
1 2 3 4 5 6 7 8 9 10
Figura 5.7 | La instrucción de repetición do...while.
Figura 5.8 | Diagrama de actividad de UML de la instrucción de repetición do...while.
Determina si
debe continuar
el ciclo [contador > 10]
[contador <= 10]
++contador
Muestra el valor
del contador
Incrementa la
variable de control
System.out.printf( “%d ”, contador );
5.5 Instrucción de repetición do...while 175
176 Capítulo 5 Instrucciones de control: parte 2
lo cual puede ser confuso. Un lector podría malinterpretar la última línea [while( condición );], como una ins-
trucción while que contiene una instrucción vacía (el punto y coma por sí solo). Por ende, la instrucción do...
while con una instrucción en su cuerpo se escribe generalmente así:
do
{
Instrucción
} while ( condición );
Buena práctica de programación 5.6
Incluya siempre las llaves en una instrucción do...while, aun y cuando éstas no sean necesarias. Esto ayuda a
eliminar la ambigüedad entre las instrucciones while y do...while que contienen sólo una instrucción.
5.6 Instrucción de selección múltiple switch
En el capítulo 4 hablamos sobre la instrucción if de selección simple y la instrucción if...else de selección
doble. Java cuenta con la instrucción switch de selección múltiple para realizar distintas acciones, con base en los
posibles valores de una variable o expresión entera. Cada acción se asocia con un valor de una expresión entera
constante (es decir, un valor constante de tipo byte, short, int o char, pero no long) que la variable o expre-
sión en la que se basa la instrucción switch pueda asumir.
La clase LibroCalificaciones con la instrucción switch para contar las calificaciones A, B, C,
D y F
La figura 5.9 contiene una versión mejorada de la clase LibroCalificaciones que presentamos en el capítulo 3
y desarrollamos un poco más en el capítulo 4. La versión de la clase que presentamos ahora no sólo calcula el
promedio de un conjunto de calificaciones numéricas introducidas por el usuario, sino que utiliza una instrucción
switch para determinar si cada calificación es el equivalente de A, B, C, D o F, y para incrementar el contador
de la calificación apropiada. La clase también imprime en pantalla un resumen del número de estudiantes que
recibieron cada calificación. La figura 5.10 muestra la entrada y la salida de ejemplo de la aplicación PruebaLi-
broCalificaciones, que utiliza la clase LibroCalificaciones para procesar un conjunto de calificaciones.
1 // Fig. 5.9: LibroCalificaciones.java
2 // La clase LibroCalificaciones usa la instrucción switch para contar las calificaciones
A, B, C, D y F.
3 import java.util.Scanner; // el programa usa la clase Scanner
4
5 public class LibroCalificaciones
6 {
7 private String nombreDelCurso; // nombre del curso que representa este
LibroCalificaciones
8 private int total; // suma de las calificaciones
9 private int contadorCalif; // número de calificaciones introducidas
10 private int aCuenta; // cuenta de calificaciones A
11 private int bCuenta; // cuenta de calificaciones B
12 private int cCuenta; // cuenta de calificaciones C
13 private int dCuenta; // cuenta de calificaciones D
14 private int fCuenta; // cuenta de calificaciones F
15
16 // el constructor inicializa nombreDelCurso;
17 // las variables de instancia int se inicializan en 0 de manera predeterminada
18 public LibroCalificaciones( String nombre )
19 {
Figura 5.9 | Clase LibroCalificaciones que utiliza una instrucción switch para contar las calificaciones A, B, C, D
y F. (Parte 1 de 3).
20 nombreDelCurso = nombre; // inicializa nombreDelCurso
21 } // fin del constructor
22
23 // método para establecer el nombre del curso
24 public void establecerNombreDelCurso( String nombre )
25 {
26 nombreDelCurso = nombre; // almacena el nombre del curso
27 } // fin del método establecerNombreDelCurso
28
29 // método para obtener el nombre del curso
30 public String obtenerNombreDelCurso()
31 {
32 return nombreDelCurso;
33 } // fin del método obtenerNombreDelCurso
34
35 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones
36 public void mostrarMensaje()
37 {
38 // obtenerNombreDelCurso obtiene el nombre del curso
39 System.out.printf( "Bienvenido al libro de calificaciones paran%s!nn",
40 obtenerNombreDelCurso() );
41 } // fin del método mostrarMensaje
42
43 // introduce un número arbitrario de calificaciones del usuario
44 public void introducirCalif()
45 {
46 Scanner entrada = new Scanner( System.in );
47
48 int calificacion; // calificación introducida por el usuario
49
50 System.out.printf( "%sn%sn %sn %sn",
51 "Escriba las calificaciones enteras en el rango de 0 a 100.",
52 "Escriba el indicador de fin de archivo para terminar la entrada:",
53 "En UNIX/Linux/Mac OS X escriba <ctrl> d y después oprima Intro",
54 "En Windows escriba <ctrl> z y después oprima Intro" );
55
56 // itera hasta que el usuario introduzca el indicador de fin de archivo
57 while ( entrada.hasNext() )
58 {
59 calificacion = entrada.nextInt(); // lee calificación
60 total += calificacion; // suma calificación a total
61 ++contadorCalif; // incrementa el número de calificaciones
62
63 // llama al método para incrementar el contador apropiado
64 incrementarContadorCalifLetra( calificacion );
65 } // fin de while
66 } // fin del método introducirCalif
67
68 // suma 1 al contador apropiado para la calificación especificada
69 public void incrementarContadorCalifLetra( int calificacion )
70 {
71 // determina cuál calificación se introdujo
72 switch ( calificacion / 10 )
73 {
74 case 9: // calificación está entre 90
75 case 10: // y 100
76 ++aCuenta; // incrementa aCuenta
77 break; // necesaria para salir del switch
Figura 5.9 | Clase LibroCalificaciones que utiliza una instrucción switch para contar las calificaciones A, B, C, D
y F. (Parte 2 de 3).
5.6 Instrucción de selección múltiple switch 177
178 Capítulo 5 Instrucciones de control: parte 2
78
79 case 8: // calificación está entre 80 y 89
80 ++bCuenta; // incrementa bCuenta
81 break; // sale del switch
82
83 case 7: // calificación está entre 70 y 79
84 ++cCuenta; // incrementa cCuenta
85 break; // sale del switch
86
87 case 6: // calificación está entre 60 y 69
88 ++dCuenta; // incrementa dCuenta
89 break; // sale del switch
90
91 default: // calificación es menor que 60
92 ++fCuenta; // incrementa fCuenta
93 break; // opcional; de todas formas sale del switch
94 } // fin de switch
95 } // fin del método incrementarContadorCalifLetra
96
97 // muestra un reporte con base en las calificaciones introducidas por el usuario
98 public void mostrarReporteCalif()
99 {
100 System.out.println( "nReporte de calificaciones:" );
101
102 // si el usuario introdujo por lo menos una calificación...
103 if ( contadorCalif != 0 )
104 {
105 // calcula el promedio de todas las calificaciones introducidas
106 double promedio = (double) total / contadorCalif;
107
108 // imprime resumen de resultados
109 System.out.printf( "El total de las %d calificaciones introducidas es %dn",
110 contadorCalif, total );
111 System.out.printf( "El promedio de la clase es %.2fn", promedio );
112 System.out.printf( "%sn%s%dn%s%dn%s%dn%s%dn%s%dn",
113 "Numero de estudiantes que recibieron cada calificacion:",
114 "A: ", aCuenta, // muestra el número de calificaciones A
115 "B: ", bCuenta, // muestra el número de calificaciones B
116 "C: ", cCuenta, // muestra el número de calificaciones C
117 "D: ", dCuenta, // muestra el número de calificaciones D
118 "F: ", fCuenta ); // muestra el número de calificaciones F
119 } // fin de if
120 else // no se introdujeron calificaciones, por lo que imprime el mensaje
apropiado
121 System.out.println( "No se introdujeron calificaciones" );
122 } // fin del método mostrarReporteCalif
123 } // fin de la clase LibroCalificaciones
Figura 5.9 | Clase LibroCalificaciones que utiliza una instrucción switch para contar las calificaciones A, B, C, D
y F. (Parte 3 de 3).
Al igual que las versiones anteriores de la clase, LibroCalificaciones (figura 5.9) declara la variable de ins-
tancia nombreDelCurso (línea 7) y contiene los métodos establecerNombreDelCurso (líneas 24 a 27), obte-
nerNombreDelCurso (líneas 30 a 33) y mostrarMensaje (líneas 36 a 41), que establecen el nombre del curso,
lo almacenan y muestran un mensaje de bienvenida al usuario, respectivamente. La clase también contiene un
constructor (líneas 18 a 21) que inicializa el nombre del curso.
La clase LibroCalificaciones también declara las variables de instancia total (línea 8) y contadorCalif
(línea 9), que llevan la cuenta de la suma de las calificaciones introducidas por el usuario y el número de cali-
ficaciones introducidas, respectivamente. Las líneas 10 a 14 declaran las variables contador para cada categoría
de calificaciones. La clase LibroCalificaciones mantiene a total, contadorCalif y a los cinco contadores de
las letras de calificación como variables de instancia, de manera que estas variables puedan utilizarse o modificarse
en cualquiera de los métodos de la clase. Observe que el constructor de la clase (líneas 18 a 21) establece sólo
el nombre del curso; las siete variables de instancia restantes son de tipo int y se inicializan con 0, de manera
predeterminada.
La clase LibroCalificaciones (figura 5.9) contiene tres métodos adicionales: introducirCalif, incre-
mentarContadorCalifLetra y mostrarReporteCalif. El método introducirCalif (líneas 44 a 66) lee un
número arbitrario de calificaciones enteras del usuario mediante el uso de la repetición controlada por un centine-
la, y actualiza las variables de instancia total y contadorCalif. El método introducirCalif llama al método
incrementarContadorCalifLetra (líneas 69 a 95) para actualizar el contador de letra de calificación apropiado
para cada calificación introducida. La clase LibroCalificaciones también contiene el método mostrarRepor-
teCalif (líneas 98 a 122), el cual imprime en pantalla un reporte que contiene el total de todas las calificaciones
introducidas, el promedio de las mismas y el número de estudiantes que recibieron cada letra de calificación.
Examinaremos estos métodos con más detalle.
La línea 48 en el método introducirCalif declara la variable calificacion que almacenará la entrada del
usuario. Las líneas 50 a 54 piden al usuario que introduzca calificaciones enteras y escriba el indicador de fin de
archivo para terminar la entrada. El indicador de fin de archivo es una combinación de teclas dependiente del sis-
tema, que el usuario introduce para indicar que no hay más datos que introducir. En el capítulo 14, Archivos y flu-
jos, veremos cómo se utiliza el indicador de fin de archivo cuando un programa lee su entrada desde un archivo.
En los sistemas UNIX/Linux/Mac OS X, el fin de archivo se introduce escribiendo la secuencia
<ctrl> d
en una línea por sí sola. Esta notación significa que hay que oprimir al mismo tiempo la tecla ctrl y la tecla d. En
los sistemas Windows, para introducir el fin de archivo se escribe
<ctrl> z
[Nota: en algunos sistemas, es necesario oprimir Intro después de escribir la secuencia de teclas de fin de archivo.
Además, Windows generalmente muestra los caracteres ^Z en la pantalla cuando se escribe el indicador de fin de
archivo, como se muestra en la salida de la figura 5.10].
Tip de portabilidad 5.1
Las combinaciones de teclas para introducir el fin de archivo son dependientes del sistema.
La instrucción while (líneas 57 a 65) obtiene la entrada del usuario. La condición en la línea 57 llama al
método hasNext de Scanner para determinar si hay más datos a introducir. Este método devuelve el valor
boolean true si hay más datos; en caso contrario, devuelve false. Después, el valor devuelto se utiliza como
el valor de la condición en la instrucción while. Mientras no se haya escrito el indicador de fin de archivo, el
método hasNext devolverá true.
La línea 59 recibe como entrada un valor del usuario. La línea 60 utiliza el operador += para sumar califi-
cacion a total. La línea 61 incrementa contadorCalif. El método mostrarReporteCalif de la clase utiliza
estas variables para calcular el promedio de las calificaciones. La línea 64 llama al método incrementarConta-
dorCalifLetra de la clase (declarado en las líneas 69 a 95) para incrementar el contador de letra de calificación
apropiado, con base en la calificación numérica introducida.
El método incrementarContadoraCalifLetra contiene una instrucción switch (líneas 72 a 94) que
determina cuál contador se debe incrementar. En este ejemplo, suponemos que el usuario introduce una califica-
ción válida en el rango de 0 a 100. Una calificación en el rango de 90 a 100 representa la A: de 80 a 89, la B; de
70 a 79, la C; de 60 a 69, la D y de 0 a 59, la F. La instrucción switch consiste en un bloque que contiene una
secuencia de etiquetas case y una instrucción case default opcional. Estas etiquetas se utilizan en este ejemplo
para determinar cuál contador se debe incrementar, con base en la calificación.
Cuando el flujo de control llega al switch, el programa evalúa la expresión entre paréntesis (calificacion /
10) que va después de la palabra clave switch. A esto se le conoce como la expresión de control de la instrucción
switch. El programa compara el valor de la expresión de control (que se debe evaluar como un valor entero de
5.6 Instrucción de selección múltiple switch 179
180 Capítulo 5 Instrucciones de control: parte 2
tipo byte, char, short o int) con cada una de las etiquetas case. La expresión de control de la línea 72 realiza
la división entera, que trunca la parte fraccionaria del resultado. Por ende, cuando dividimos cualquier valor en el
rango de 0 a 100 entre 10, el resultado es siempre un valor de 0 a 10. Utilizamos varios de estos valores en nuestras
etiquetas case. Por ejemplo, si el usuario introduce el entero 85, la expresión de control se evalúa como el valor
int 8. La instrucción switch compara a 8 con cada etiqueta case. Si ocurre una coincidencia (case 8: en la
línea 79), el programa ejecuta las instrucciones para esa instrucción case. Para el entero 8, la línea 80 incrementa
a bCuenta, ya que una calificación entre 80 y 89 es una B. La instrucción break (línea 81) hace que el control del
programa proceda con la primera instrucción después del switch; en este programa, llegamos al final del cuerpo
del método incrementarContadorCalifLetra, por lo que el control regresa a la línea 65 en el método intro-
ducirCalif (la primera línea después de la llamada a incrementarContadorCalifLetra). Esta línea marca el
fin del cuerpo del ciclo while que recibe las calificaciones de entrada (líneas 57 a 65), por lo que el control fluye
hacia la condición del while (línea 57) para determinar si el ciclo debe seguir ejecutándose.
Las etiquetas case en nuestro switch evalúan explícitamente los valores 10, 9, 8, 7 y 6. Observe los casos
en las líneas 74 y 75, que evalúan los valores 9 y 10 (los cuales representan la calificación A). Al listar las etiquetas
case en forma consecutiva, sin instrucciones entre ellas, pueden ejecutar el mismo conjunto de instrucciones;
cuando la expresión de control se evalúa como 9 o 10, se ejecutan las instrucciones de las líneas 76 y 77. La
instrucción switch no cuenta con un mecanismo para evaluar rangos de valores, por lo que cada valor que deba
evaluarse se tiene que listar en una etiqueta case separada. Observe que cada case puede tener varias instruc-
ciones. La instrucción switch es distinta de otras instrucciones de control, en cuanto a que no requiere llaves
alrededor de varias instrucciones en cada case.
Sin instrucciones break, cada vez que ocurre una coincidencia en el switch, se ejecutan las instrucciones
para ese case y los subsiguientes, hasta encontrar una instrucción break o el final del switch. A menudo a esto
se le conoce como que las etiquetas case “se pasarían” hacia las instrucciones en las etiquetas case subsiguientes.
(Esta característica es perfecta para escribir un programa conciso, que muestre la canción iterativa “Los Doce Días
de Navidad” en el ejercicio 5.29).
Error común de programación 5.7
Olvidar una instrucción break cuando se necesita una en una instrucción switch es un error lógico.
Si no ocurre una coincidencia entre el valor de la expresión de control y una etiqueta case, se ejecuta el
caso default (líneas 91 a 93). Utilizamos el caso default en este ejemplo para procesar todos los valores de la
expresión de control que sean menores de 6; esto es, todas las calificaciones de reprobado. Si no ocurre una coin-
cidencia y la instrucción switch no contiene un caso default, el control del programa simplemente continúa
con la primera instrucción después de la instrucción switch.
La clase PruebaLibroCalificaciones para demostrar la clase LibroCalificaciones
La clase PruebaLibroCalificaciones (figura 5.10) crea un objeto LibroCalificaciones (líneas 10 y 11). La
línea 13 invoca el método mostrarMensaje del objeto para imprimir en pantalla un mensaje de bienvenida para
el usuario. La línea 14 invoca el método introducirCalif del objeto para leer un conjunto de calificaciones
del usuario y llevar el registro de la suma de todas las calificaciones introducidas, y el número de calificaciones.
Recuerde que el método introducirCalif también llama al método incrementarContadorCalifLetra para
llevar el registro del número de estudiantes que recibieron cada letra de calificación. La línea 15 invoca el método
mostrarReporteCalif de la clase LibroCalificaciones, el cual imprime en pantalla un reporte con base en
las calificaciones introducidas (como en la ventana de entrada/salida en la figura 5.10). La línea 103 de la clase
LibroCalificaciones (figura 5.9) determina si el usuario introdujo por lo menos una calificación; esto evita la
división entre cero. De ser así, la línea 106 calcula el promedio de las calificaciones. A continuación, las líneas 109
a 118 imprimen en pantalla el total de todas las calificaciones, el promedio de la clase y el número de estudiantes
que recibieron cada letra de calificación. Si no se introdujeron calificaciones, la línea 121 imprime en pantalla un
mensaje apropiado. Los resultados en la figura 5.10 muestran un reporte de calificaciones de ejemplo, con base
en 10 calificaciones.
Observe que la clase PruebaLibroCalificaciones (figura 5.10) no llama directamente al método incre-
mentarContadorCalifLetra de LibroCalificaciones (líneas 69 a 95 de la figura 5.9). Este método lo utiliza
exclusivamente el método introducirCalif de la clase LibroCalificaciones para actualizar el contador de la
1 // Fig. 5.10: PruebaLibroCalificaciones.java
2 // Crea un objeto LibroCalificaciones, introduce las calificaciones y muestra un
reporte.
3
4 public class PruebaLibroCalificaciones
5 {
6 public static void main( String args[] )
7 {
8 // crea un objeto LibroCalificaciones llamado miLibroCalificaciones y
9 // pasa el nombre del curso al constructor
10 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones(
11 "CS101 Introducción a la programación en Java" );
12
13 miLibroCalificaciones.mostrarMensaje(); // muestra un mensaje de bienvenida
14 miLibroCalificaciones.introducirCalif(); // lee calificaciones del usuario
15 miLibroCalificaciones.mostrarReporteCalif(); // muestra reporte basado en las
calificaciones
16 } // fin de main
17 } // fin de la clase PruebaLibroCalificaciones
Bienvenido al libro de calificaciones para
CS101 Introduccion a la programacion en Java!
Escriba las calificaciones enteras en el rango de 0 a 100.
Escriba el indicador de fin de archivo para terminar la entrada:
En UNIX/Linux/Mac OS X escriba <ctrl> d y despues oprima Intro
En Windows escriba <ctrl> z y despues oprima Intro
99
92
45
57
63
71
76
85
90
100
^Z
Reporte de calificaciones:
El total de las 10 calificaciones introducidas es 778
El promedio de la clase es 77.80
Numero de estudiantes que recibieron cada calificacion:
A: 4
B: 1
C: 2
D: 1
F: 2
Figura 5.10 | PruebaLibroCalificaciones crea un objeto LibroCalificaciones e invoca a sus métodos.
calificación de letra apropiado, a medida que el usuario introduce cada nueva calificación. El método incre-
mentarContadorCalifLetra existe únicamente para dar soporte a las operaciones de los demás métodos de la
clase LibroCalificaciones, por lo cual se declara como private. En el capítulo 3 vimos que los métodos que se
declaran con el modificador de acceso private pueden llamarse sólo por otros métodos de la clase en la que es-
tán declarados los métodos private. Dichos métodos se conocen comúnmente como métodos utilitarios o
métodos ayudantes, debido a que sólo pueden llamarse mediante otros métodos de esa clase y se utilizan para dar
soporte a la operación de esos métodos.
5.6 Instrucción de selección múltiple switch 181
182 Capítulo 5 Instrucciones de control: parte 2
Diagrama de actividad de UML de la instrucción switch
La figura 5.11 muestra el diagrama de actividad de UML para la instrucción switch general. La mayoría de las
instrucciones switch utilizan una instrucción break en cada case para terminar la instrucción switch después
de procesar el case. La figura 5.11 enfatiza esto al incluir instrucciones break en el diagrama de actividad. Es-
te diagrama hace evidente que break al final de una etiqueta case hace que el control salga de la instrucción
switch de inmediato.
No se requiere una instrucción break para la última etiqueta case del switch (o para el caso default
opcional, cuando aparece al último), ya que la ejecución continúa con la siguiente instrucción que va después del
switch.
Observación de ingeniería de software 5.2
Proporcione un caso default en las instrucciones switch. Al incluir un caso default usted puede enfocarse en la
necesidad de procesar las condiciones excepcionales.
Buena práctica de programación 5.7
Aunque cada case y el caso default en una instrucción switch pueden ocurrir en cualquier orden, es conveniente
colocar la etiqueta default. Cuando el caso default se lista al último, no se requiere el break para ese caso. Algu-
nos programadores incluyen este break para mejorar la legibilidad y tener simetría con los demás casos.
Cuando utilice la instrucción switch, recuerde que la expresión después de cada case debe ser una expre-
sión entera constante; es decir, cualquier combinación de constantes enteras que se evalúen como un valor entero
constante (por ejemplo, –7, 0 o 221). Una constante entera es tan solo un valor entero. Además, puede utili-
zar constantes tipo carácter: caracteres específicos entre comillas sencillas, como ‘A’, ‘7’ o ‘$’, las cuales
Figura 5.11 | Diagrama de actividad de UML de la instrucción switch de selección múltiple con instrucciones
break.
...
Acción(es) de default
Acción(es) del case a
Acción(es) del case b
Acción(es) del case z break
break
break
case b
case z
case a
[falso]
[verdadero]
[verdadero]
[verdadero]
[falso]
[falso]
representan los valores enteros de los caracteres. (En el apéndice B, Conjunto de caracteres ASCII, se muestran
los valores enteros de los caracteres en el conjunto de caracteres ASCII, que es un subconjunto del conjunto de
caracteres Unicode utilizado por Java).
La expresión en cada case también puede ser una variable constante: una variable que contiene un valor
que no cambia durante todo el programa. Dicha variable se declara mediante la palabra clave final (que des-
cribiremos en el capítulo 6, Métodos: un análisis más detallado). Java tiene una característica conocida como
enumeraciones, que también presentaremos en el capítulo 6. Las constantes de enumeración también pueden
utilizarse en etiquetas case. En el capítulo 10, Programación orientada a objetos: polimorfismo, presentaremos
una manera más elegante de implementar la lógica del switch; utilizaremos una técnica llamada polimorfismo
para crear programas que a menudo son más legibles, fáciles de mantener y de extender que los programas que
utilizan lógica de switch.
5.7 Instrucciones break y continue
Además de las instrucciones de selección y repetición, Java cuenta con las instrucciones break y continue (que
presentamos en esta sección y en el apéndice N, Instrucciones break y continue etiquetadas) para alterar el
flujo de control. En la sección anterior mostramos cómo puede utilizarse la instrucción break para terminar
la ejecución de una instrucción switch. En esta sección veremos cómo utilizar break en las instrucciones de
repetición.
Java también cuenta con las instrucciones break y continue etiquetadas, para usarlas en los casos en los que
es conveniente alterar el flujo de control en las instrucciones de control anidadas. En el apéndice N hablaremos
sobre las instrucciones break y continue etiquetadas.
Instrucción break
Cuando break se ejecuta en una instrucción while, for, do...while, o switch, ocasiona la salida inmediata de
esa instrucción. La ejecución continúa con la primera instrucción después de la instrucción de control. Los usos
comunes de break son para escapar anticipadamente del ciclo, o para omitir el resto de una instrucción switch
(como en la figura 5.9). La figura 5.12 demuestra el uso de una instrucción break para salir de un ciclo for.
1 // Fig. 5.12: PruebaBreak.java
2 // La instrucción break para salir de una instrucción for.
3 public class PruebaBreak
4 {
5 public static void main( String args[] )
6 {
7 int cuenta; // la variable de control también se usa cuando termina el ciclo
8
9 for ( cuenta = 1; cuenta <= 10; cuenta++ ) // itera 10 veces
10 {
11 if ( cuenta == 5 ) // si cuenta es 5,
12 break; // termina el ciclo
13
14 System.out.printf( "%d ", cuenta );
15 } // fin de for
16
17 System.out.printf( "nSalio del ciclo en cuenta = %dn", cuenta );
18 } // fin de main
19 } // fin de la clase PruebaBreak
1 2 3 4
Salio del ciclo en cuenta = 5
Figura 5.12 | La instrucción break para salir de una instrucción for.
5.7 Instrucciones break y continue 183
184 Capítulo 5 Instrucciones de control: parte 2
Cuando la instrucción if anidada en la línea 11 dentro de la instrucción for (líneas 9 a 15) determina que
cuenta es 5, se ejecuta la instrucción break en la línea 12. Esto termina la instrucción for y el programa conti-
núa a la línea 17 (inmediatamente después de la instrucción for), la cual muestra un mensaje indicando el valor
de la variable de control cuando terminó el ciclo. El ciclo ejecuta su cuerpo por completo sólo cuatro veces en
vez de 10.
Instrucción continue
Cuando la instrucción continue se ejecuta en una instrucción while, for o do...while, omite las instruccio-
nes restantes en el cuerpo del ciclo y continúa con la siguiente iteración del ciclo. En las instrucciones while y
do...while, la aplicación evalúa la prueba de continuación de ciclo justo después de que se ejecuta la instrucción
continue. En una instrucción for se ejecuta la expresión de incremento y después el programa evalúa la prueba
de continuación de ciclo.
La figura 5.13 utiliza la instrucción continue en un ciclo for para omitir la instrucción de la línea 12 cuan-
do la instrucción if anidada (línea 9) determina que el valor de cuenta es 5. Cuando se ejecuta la instrucción
continue, el control del programa continúa con el incremento de la variable de control en la instrucción for
(línea 7).
En la sección 5.3 declaramos que la instrucción while puede utilizarse, en la mayoría de los casos, en lugar
de for. La única excepción ocurre cuando la expresión de incremento en el while va después de una instrucción
continue. En este caso, el incremento no se ejecuta antes de que el programa evalúe la condición de continua-
ción de repetición, por lo que el while no se ejecuta de la misma manera que el for.
Observación de ingeniería de software 5.3
Algunos programadores sienten que las instrucciones break y continue violan la programación estructurada. Ya
que pueden lograrse los mismos efectos con las técnicas de programación estructurada, estos programadores prefieren
no utilizar instrucciones break o continue.
Observación de ingeniería de software 5.4
Existe una tensión entre lograr la ingeniería de software de calidad y lograr el software con mejor desempeño.
A menudo, una de estas metas se logra a expensas de la otra. Para todas las situaciones excepto las que demanden el
mayor rendimiento, aplique la siguiente regla empírica: primero, asegúrese de que su código sea simple y correcto;
después hágalo rápido y pequeño, pero sólo si es necesario.
1 // Fig. 5.13: PruebaContinue.java
2 // Instrucción continue para terminar una iteración de una instrucción for.
3 public class PruebaContinue
4 {
5 public static void main( String args[] )
6 {
7 for ( int cuenta = 1; cuenta <= 10; cuenta++ ) // itera 10 veces
8 {
9 if ( cuenta == 5 ) // si cuenta es 5,
10 continue; // omite el resto del código en el ciclo
11
12 System.out.printf( "%d ", cuenta );
13 } // fin de for
14
15 System.out.println( "nSe uso continue para omitir imprimir 5" );
16 } // fin de main
17 } // fin de la clase PruebaContinue
1 2 3 4 6 7 8 9 10
Se uso continue para omitir imprimir 5
Figura 5.13 | Instrucción continue para terminar una iteración de una instrucción for.
5.8 Operadores lógicos
Cada una de las instrucciones if, if...else, while, do...while y for requieren una condición para determi-
nar cómo continuar con el flujo de control de un programa. Hasta ahora sólo hemos estudiado las condiciones
simples, como cuenta <= 10, numero != valorCentinela y total > 1000. Las condiciones simples se expre-
san en términos de los operadores relacionales >, <, >= y <=, y los operadores de igualdad == y !=; cada expresión
evalúa sólo una condición. Para evaluar condiciones múltiples en el proceso de tomar una decisión, ejecutamos
estas pruebas en instrucciones separadas o en instrucciones if o if...else anidadas. En ocasiones, las instruc-
ciones de control requieren condiciones más complejas para determinar el flujo de control de un programa.
Java cuenta con los operadores lógicos para que usted pueda formar condiciones más complejas, al combi-
nar las condiciones simples. Los operadores lógicos son && (AND condicional), || (OR condicional), & (AND
lógico booleano), | (OR inclusivo lógico booleano), ^ (OR exclusivo lógico booleano) y ! (NOT lógico).
Operador AND (&&) condicional
Suponga que deseamos asegurar en cierto punto de una aplicación que dos condiciones sean ambas verdaderas,
antes de elegir cierta ruta de ejecución. En este caso, podemos utilizar el operador && (AND condicional) de
la siguiente manera:
if ( genero == FEMENINO && edad >= 65 )
++mujeresMayores;
Esta instrucción if contiene dos condiciones simples. La condición genero == FEMENINO compara la variable
genero con la constante FEMENINO. Por ejemplo, esto podría evaluarse para determinar si una persona es mujer.
La condición edad >= 65 podría evaluarse para determinar si una persona es un ciudadano mayor. La instrucción
if considera la condición combinada
genero == FEMENINO && edad >= 65
la cual es verdadera si, y sólo si ambas condiciones simples son verdaderas. Si la condición combinada es verdade-
ra, el cuerpo de la instrucción if incrementa a mujeresMayores en 1. Si una o ambas condiciones simples son
falsas, el programa omite el incremento. Algunos programadores consideran que la condición combinada anterior
es más legible si se agregan paréntesis redundantes, como por ejemplo:
( genero == FEMENINO ) && ( edad >= 65 )
La tabla de la figura 5.14 sintetiza el uso del operador &&. Esta tabla muestra las cuatro combinaciones posibles
de valores false y true para expresión1 y expresión2. A dichas tablas se les conoce como tablas de verdad. Java
evalúa todas las expresiones que incluyen operadores relacionales, de igualdad o lógicos como true o false.
expresión1 expresión2 expresión1 && expresión2
false false false
false true false
true false false
true true true
Figura 5.14 | Tabla de verdad del operador && (AND condicional).
Operador OR condicional (||)
Ahora suponga que deseamos asegurar que cualquiera o ambas condiciones sean verdaderas antes de elegir cierta
ruta de ejecución. En este caso, utilizamos el operador || (OR condicional), como se muestra en el siguiente
segmento de un programa:
if ( ( promedioSemestre >= 90 ) || ( examenFinal >= 90 ) )
System.out.println ( “La calificacion del estudiante es A” );
5.8 Operadores lógicos 185
186 Capítulo 5 Instrucciones de control: parte 2
Esta instrucción también contiene dos condiciones simples. La condición promedioSemestre >= 90 se evalúa
para determinar si el estudiante merece una A en el curso, debido a que tuvo un sólido rendimiento a lo largo
del semestre. La condición examenFinal >= 90 se evalúa para determinar si el estudiante merece una A en el
curso debido a un desempeño sobresaliente en el examen final. Después, la instrucción if considera la condición
combinada
( promedioSemestre >= 90 ) || ( examenFinal >= 90 )
y otorga una A al estudiante si una o ambas de las condiciones simples son verdaderas. La única vez que no se
imprime el mensaje “La calificación del estudiante es A” es cuando ambas condiciones simples son
falsas. La figura 5.15 es una tabla de verdad para el operador OR condicional (||). El operador && tiene mayor
precedencia que el operador ||. Ambos operadores se asocian de izquierda a derecha.
expresión1 expresión2 expresión1 || expresión2
false false false
false true true
true false true
true true true
Figura 5.15 | Tabla de verdad del operador (OR condicional) ||.
Evaluación en corto circuito de condiciones complejas
Las partes de una expresión que contienen los operadores && o || se evalúan sólo hasta que se sabe si la condición
es verdadera o falsa. Por ende, la evaluación de la expresión
( genero == FEMENINO ) && ( edad >= 65 )
se detiene de inmediato si genero no es igual a FEMENINO (es decir, en este punto es evidente que toda la expre-
sión es false) y continúa si genero es igual a FEMENINO (es decir, toda la expresión podría ser aún true si la
condición edad >= 65 es true). Esta característica de las expresiones AND y OR condicional se conoce como
evaluación en corto circuito.
Error común de programación 5.8
En las expresiones que utilizan el operador &&, una condición (a la cual le llamamos condición dependiente) puede
requerir que otra condición sea verdadera para que la evaluación de la condición dependiente tenga significado. En
este caso, la condición dependiente debe colocarse después de la otra condición, o podría ocurrir un error. Por ejemplo,
en la expresión ( i != 0) && (10 / i == 2), la segunda condición debe aparecer después de la primera, o podría
ocurrir un error de división entre cero.
Operadores AND lógico booleano (&) y OR inclusivo lógico booleano (|)
Los operadores AND lógico booleano (&) y OR inclusivo lógico booleano (|) funcionan en forma idéntica
a los operadores && (AND condicional) y || (OR condicional), con una excepción: los operadores lógicos boolea-
nos siempre evalúan ambos operandos (es decir, no realizan una evaluación en corto circuito). Por lo tanto, la
expresión
( genero == 1 ) & ( edad >= 65 )
evalúa edad >= 65, sin importar que genero sea igual o no a 1. Esto es útil si el operando derecho del operador
AND lógico booleano o del OR inclusivo lógico booleano tiene un efecto secundario requerido: la modificación
del valor de una variable. Por ejemplo, la expresión
( cumpleanios == true ) | ( ++edad >= 65 )
garantiza que se evalúe la condición ++edad >= 65. Por ende, la variable edad se incrementa en la expresión
anterior, sin importar que la expresión total sea true o false.
Tip para prevenir errores 5.4
Por cuestión de claridad, evite las expresiones con efectos secundarios en las condiciones. Los efectos secundarios pue-
den tener una apariencia inteligente, pero pueden hacer que el código sea más difícil de entender y pueden llegar a
producir errores lógicos sutiles.
OR exclusivo lógico booleano (^)
Una condición compleja que contiene el operador OR exclusivo lógico booleano (^) es true si y sólo si uno de
sus operandos es true y el otro es false. Si ambos operandos son true o si ambos son false, toda la condición
es false. La figura 5.16 es una tabla de verdad para el operador OR exclusivo lógico booleano (^). También se
garantiza que este operador evaluará ambos operandos.
expresión1 expresión2 expresión1 ^ expresión2
false false false
false true true
true false true
true true false
Figura 5.16 | Tabla de verdad del operador ^ (OR exclusivo
lógico booleano).
expresión !expresión
false true
true false
Figura 5.17 | Tabla de verdad
del operador ! (negación lógica, o
NOT lógico).
Operador lógico de negación (!)
El operador ! (NOT lógico, también conocido como negación lógica o complemento lógico) “invierte” el sig-
nificado de una condición. A diferencia de los operadores lógicos &&, ||, &, | y ^, que son operadores binarios
que combinan dos condiciones, el operador lógico de negación es un operador unario que sólo tiene una con-
dición como operando. Este operador se coloca antes de una condición para elegir una ruta de ejecución si la
condición original (sin el operador lógico de negación) es false, como en el siguiente segmento de código:
if ( ! ( calificacion == valorCentinela ) )
System.out.printf( “La siguiente calificación es %dn”, calificacion );
que ejecuta la llamada a printf sólo si calificacion no es igual a valorCentinela. Los paréntesis alrededor
de la condición calificacion == valorCentinela son necesarios, ya que el operador lógico de negación tiene
mayor precedencia que el de igualdad.
En la mayoría de los casos, puede evitar el uso de la negación lógica si expresa la condición en forma distinta,
con un operador relacional o de igualdad apropiado. Por ejemplo, la instrucción anterior también puede escribirse
de la siguiente manera:
if ( calificacion != valorCentinela )
System.out.printf( “La siguiente calificación es %dn”, calificacion );
Esta flexibilidad le puede ayudar a expresar una condición de una manera más conveniente. La figura 5.17 es una
tabla de verdad para el operador lógico de negación.
5.8 Operadores lógicos 187
188 Capítulo 5 Instrucciones de control: parte 2
Ejemplo de los operadores lógicos
La figura 5.18 demuestra el uso de los operadores lógicos y lógicos booleanos; para ello produce sus tablas de
verdad. Los resultados muestran la expresión que se evalúo y el resultado boolean de esa expresión. Los valores
de las expresiones boolean se muestran mediante printf, usando el especificador de formato %b, que imprime
la palabra “true” o “false”, con base en el valor de la expresión. Las líneas 9 a 13 producen la tabla de verdad para el
&&. Las líneas 16 a 20 producen la tabla de verdad para el ||. Las líneas 23 a 27 producen la tabla de verdad para
el &. Las líneas 30 a 35 producen la tabla de verdad para el |. Las líneas 38 a 43 producen la tabla de verdad
para el ^. Las líneas 46 a 47 producen la tabla de verdad para el !.
1 // Fig. 5.18: OperadoresLogicos.java
2 // Los operadores lógicos.
3
4 public class OperadoresLogicos
5 {
6 public static void main( String args[] )
7 {
8 // crea tabla de verdad para el operador && (AND condicional)
9 System.out.printf( "sn%s: %bn%s: %bn%s: %bn%s: %bnn",
10 "AND condicional (&&)", "false && false"( false && false ),
11 "false && true", ( false && true ),
12 "true && false", ( true && false ),
13 "true && true", ( true && true ) );
14
15 // crea tabla de verdad para el operador || (OR condicional)
16 System.out.printf( "%sn%s: %bn%s: %bn%s: %bn%s: %bnn",
17 "OR condicional (||)", "false || false", ( false || false ),
18 "false || true", ( false || true ),
19 "true || false", ( true || false ),
20 "true || true", ( true || true ) );
21
22 // crea tabla de verdad para el operador & (AND lógico booleano)
23 System.out.printf( "%sn%s: %bn%s: %bn%s: %bn%s: %bnn",
24 "AND logico booleano (&)", "false & false", ( false & false ),
25 "false & true", ( false & true ),
26 "true & false", ( true & false ),
27 "true & true", ( true & true ) );
28
29 // crea tabla de verdad para el operador | (OR inclusivo lógico booleano)
30 System.out.printf( "%sn%s: %bn%s: %bn%s: %bn%s: %bnn",
31 "OR inclusivo logico booleano (|)",
32 "false | false", ( false | false ),
33 "false | true", ( false | true ),
34 "true | false", ( true | false ),
35 "true | true", ( true | true ) );
36
37 // crea tabla de verdad para el operador ^ (OR exclusivo lógico booleano)
38 System.out.printf( "%sn%s: %bn%s: %bn%s: %bn%s: %bnn",
39 "OR exclusivo logico booleano (^)",
40 "false ^ false", ( false ^ false ),
41 "false ^ true", ( false ^ true ),
42 "true ^ false", ( true ^ false ),
43 "true ^ true", ( true ^ true ) );
44
45 // crea tabla de verdad para el operador ! (negación lógica)
46 System.out.printf( "%sn%s: %bn%s: %bn", "NOT logico (!)",
47 "!false"( !false ), "!true", ( !true ) );
Figura 5.18 | Los operadores lógicos. (Parte 1 de 2).
La figura 5.19 muestra la precedencia y la asociatividad de los operadores de Java presentados hasta ahora.
Los operadores se muestran de arriba hacia abajo, en orden descendente de precedencia.
48 } // fin de main
49 } // fin de la clase OperadoresLogicos
AND condicional (&&)
false && false: false
false && true: false
true && false: false
true && true: true
OR condicional (||)
false || false: false
false || true: true
true || false: true
true || true: true
AND logico booleano (&)
false & false: false
false & true: false
true & false: false
true & true: true
OR inclusivo logico booleano (|)
false | false: false
false | true: true
true | false: true
true | true: true
OR exclusivo logico booleano (^)
false ^ false: false
false ^ true: true
true ^ false: true
true ^ true: false
NOT logico (!)
!false: true
!true: false
Figura 5.18 | Los operadores lógicos. (Parte 2 de 2).
Operadores Asociatividad Tipo
++ -- derecha a izquierda postfijo unario
++ - + - ! (tipo) derecha a izquierda prefijo unario
* / % izquierda a derecha multiplicativo
+ - izquierda a derecha aditivo
< <= > >= izquierda a derecha relacional
== != izquierda a derecha igualdad
& izquierda a derecha AND lógico booleano
Figura 5.19 | Precedencia/asociatividad de los operadores descritos hasta ahora. (Parte 1 de 2).
5.8 Operadores lógicos 189
190 Capítulo 5 Instrucciones de control: parte 2
5.9 Resumen sobre programación estructurada
Así como los arquitectos diseñan edificios empleando la sabiduría colectiva de su profesión, de igual forma, los
programadores diseñan programas. Nuestro campo es mucho más joven que la arquitectura, y nuestra sabiduría
colectiva es mucho más escasa. Hemos aprendido que la programación estructurada produce programas que son
más fáciles de entender, probar, depurar, modificar que los programas no estructurados, e incluso probar que
son correctos en sentido matemático.
La figura 5.20 utiliza diagramas de actividad de UML para sintetizar las instrucciones de control de Java.
Los estados inicial y final indican el único punto de entrada y el único punto de salida de cada instrucción de
control. Si conectamos los símbolos individuales de un diagrama de actividad en forma arbitrara, existe la posibi-
lidad de que se produzcan programas no estructurados. Por lo tanto, la profesión de la programación ha elegido
un conjunto limitado de instrucciones de control que pueden combinarse sólo de dos formas simples, para crear
programas estructurados.
Por cuestión de simpleza, sólo se utilizan instrucciones de control de una sola entrada/una sola salida; sólo
hay una forma de entrar y una forma de salir de cada instrucción de control. Es sencillo conectar instrucciones
de control en secuencia para formar programas estructurados. El estado final de una instrucción de control se
conecta al estado inicial de la siguiente instrucción de control; es decir, las instrucciones de control se colocan una
después de la otra en un programa en secuencia. A esto le llamamos “apilamiento de instrucciones de control”. Las
reglas para formar programas estructurados también permiten anidar las instrucciones de control.
La figura 5.21 muestra las reglas para formar programas estructurados. Las reglas suponen que pueden
utilizarse estados de acción para indicar cualquier acción. Además, las reglas suponen que comenzamos con el
diagrama de actividad más sencillo (figura 5.22), que consiste solamente de un estado inicial, un estado de acción,
un estado final y flechas de transición.
Al aplicar las reglas de la figura 5.21, siempre se obtiene un diagrama de actividad estructurado apropiada-
mente, con una agradable apariencia de bloque de construcción. Por ejemplo, si se aplica la regla 2 repetidamente
al diagrama de actividad más sencillo, se obtiene un diagrama de actividad que contiene muchos estados de acción
en secuencia (figura 5.23). La regla 2 genera una pila de estructuras de control, por lo que llamaremos a la regla 2
regla de apilamiento. [Nota: las líneas punteadas verticales en la figura 5.23 no son parte de UML. Las utilizamos
para separar los cuatro diagramas de actividad que demuestran cómo se aplica la regla 2 de la figura 5.21].
La regla 3 se conoce como regla de anidamiento. Al aplicar la regla 3 repetidamente al diagrama de acti-
vidad más sencillo, se obtiene un diagrama de actividad con instrucciones de control perfectamente anidadas.
Por ejemplo, en la figura 5.24 el estado de acción en el diagrama de actividad más sencillo se reemplaza con una
instrucción de selección doble (if...else). Luego la regla 3 se aplica otra vez a los estados de acción en la ins-
trucción de selección doble, reemplazando cada uno de estos estados con una instrucción de selección doble. El
símbolo punteado de estado de acción alrededor de cada una de las instrucciones de selección doble, representa el
estado de acción que se reemplazó. [Nota: las flechas punteadas y los símbolos punteados de estado de acción que
se muestran en la figura 5.24 no son parte de UML. Aquí se utilizan para ilustrar que cualquier estado de acción
puede reemplazarse con un enunciado de control].
La regla 4 genera instrucciones más grandes, más implicadas y más profundamente anidadas. Los diagramas
que surgen debido a la aplicación de las reglas de la figura 5.21 constituyen el conjunto de todos los posibles
Operadores Asociatividad Tipo
^ izquierda a derecha OR exclusivo lógico booleano
| izquierda a derecha OR inclusivo lógico booleano
&& izquierda a derecha AND condicional
|| izquierda a derecha OR condicional
?: derecha a izquierda condicional
= += -= *= /= %= derecha a izquierda asignación
Figura 5.19 | Precedencia/asociatividad de los operadores descritos hasta ahora. (Parte 2 de 2).
diagramas de actividad estructurados y, por lo tanto, el conjunto de todos los posibles programas estructurados.
La belleza de la metodología estructurada es que utilizamos sólo siete instrucciones de control simples de una sola
entrada/una sola salida, y las ensamblamos en una de sólo dos formas simples.
Si se siguen las reglas de la figura 5.21, no podrá crearse un diagrama de actividad “sin estructura” (como
el de la figura 5.25). Si usted no está seguro de que cierto diagrama sea estructurado, aplique las reglas de la
5.9 Resumen sobre programación estructurada 191
break
[v]
[f]
instrucción if…else
(selección doble)
instrucción if
(selección simple)
[v]
[f]
[v]
[f]
break
[v]
break
[v]
[f]
[f]
instrucción switch con
instrucciones breaks
(selección múltiple)
Secuencia Selección
Repetición
procesamiento default
inicialización
incremento
...
...
[v]
[f]
instrucción for
[v]
[f]
instrucción while
[v]
[f]
instrucción do…while
Figura 5.20 | Instrucciones de secuencia, selección y repetición de una sola entrada/una sola salida de Java.
192 Capítulo 5 Instrucciones de control: parte 2
figura 5.21 en orden inverso para reducir el diagrama al diagrama de actividad más sencillo. Si puede reducirlo,
entonces el diagrama original es estructurado; de lo contrario, no es estructurado.
Reglas para formar programas estructurados
1) Comenzar con el diagrama de actividad más sencillo (figura 5.22).
2) Cualquier estado de acción puede reemplazarse por dos estados de acción en secuencia.
3) Cualquier estado de acción puede reemplazarse por cualquier instrucción de control
(secuencia de estados de acción, if, if...else, switch, while, do...while o for).
4) Las reglas 2 y 3 pueden aplicarse tantas veces como se desee y en cualquier orden.
Figura 5.21 | Reglas para formar programas estructurados.
estado de acción
estado de acción estado de acción
aplicar
regla 2
aplicar
regla 2
aplicar
regla 2
estado de acción
estado de acción
estado de acción estado de acción
estado de acción
estado de acción
...
Figura 5.22 | El diagrama de actividad más sencillo.
Figura 5.23 | El resultado de aplicar la regla de apilamiento (regla 2) de la figura 5.21 repetidamente al diagrama
de actividad más sencillo.
estado de acción
estado de acción
[v]
[f]
[v]
[f]
[v]
[f]
[v]
[f]
estado de acción
estado de acción
estado de acción estado de acción
estado de acción
aplicar
regla 3
aplicar
regla 3
aplicar
regla 3
Figura 5.24 | Aplicación de la regla de anidamiento (regla 3) de la figura 5.21 al diagrama de actividad más
sencillo.
Figura 5.25 | Diagrama de actividad “sin estructura”.
estado de acción
estado de acción
estado de acción estado de acción
5.9 Resumen sobre programación estructurada 193
194 Capítulo 5 Instrucciones de control: parte 2
La programación estructurada promueve la simpleza. Bohm y Jacopini nos han dado el resultado de que sólo
se necesitan tres formas de control para implementar un algoritmo:
Secuencia.
Selección.
Repetición.
La estructura de secuencia es trivial. Simplemente enliste las instrucciones a ejecutar en el orden en el que
deben ejecutarse. La selección se implementa en una de tres formas:
Instrucción if (selección simple).
Instrucción if...else (selección doble).
Instrucción switch (selección múltiple).
De hecho, es sencillo demostrar que la instrucción if simple es suficiente para ofrecer cualquier forma de selec-
ción; todo lo que pueda hacerse con las instrucciones if...else y switch puede implementarse si se combinan
instrucciones if (aunque tal vez no con tanta claridad y eficiencia).
La repetición se implementa en una de tres maneras:
Instrucción while.
Instrucción do...while.
Instrucción for.
Es sencillo demostrar que la instrucción while es suficiente para proporcionar cualquier forma de repetición.
Todo lo que puede hacerse con las instrucciones do...while y for, puede hacerse también con la instrucción
while (aunque tal vez no sea tan sencillo).
Si se combinan estos resultados, se demuestra que cualquier forma de control necesaria en un programa en
Java puede expresarse en términos de:
Secuencia.
Instrucción if (selección).
Instrucción while (repetición).
y que estos tres elementos pueden combinarse en sólo dos formas: apilamiento y anidamiento. Evidentemente, la
programación estructurada es la esencia de la simpleza.
5.10 (Opcional) Ejemplo práctico de GUI y gráficos:
dibujo de rectángulos y óvalos
Esta sección demuestra cómo dibujar rectángulos y óvalos, usando los métodos drawRect y drawOval de Gra-
phics, respectivamente. Estos métodos se demuestran en la figura 5.26.
La línea 6 empieza la declaración de la clase para Figuras, que extiende a JPanel. La variable de instancia
opcion, declarada en la línea 8, determina si paintComponent debe dibujar rectángulos u óvalos. El constructor
de Figuras en las líneas 11 a 14 inicializa opcion con el valor que se pasa en el parámetro opcionUsuario.
El método paintComponent (líneas 17 a 36) realiza el dibujo actual. Recuerde que la primera instrucción
en todo método paintComponent debe ser una llamada a super.paintComponent, como en la línea 19. Las
líneas 21 a 35 iteran 10 veces para dibujar 10 figuras. La instrucción switch (líneas 24 a 34) elije entre dibujar
rectángulos y dibujar óvalos.
Si opcion es 1, entonces el programa dibuja un rectángulo. Las líneas 27 y 28 llaman al método drawRect de
Graphics. El método drawRect requiere cuatro argumentos. Los primeros dos representan las coordenadas x y y
de la esquina superior izquierda del rectángulo; los siguientes dos representan la anchura y la altura del rectángulo.
En este ejemplo, empezamos en la posición 10 píxeles hacia abajo y 10 píxeles a la derecha de la esquina superior
izquierda, y cada iteración del ciclo avanza la esquina superior izquierda otros 10 píxeles hacia abajo y a la derecha.
La anchura y la altura del rectángulo empiezan en 50 píxeles, y se incrementan por 10 píxeles en cada iteración.
Si opcion es 2, el programa dibuja un óvalo. Al dibujar un óvalo se crea un rectángulo imaginario llamado
rectángulo delimitador, y dentro de éste se crea un óvalo que toca los puntos medios de todos los cuatro lados
•
•
•
•
•
•
•
•
•
•
•
•
del rectángulo delimitador. El método drawOval (líneas 31 y 32) requiere los mismos cuatro argumentos que el
método drawRect. Los argumentos especifican la posición y el tamaño del rectángulo delimitador para el óvalo.
Los valores que se pasan a drawOval en este ejemplo son exactamente los mismos valores que se pasan a drawRect
en las líneas 27 y 28. Como la anchura y la altura del rectángulo delimitador son idénticas en este ejemplo, las
líneas 27 y 28 dibujan un círculo. Puede modificar el programa para dibujar rectángulos y óvalos, para ver cómo
se relacionan drawOval y drawRect.
La figura 5.27 es responsable de manejar la entrada del usuario y crear una ventana para mostrar el dibujo
apropiado, con base en la respuesta del usuario. La línea 3 importa a JFrame para manejar la pantalla, y la línea 4
importa a JOptionPane para manejar la entrada.
Las líneas 11 a 13 muestran un cuadro de diálogo al usuario y almacenan la respuesta de éste en la variable
entrada. La línea 15 utiliza el método parseInt de Integer para convertir el objeto String introducido por
el usuario en un int, y almacena el resultado en la variable opcion. En la línea 18 se crea una instancia de la
clase Figuras, y se pasa la opción del usuario al constructor. Las líneas 20 a 25 realizan las operaciones estándar
para crear y establecer una ventana: crear un marco, configurarlo para que la aplicación termine cuando se cierre,
agregar el dibujo al marco, establecer su tamaño y hacerlo visible.
Figura 5.26 | Cómo dibujar una cascada de figuras, con base en la opción elegida por el usuario.
1 // Fig. 5.26: Figuras.java
2 // Demuestra cómo dibujar distintas figuras.
3 import java.awt.Graphics;
4 import javax.swing.JPanel;
5
6 public class Figuras extends JPanel
7 {
8 private int opcion; // opción del usuario acerca de cuál figura dibujar
9
10 // el constructor establece la opción del usuario
11 public Figuras( int opcionUsuario )
12 {
13 opcion = opcionUsuario;
14 } // fin del constructor de Figuras
15
16 // dibuja una cascada de figuras, empezando desde la esquina superior izquierda
17 public void paintComponent( Graphics g )
18 {
19 super.paintComponent( g );
20
21 for ( int i = 0; i < 10; i++ )
22 {
23 // elije la figura con base en la opción del usuario
24 switch ( opcion )
25 {
26 case 1: // dibuja rectángulos
27 g.drawRect( 10 + i * 10, 10 + i * 10,
28 50 + i * 10, 50 + i * 10 );
29 break;
30 case 2: // dibuja óvalos
31 g.drawOval( 10 + i * 10, 10 + i * 10,
32 50 + i * 10, 50 + i * 10 );
33 break;
34 } // fin del switch
35 } // fin del for
36 } // fin del método paintComponent
37 } // fin de la clase Figuras
5.10 (Opcional) Ejemplo práctico de GUI y gráficos: dibujo de rectángulos y óvalos 195
196 Capítulo 5 Instrucciones de control: parte 2
Figura 5.27 | Cómo obtener datos de entrada del usuario y crear un objeto JFrame para mostrar figuras.
1 // Fig. 5.27: PruebaFiguras.java
2 // Aplicación de prueba que muestra la clase Figuras.
3 import javax.swing.JFrame;
4 import javax.swing.JOptionPane;
5
6 public class PruebaFiguras
7 {
8 public static void main( String args[] )
9 {
10 // obtiene la opción del usuario
11 String entrada = JOptionPane.showInputDialog(
12 "Escriba 1 para dibujar rectangulosn" +
13 "Escriba 2 para dibujar ovalos" );
14
15 int opcion = Integer.parseInt( entrada ); // convierte entrada en int
16
17 // crea el panel con la entrada del usuario
18 Figuras panel = new Figuras( opcion );
19
20 JFrame aplicacion = new JFrame(); // crea un nuevo objeto JFrame
21
22 aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
23 aplicacion.add( panel ); // agrega el panel al marco
24 aplicacion.setSize( 300, 300 ); // establece el tamaño deseado
25 aplicacion.setVisible( true ); // muestra el marco
26 } // fin de main
27 } // fin de la clase PruebaFiguras
Ejercicios del ejemplo práctico de GUI y gráficos
5.1 Dibuje 12 círculos concéntricos en el centro de un objeto JPanel (figura 5.28). El círculo más interno debe tener
un radio de 10 píxeles, y cada círculo sucesivo debe tener un radio 10 píxeles mayor que el anterior. Empiece por buscar el
centro del objeto JPanel. Para obtener la esquina superior izquierda de un círculo, avance un radio hacia arriba y un radio
a la izquierda, partiendo del centro. La anchura y la altura del rectángulo delimitador es el diámetro del círculo (el doble del
radio).
5.2 Modifique el ejercicio 5.16 de los ejercicios de fin de capítulo para leer la entrada usando cuadros de diálogo, y mos-
trar el gráfico de barras usando rectángulos de longitudes variables.
5.11 (Opcional) Ejemplo práctico de Ingeniería de Software:
cómo identificar los estados y actividades de los objetos
En la sección 4.15 identificamos muchos de los atributos de las clases necesarios para implementar el sistema
ATM, y los agregamos al diagrama de clases de la figura 4.24. En esta sección le mostraremos la forma en que
estos atributos representan el estado de un objeto. Identificaremos algunos estados clave que pueden ocupar nues-
tros objetos y hablaremos acerca de cómo cambian los objetos de estado, en respuesta a los diversos eventos que
ocurren en el sistema. También hablaremos sobre el flujo de trabajo, o actividades, que realizan los objetos en
el sistema ATM. En esta sección presentaremos las actividades de los objetos de transacción SolicitudSaldo y
Retiro.
Diagramas de máquina de estado
Cada objeto en un sistema pasa a través de una serie de estados. El estado actual de un objeto se indica mediante
los valores de los atributos del objeto en cualquier momento dado. Los diagramas de máquina de estado (que
se conocen comúnmente como diagramas de estado) modelan varios estados de un objeto y muestran bajo
qué circunstancias el objeto cambia de estado. A diferencia de los diagramas de clases que presentamos en las
secciones anteriores del ejemplo práctico, que se enfocaban principalmente en la estructura del sistema, los diagra-
mas de estado modelan parte del comportamiento del sistema.
La figura 5.29 es un diagrama de estado simple que modela algunos de los estados de un objeto de la clase
ATM. UML representa a cada estado en un diagrama de estado como un rectángulo redondeado con el nom-
bre del estado dentro de éste. Un círculo relleno con una punta de flecha designa el estado inicial. Recuerde
que en el diagrama de clases de la figura 4.24 modelamos esta información de estado como el atributo Boolean
de nombre usuarioAutenticado. Este atributo se inicializa en false, o en el estado “Usuario no autentica-
do”, de acuerdo con el diagrama de estado.
Las flechas indican las transiciones entre los estados. Un objeto puede pasar de un estado a otro, en respuesta
a los diversos eventos que ocurren en el sistema. El nombre o la descripción del evento que ocasiona una transi-
ción se escribe cerca de la línea que corresponde a esa transición. Por ejemplo, el objeto ATM cambia del estado
“Usuario no autenticado” al estado “Usuario autenticado”, una vez que la base de datos autentica al usuario. En
el documento de requerimientos vimos que para autenticar a un usuario, la base de datos compara el número de
Figura 5.28 | Cómo dibujar círculos concéntricos.
5.11 (Opcional) Ejemplo práctico de Ingeniería de Software: como identificar los estados y ... 197
198 Capítulo 5 Instrucciones de control: parte 2
cuenta y el NIP introducidos por el usuario con los de la cuenta correspondiente en la base de datos. Si la base
de datos indica que el usuario ha introducido un número de cuenta válido y el NIP correcto, el objeto ATM pasa
al estado “Usuario autenticado” y cambia su atributo usuarioAutenticado al valor true. Cuando el usuario
sale del sistema al seleccionar la opción “salir” del menú principal, el objeto ATM regresa al estado “Usuario no
autenticado”.
Figura 5.30 | Diagrama de actividad para un objeto SolicitudSaldo.
obtener saldo de cuenta de la base de datos
mostrar saldo en la pantalla
Usuario no autenticado Usuario autenticado
la base de datos del banco autentica al usuario
el usuario sale del sistema
Figura 5.29 | Diagrama de estado para el objeto ATM.
Observación de ingeniería de software 5.5
Por lo general, los diseñadores de software no crean diagramas de estado que muestren todos los posibles estados y
transiciones de estados para todos los atributos; simplemente hay demasiados. Lo común es que los diagramas de
estado muestren sólo los estados y las transiciones de estado importantes.
Diagramas de actividad
Al igual que un diagrama de estado, un diagrama de actividad modela los aspectos del comportamiento de un
sistema. A diferencia de un diagrama de estado, un diagrama de actividad modela el flujo de trabajo (secuencia
de objetos) de un objeto durante la ejecución de un programa. Un diagrama de actividad modela las accio-
nes a realizar y en qué orden las realizará el objeto. El diagrama de actividad de la figura 5.30 modela las acciones
involucradas en la ejecución de una transacción de solicitud de saldo. Asumimos que ya se ha inicializado un
objeto SolicitudSaldo y que ya se le ha asignado un número de cuenta válido (el del usuario actual), por lo
que el objeto sabe qué saldo extraer de la base de datos. El diagrama incluye las acciones que ocurren después
de que el usuario selecciona la opción de solicitud de saldo del menú principal y antes de que el ATM devuelva
al usuario al menú principal; un objeto SolicitudSaldo no realiza ni inicia estas acciones, por lo que no las mode-
lamos aquí. El diagrama empieza extrayendo de la base de datos el saldo de la cuenta. Después, SolicitudSaldo
muestra el saldo en la pantalla. Esta acción completa la ejecución de la transacción. Recuerde que hemos optado por
representar el saldo de una cuenta como los atributos saldoDisponible y saldoTotal de la clase Cuenta, por lo
que las acciones que se modelan en la figura 5.30 hacen referencia a la obtención y visualización de ambos atributos
del saldo.
UML representa una acción en un diagrama de actividad como un estado de acción, el cual se modela
mediante un rectángulo en el que sus lados izquierdo y derecho se sustituyen por arcos hacia fuera. Cada estado
de acción contiene una expresión de acción; por ejemplo, “obtener de la base de datos el saldo de la cuenta”; eso
especifica una acción a realizar. Una flecha conecta dos estados de acción, con lo cual indica el orden en el que
5.11 (Opcional) Ejemplo práctico de Ingeniería de Software: como identificar los estados y... 199
ocurren las acciones representadas por los estados de acción. El círculo relleno (en la parte superior de la figura
5.30) representa el estado inicial de la actividad: el inicio del flujo de trabajo antes de que el objeto realice las
acciones modeladas. En este caso, la transacción primero ejecuta la expresión de acción “obtener de la base de
datos el saldo de la cuenta”. Después, la transacción muestra ambos saldos en la pantalla. El círculo relleno ence-
rrado en un círculo sin relleno (en la parte inferior de la figura 5.30) representa el estado final: el fin del flujo de
trabajo una vez que el objeto realiza las acciones modeladas. Utilizamos diagramas de actividad de UML para
ilustrar el flujo de control para las instrucciones de control que presentamos en los capítulos 4 y 5.
La figura 5.31 muestra un diagrama de actividad para una transacción de retiro. Asumimos que ya se ha
asignado un número de cuenta válido a un objeto Retiro. No modelaremos al usuario seleccionando la opción
[el usuario seleccionó una cantidad]
[el usuario canceló la transacción]
[monto > saldo disponible]
[monto <= saldo disponible]
[hay suficiente efectivo disponible]
[no hay suficiente efectivo disponible]
muestra el menú de montos de retiro y la opción para cancelar
introduce la selección del menú
interactuar con la base de datos para cargar el monto a la cuenta del usuario
dispensar efectivo
instruir al usuario para que tome el efectivo
establecer atributo del monto
mostrar mensaje de error apropiado
evaluar si hay suficiente efectivo en el dispensador de efectivo
obtener de la base de datos el saldo disponible de la cuenta del usuario
Figura 5.31 | Diagrama de actividad para una transacción de retiro.
200 Capítulo 5 Instrucciones de control: parte 2
de retiro del menú principal ni al ATM devolviendo al usuario al menú principal, ya que estas acciones no las
realiza un objeto Retiro. La transacción primero muestra un menú de montos estándar de retiro (que se muestra
en la figura 2.19) y una opción para cancelar la transacción. Después la transacción recibe una selección del menú
de parte del usuario. Ahora el flujo de actividad llega a una decisión (una bifurcación indicada por el pequeño
símbolo de rombo). [Nota: en versiones anteriores de UML, una decisión se conocía como una bifurcación]. Este
punto determina la siguiente acción con base en la condición de guardia asociada (entre corchetes, enseguida de
la transición), que indica que la transición ocurre si se cumple esta condición de guardia. Si el usuario cancela la
transacción al elegir la opción “cancelar” del menú, el flujo de actividad salta inmediatamente al siguiente estado.
Observe la fusión (indicada mediante el pequeño símbolo de rombo), en donde el flujo de actividad de cance-
lación se combina con el flujo de actividad principal, antes de llegar al estado final de la actividad. Si el usuario
selecciona un monto de retiro del menú, Retiro establece monto (un atributo modelado originalmente en la
figura 4.24) al valor elegido por el usuario.
Después de establecer el monto de retiro, la transacción obtiene el saldo disponible de la cuenta del usuario
(es decir, el atributo saldoDisponible del objeto Cuenta del usuario) de la base de datos. Después el flujo de
actividad llega a otra decisión. Si el monto de retiro solicitado excede al saldo disponible del usuario, el sistema
muestra un mensaje de error apropiado, en el cual informa al usuario sobre el problema y después regresa al prin-
cipio del diagrama de actividad, y pide al usuario que introduzca un nuevo monto. Si el monto de retiro solicitado
es menor o igual al saldo disponible del usuario, la transacción continúa. A continuación, la transacción evalúa
si el dispensador de efectivo tiene suficiente efectivo para satisfacer la solicitud de retiro. Si éste no es el caso, la
transacción muestra un mensaje de error apropiado, después regresa al principio del diagrama de actividad y pide
al usuario que seleccione un nuevo monto. Si hay suficiente efectivo disponible, la transacción interactúa con la
base de datos para cargar el monto retirado de la cuenta del usuario (es decir, restar el monto tanto del atributo
saldoDisponible como del atributo saldoTotal del objeto Cuenta del usuario). Después la transacción entre-
ga el monto deseado de efectivo e instruye al usuario para que lo tome. Por último, el flujo de actividad se fusiona
con el flujo de actividad de cancelación antes de llegar al estado final.
Hemos llevado a cabo los primeros pasos para modelar el comportamiento del sistema ATM y hemos mos-
trado cómo participan los atributos de un objeto para realizar las actividades del mismo. En la sección 6.14 inves-
tigaremos los comportamientos para todas las clases, de manera que obtengamos una interpretación más precisa
del comportamiento del sistema, al “completar” los terceros compartimientos de las clases en nuestro diagrama de
clases.
Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
5.1 Indique si el siguiente enunciado es verdadero o falso y, si es falso, explique por qué: los diagramas de estado modelan
los aspectos estructurales de un sistema.
5.2 Un diagrama de actividad modela las (los) _________ que realiza un objeto y el orden en el que las(los) realiza.
a) acciones
b) atributos
c) estados
d) transiciones de estado
5.3 Con base en el documento de requerimientos, cree un diagrama de actividad para una transacción de depósito.
Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
5.1 Falso. Los diagramas de estado modelan parte del comportamiento del sistema.
5.2 a.
5.3 La figura 5.32 presenta un diagrama de actividad para una transacción de depósito. El diagrama modela las acciones
que ocurren una vez que el usuario selecciona la opción de depósito del menú principal, y antes de que el ATM regrese al usua-
rio al menú principal. Recuerde que una parte del proceso de recibir un monto de depósito de parte del usuario implica con-
vertir un número entero de centavos a una cantidad en dólares. Recuerde también que para acreditar un monto de depósito
a una cuenta sólo hay que incrementar el atributo saldoTotal del objeto Cuenta del usuario. El banco actualiza el atributo
saldoDisponible del objeto Cuenta del usuario sólo después de confirmar el monto de efectivo en el sobre de depósito y
después de verificar los cheques que haya incluido; esto ocurre en forma independiente del sistema ATM.
5.12 Conclusión
En este capítulo completamos nuestra introducción a las instrucciones de control de Java, las cuales nos permi-
ten controlar el flujo de la ejecución en los métodos. El capítulo 4 trató acerca de las instrucciones de control if,
if...else y while de Java. En este capítulo vimos el resto de las instrucciones de control de Java: for, do...whi-
le y switch. Aquí le mostramos que cualquier algoritmo puede desarrollarse mediante el uso de combinaciones
de instrucciones de secuencia (es decir, instrucciones que se listan en el orden en el que deben ejecutarse), los tres
tipos de instrucciones de selección (if, if...else y switch) y los tres tipos de instrucciones de repetición (while,
do...while y for). En este capítulo y en el anterior hablamos acerca de cómo puede combinar estos bloques
de construcción para utilizar las técnicas, ya probadas, de construcción de programas y solución de problemas.
En este capítulo también se introdujeron los operadores lógicos de Java, que nos permiten utilizar expresiones
condicionales más complejas en las instrucciones de control.
En el capítulo 3 presentamos los conceptos básicos de los objetos, las clases y los métodos. En los capítulos
4 y 5 se introdujeron los tipos de instrucciones de control que podemos utilizar para especificar la lógica de
los programas en métodos. En el capítulo 6 examinaremos los métodos con más detalle.
Figura 5.32 | Diagrama de actividad para una transacción de depósito.
[el usuario canceló la transacción]
[el usuario escribió un monto]
[se recibió el sobre de depósito]
[no se recibió el sobre
del depósito]
pedir al usuario que escriba un monto a depositar o que cancele
recibir la entrada del usuario
tratar de recibir el sobre de depósito
interactuar con la base de datos para abonar el monto a la cuenta del usuario
mostrar mensaje de error
establecer el atributo monto
instruir al usuario para que inserte el sobre de depósito
Resumen 201
Resumen
Sección 5.2 Fundamentos de la repetición controlada por contador
• La repetición controlada por contador requiere una variable de control (o contador de ciclo), el valor inicial de la
variable de control, el incremento (o decremento) en base al cual se modifica la variable de control cada vez que pasa
por el ciclo (lo que también se conoce como cada iteración del ciclo) y la condición de continuación de ciclo, que
determina si el ciclo debe seguir ejecutándose.
• Podemos declarar e inicializar una variable en la misma instrucción.
Sección 5.3 Instrucción de repetición for
• La instrucción while puede usarse para implementar cualquier ciclo controlado por contador.
• La instrucción de repetición for especifica los detalles acerca de la repetición controlada por contador, en una sola
línea de código.
• Cuando la instrucción for comienza a ejecutarse, su variable de control se declara y se inicializa. Después, el pro-
grama verifica la condición de continuación de ciclo. Si al principio la condición es verdadera, el cuerpo se ejecuta.
Después de ejecutar el cuerpo del ciclo, se ejecuta la expresión de incremento. Después, se lleva a cabo otra vez
la prueba de continuación de ciclo, para determinar si el programa debe continuar con la siguiente iteración del
ciclo.
• El formato general de la instrucción for es
for ( inicialización; condiciónDeContinuacionDeCiclo; incremento )
instrucción
en donde la expresión inicialización asigna un nombre a la variable de control del ciclo y, de manera opcional, pro-
porciona su valor inicial. condiciónDeContinuaciónDeCiclo es la condición que determina si el ciclo debe continuar
su ejecución, e incremento modifica el valor de la variable de control (posiblemente un incremento o decremento),
de manera que la condición de continuación de ciclo se vuelve falsa en un momento dado. Los dos signos de punto
y coma en el encabezado for son obligatorios.
• En la mayoría de los casos, la instrucción for se puede representar con una instrucción while equivalente, de la
siguiente forma:
inicialización;
while ( condiciónDeContinuaciónDeCiclo )
{
instrucción
incremento;
}
• Por lo general, las instrucciones for se utilizan para la repetición controlada por contador y las instrucciones while
para la repetición controlada por centinela.
• Si la expresión de inicialización en el encabezado del for declara la variable de control, ésta sólo puede usarse en esa
instrucción for; no existirá fuera de la instrucción for.
• Las tres expresiones en un encabezado for son opcionales. Si se omite la condiciónDeContinuaciónDeCiclo, Java asu-
me que la condición de continuación de ciclo siempre es verdadera, con lo cual se crea un ciclo infinito. Podríamos
omitir la expresión inicialización si el programa inicializa la variable de control antes del ciclo. Podríamos omitir la
expresión incremento si el programa calcula el incremento con instrucciones en el cuerpo del ciclo, o si no se necesita
un incremento.
• La expresión de incremento en un for actúa como si fuera una instrucción independiente al final del cuerpo del
for.
• El incremento de una instrucción for puede ser también negativo, en cuyo caso es en realidad un decremento, y el
ciclo cuenta en forma descendente.
• Si al principio la condición de continuación de ciclo es false, el programa no ejecuta el cuerpo de la instrucción
for. En vez de ello, la ejecución continúa con la instrucción después del for.
Sección 5.4 Ejemplos sobre el uso de la instrucción for
• Java trata a las constantes de punto flotante, como 1000.0 y 0.05, como de tipo double. De manera similar, Java
trata a las constantes de números enteros, como 7 y -22, como de tipo int.
202 Capítulo 5 Instrucciones de control: parte 2
• El especificador de formato %20s indica que el objeto String de salida debe mostrarse con una anchura de campo
de 20; es decir, printf muestra el valor con al menos 20 posiciones de caracteres. Si el valor a imprimir es menor de
20 posiciones de caracteres de ancho, se justifica a la derecha en el campo de manera predeterminada.
• Math.pow(x, y) calcula el valor de x elevado a la y-ésima potencia. El método recibe dos argumentos double y
devuelve un valor double.
• La bandera de formato coma (,) en un especificador de formato (por ejemplo, %,20.2f) indica que un valor de
punto flotante debe imprimirse con un separador de agrupamiento. El separador actual que se utiliza es específico
de la configuración regional del usuario (es decir, el país). Por ejemplo, en los Estados Unidos el número se impri-
mirá usando comas para separar cada tres dígitos, y un punto decimal para separar la parte fraccionaria del número,
como en 1,234.45.
• El .2 en un especificador de formato (por ejemplo, %,20.2f) indica la precisión de un número con formato; en
este caso, el número se redondea a la centésima más cercana y se imprime con dos dígitos a la derecha del punto
decimal.
Sección 5.5 Instrucción de repetición do...while
• La instrucción de repetición do...while es similar a la instrucción while. En la instrucción while, el programa eva-
lúa la condición de continuación de ciclo al principio del ciclo, antes de ejecutar su cuerpo; si la condición es falsa, el
cuerpo nunca se ejecuta. La instrucción do...while evalúa la condición de continuación de ciclo después de ejecutar
el cuerpo del ciclo; por lo tanto, el cuerpo siempre se ejecuta por lo menos una vez. Cuando termina una instrucción
do...while, la ejecución continúa con la siguiente instrucción en secuencia.
• No es necesario usar llaves en la instrucción de repetición do...while si sólo hay una instrucción en el cuerpo. Sin
embargo, la mayoría de los programadores incluyen las llaves, para evitar confusión entre las instrucciones while y
do...while.
Sección 5.6 Instrucción de selección múltiple switch
• La instrucción switch de selección múltiple realiza distintas acciones, con base en los posibles valores de una variable o
expresión entera. Cada acción se asocia con el valor de una expresión entera constante (es decir, un valor constante de
tipo byte, short, int o char, pero no long) que la variable o expresión en la que se basa el switch puede asumir.
• El indicador de fin de archivo es una combinación de teclas dependiente del sistema, que el usuario escribe para
indicar que no hay más datos qué introducir. En los sistemas UNIX/Linux/Mac OS X, el fin de archivo se introduce
escribiendo la secuencia <ctrl> d en una línea por sí sola. Esta notación significa que hay que imprimir al mismo
tiempo la tecla ctrl y la tecla d. En los sistemas Windows, el fin de archivo se puede introducir escribiendo <ctrl> z.
• El método hasNext de Scanner determina si hay más datos qué introducir. Este método devuelve el valor boolean
true si hay más datos; en caso contrario, devuelve false. Mientras no se haya escrito el indicador de fin de archivo,
el método hasNext devolverá true.
• La instrucción switch consiste en un bloque que contiene una secuencia de etiquetas case y un caso default opcional.
• Cuando el flujo de control llega al switch, el programa evalúa la expresión de control del switch. El programa com-
para el valor de la expresión de control (que debe evaluarse como un valor entero de tipo byte, char, short o int)
con cada etiqueta case. Si ocurre una coincidencia, el programa ejecuta las instrucciones para esa etiqueta case.
• Al enlistar etiquetas case en forma consecutiva, sin instrucciones entre ellas, permite que las etiquetas ejecuten el
mismo conjunto de instrucciones.
• La instrucción switch no cuenta con un mecanismo para evaluar rangos de valores, por lo que todo valor que deba
evaluarse tiene que enlistarse en una etiqueta case separada.
• Cada case puede tener varias instrucciones. La instrucción switch se diferencia de las otras instrucciones de con-
trol, en cuanto a que no requiere llaves alrededor de varias instrucciones en una etiqueta case.
• Sin las instrucciones break, cada vez que ocurre una coincidencia en el switch, las instrucciones para ese case y los
case subsiguientes se ejecutarán hasta llegar a una instrucción break o al final de la instrucción switch. A menudo
esto se conoce como “pasar” a las instrucciones en las etiquetas case subsiguientes.
• Si no ocurre una coincidencia entre el valor de la expresión de control y una etiqueta case, se ejecuta el caso default
opcional. Si no ocurre una coincidencia y la instrucción switch no tiene un caso default, el control del programa
simplemente continúa con la primera instrucción después del switch.
• La instrucción break no se requiere para la última etiqueta case de la instrucción switch (ni para el caso default
opcional, cuando aparece al último), ya que la ejecución continúa con la siguiente instrucción después del switch.
Sección 5.7 Instrucciones break y continue
• Además de las instrucciones de selección y repetición, Java cuenta con las instrucciones break y continue (que
presentamos en esta sección y en el apéndice N, Instrucciones break y continue etiquetadas) para alterar el flujo
Resumen 203
de control. La sección anterior mostró cómo se puede utilizar break para terminar la ejecución de una instrucción
switch. Esta sección habla acerca de cómo utilizar break en instrucciones de repetición.
• Cuando la instrucción break se ejecuta en una instrucción while, for, do...while o switch, provoca la salida
inmediata de esa instrucción. La ejecución continúa con la primera instrucción después de la instrucción de control.
• Cuando la instrucción continue se ejecuta en una instrucción while, for o do...while, omite el resto de las
instrucciones en el cuerpo del ciclo y continúa con la siguiente iteración del mismo. En las instrucciones while y
do...while, el programa evalúa la prueba de continuación de ciclo inmediatamente después de que se ejecuta la
instrucción continue. En una instrucción for, se ejecuta la expresión de incremento y después el programa evalúa
la prueba de continuación de ciclo.
Sección 5.8 Operadores lógicos
• Las condiciones simples se expresan en términos de los operadores relacionales >, <, >= y <=, y los operadores de
igualdad == y !=, y cada expresión sólo evalúa una condición.
• Los operadores lógicos nos permiten formar condiciones más complejas, mediante la combinación de condiciones
simples. Los operadores lógicos son && (AND condicional), || (OR condicional), & (AND lógico booleano), | (OR
inclusivo lógico booleano), ^ (OR exclusivo lógico booleano) y ! (NOT lógico).
• Para asegurar que dos condiciones sean ambas verdaderas antes de elegir cierta ruta de ejecución, utilice el operador
&& (AND condicional). Este operador da como resultado verdadero sí, y sólo si ambas de sus condiciones simples
son verdaderas. Si una o ambas condiciones simples son falsas, la expresión completa es falsa.
• Para asegurar que una o ambas condiciones sean verdaderas antes de elegir cierta ruta de ejecución, utilice el opera-
dor || (OR condicional), que se evalúa como verdadero si una o ambas de sus condiciones simples son verdaderas.
• Las partes de una expresión que contienen operadores && o || se evalúan sólo hasta que se conoce si la condición es
verdadera o falsa. Esta característica de las expresiones AND condicional y OR condicional se conoce como evalua-
ción de corto circuito.
• Los operadores AND lógico booleano (&) y OR inclusivo lógico booleano (|) funcionan de manera idéntica a los
operadores && (AND condicional) y || (OR condicional), con una excepción: los operadores lógicos boleanos siem-
pre evalúan ambos operandos (es decir, no realizan una evaluación de corto circuito).
• Una condición simple que contiene el operador OR exclusivo lógico booleano (^) es true si, y sólo si uno de sus
operandos es true y el otro es false. Si ambos operandos son true o ambos son false, toda la condición es false.
También se garantiza que este operador evaluará ambos operandos.
• El operador ! (NOT lógico, también conocido como negación lógica o complemento lógico) “invierte” el signifi-
cado de una condición. El operador de negación lógico es un operador unario, que sólo tiene una condición como
operando. Este operador se coloca antes de una condición, para elegir una ruta de ejecución si la condición original
(sin el operador de negación lógico) es false.
• En la mayoría de los casos, podemos evitar el uso de la negación lógica si expresamos la condición de manera distin-
ta, con un operador relacional o de igualdad apropiado.
Sección 5.10 (Opcional) Ejemplo práctico de GUI y gráficos: dibujo de rectángulos y óvalos
• Los métodos drawRect y drawOval de Graphics dibujan rectángulos y óvalos, respectivamente.
• El método drawRect de Graphics requiere cuatro argumentos. Los primeros dos representan las coordenadas x y y
de la esquina superior izquierda del rectángulo; los otros dos representan la anchura y la altura del rectángulo.
• Al dibujar un óvalo, se crea un rectángulo imaginario llamado rectángulo delimitador, y se coloca en su interior
un óvalo que toca los puntos medios de los cuatro lados del rectángulo delimitador. El método drawOval requiere
los mismos cuatro argumentos que el método drawRect. Los argumentos especifican la posición y el tamaño del
rectángulo delimitador para el óvalo.
Terminología
204 Capítulo 5 Instrucciones de control: parte 2
- (menos), bandera de formato
!, operador NOT lógico
%b, especificador de formato
&, operador AND lógico booleano
&&, operador AND condicional
, (coma), bandera de formato
^, operador OR exclusivo lógico booleano
|, operador OR lógico booleano
||, operador OR condicional
alcance de una variable
anchura de campo
AND condicional (&&)
AND lógico booleano (&)
break, instrucción
case, etiqueta
complemento lógico (!)
condición de continuación de ciclo
condición simple
constante de caracteres
continue, instrucción
decrementar una variable de control
default, caso en una instrucción switch
do…while, instrucción de repetición
drawOval, método de la clase Graphics (GUI)
drawRect, método de la clase Graphics (GUI)
efecto secundario
error por desplazamiento en 1
evaluación de corto circuito
expresión de control de una instrucción switch
expresión entera constante
final, palabra clave
for, encabezado
for, encabezado de la instrucción
for, instrucción de repetición
hasNext, método de la clase Scanner
incrementar una variable de control
indicador de fin de archivo
instrucción de repetición
instrucciones de control anidadas
instrucciones de control apiladas
instrucciones de control de una sola entrada/una sola
salida
iteración de un ciclo
justificar a la derecha
justificar a la izquierda
método ayudante
negación lógica (!)
operadores lógicos
OR condicional (||)
OR exclusivo lógico booleano (^)
OR inclusivo lógico booleano (|)
rectángulo delimitador de un óvalo (GUI)
regla de anidamiento
regla de apilamiento
selección múltiple
static, método
switch, instrucción de selección
tabla de verdad
valor inicial
variable constante
variable de control
Ejercicios de autoevaluación 205
Ejercicios de autoevaluación
5.1 Complete los siguientes enunciados:
a) Por lo general, las instrucciones ______________ se utilizan para la repetición controlada por contador y
las instrucciones ______________ se utilizan para la repetición controlada por centinela.
b) La instrucción do...while evalúa la condición de continuación de ciclo _______________ ejecutar el
cuerpo del ciclo; por lo tanto, el cuerpo siempre se ejecuta por lo menos una vez.
c) La instrucción _______________ selecciona una de varias acciones, con base en los posibles valores de una
variable o expresión entera.
d) Cuando se ejecuta la instrucción _______________ en una instrucción de repetición, se omite el resto de
las instrucciones en el cuerpo del ciclo y se continúa con la siguiente iteración del ciclo.
e) El operador ______________ se puede utilizar para asegurar que ambas condiciones sean verdaderas, antes
de elegir cierta ruta de ejecución.
f) Si al principio, la condición de continuación de ciclo en un encabezado for es _____________, el progra-
ma no ejecuta el cuerpo de la instrucción for.
g) Los métodos que realizan tareas comunes y no requieren objetos se llaman métodos _____________.
5.2 Conteste con verdadero o falso a cada una de las siguientes proposiciones; en caso de ser falso, explique por qué.
a) El caso default es requerido en la instrucción de selección switch.
b) La instrucción break es requerida en el último caso de una instrucción de selección switch.
c) La expresión ( ( x > y ) && ( a < b ) ) es verdadera si x > y es verdadera, o si a < b es verdadera.
d) Una expresión que contiene el operador || es verdadera si uno o ambos de sus operandos son verdaderos.
e) La bandera de formato coma (,) en un especificador de formato (por ejemplo, %,20.2f) indica que un
valor debe imprimirse con un separador de miles.
f) Para evaluar un rango de valores en una instrucción switch, use un guión corto (–) entre los valores inicial
y final del rango en una etiqueta case.
g) Al enlistar las instrucciones case en forma consecutiva, sin instrucciones entre ellas, pueden ejecutar el
mismo conjunto de instrucciones.
5.3 Escriba una instrucción o un conjunto de instrucciones en Java, para realizar cada una de las siguientes tareas:
a) Sumar los enteros impares entre 1 y 99, utilizando una instrucción for. Suponga que se han declarado las
variables enteras suma y cuenta.
b) Calcular el valor de 2.5 elevado a la potencia de 3, utilizando el método pow.
c) Imprimir los enteros del 1 al 20, utilizando un ciclo while y la variable contador i. Suponga que la variable
i se ha declarado, pero no se ha inicializado. Imprima solamente cinco enteros por línea. [Sugerencia: use el
cálculo i % 5. Cuando el valor de esta expresión sea 0, imprima un carácter de nueva línea; de lo contrario,
imprima un carácter de tabulación. Suponga que este código es una aplicación. Utilice el método System.
out.println() para producir el carácter de nueva línea, y el método System.out.print( ‘t’ ) para
producir el carácter de tabulación].
d) Repita la parte (c), usando una instrucción for.
5.4 Encuentre el error en cada uno de los siguientes segmentos de código, y explique cómo corregirlo:
a) i = 1;
while ( i <= 10 );
i++;
}
b) for ( k = 0.1; k != 1.0; k += 0.1 )
System.out.println ( k );
c) switch ( n )
{
case 1:
System.out.println( "El número es 1" );
case 2:
System.out.println( "El número es 2" );
break;
default:
System.out.println( "El número no es 1 ni 2" );
break;
}
d) El siguiente código debe imprimir los valores 1 a 10:
n = 1;
while ( n < 10 )
System.out.println( n++ );
Respuestas a los ejercicios de autoevaluación
5.1 a) for, while. b) después de. c) switch. d) continue. e) && (AND condicional). f) false. g) static.
5.2 a) Falso. El caso default es opcional. Si no se necesita una acción predeterminada, entonces no hay necesi-
dad de un caso default. b) Falso. La instrucción break se utiliza para salir de la instrucción switch. La instrucción
break no se requiere para el último caso en una instrucción switch. c) Falso. Ambas expresiones relacionales deben
ser verdaderas para que toda la expresión sea verdadera, cuando se utilice el operador &&. d) Verdadero. e) Verdadero.
f) Falso. La instrucción switch no cuenta con un mecanismo para evaluar rangos de valores, por lo que todo valor que
deba evaluarse se debe enlistar en una etiqueta case por separado. g) Verdadero.
5.3 a) suma = 0;
for ( cuenta = 1; cuenta <= 99; cuenta += 2 )
suma += cuenta;
b) double resultado = Math.pow( 2.5, 3 );
c) i = 1;
while ( i <= 20 )
{
System.out.print( i );
if ( i % 5 == 0 )
System.out.println()
else
System.out.print( 't' );
++i;
}
206 Capítulo 5 Instrucciones de control: parte 2
d) for ( i = 1; i <= 20; i++ )
{
System.out.print( i );
if ( i % 5 == 0 )
System.out.println();
else
System.out.print( 't' );
}
5.4 a) Error: el punto y coma después del encabezado while provoca un ciclo infinito, y falta una llave izquierda.
Corrección: reemplazar el punto y coma por una llave izquierda ({), o eliminar tanto el punto y coma (;)
como la llave derecha (}).
b) Error: utilizar un número de punto flotante para controlar una instrucción for tal vez no funcione, ya que
los números de punto flotante se representan sólo aproximadamente en la mayoría de las computadoras.
Corrección: utilice un entero, y realice el cálculo apropiado en orden para obtener los valores deseados:
for ( k = 1; k != 10, k++ )
System.out.println( ( double ) k / 10 );
c) Error: el código que falta es la instrucción break en las instrucciones del primer case.
Corrección: agregue una instrucción break al final de las instrucciones para el primer case. Observe que
esta omisión no es necesariamente un error, si el programador desea que la instrucción del case 2: se eje-
cute siempre que lo haga la instrucción del case 1:.
d) Error: se está utilizando un operador relacional inadecuado en la condición de continuación de la instruc-
ción de repetición while.
Corrección: use <= en vez de <, o cambie 10 a 11.
Ejercicios
5.5 Describa los cuatro elementos básicos de la repetición controlada por contador.
5.6 Compare y contraste las instrucciones de repetición while y for.
5.7 Hable sobre una situación en la que sería más apropiado usar una instrucción do...while que una instrucción
while. Explique por qué.
5.8 Compare y contraste las instrucciones break y continue.
5.9 Encuentre y corrija el(los) error(es) en cada uno de los siguientes fragmentos de código:
a) for ( i = 100, i >= 1, i++ )
System.out.println ( i );
b) El siguiente código debe imprimirse sin importar si el valor entero es par o impar:
switch ( valor % 2 )
{
case 0:
System.out.println( "Entero par" );
case 1:
System.out.println( "Entero impar" );
}
c) El siguiente código debe imprimir los enteros impares del 19 al 1:
for ( i = 19; i >= 1; i += 2 )
System.out.println( i );
d) El siguiente código debe imprimir los enteros pares del 2 al 100:
contador = 2;
do
{
System.out.println( contador );
contador += 2;
} While ( contador < 100 );
Ejercicios 207
5.10 ¿Qué es lo que hace el siguiente programa?
1 public class Imprimir
2 {
3 public static void main( String args[] )
4 {
5 for ( int i = 1; i <= 10; i++ )
6 {
7 for (int j = 1; j <= 5; j++ )
8 System.out.print( '@' );
9
10 System.out.println();
11 } // fin del for exterior
12 } // fin de main
13 } // fin de la clase Imprimir.
5.11 Escriba una aplicación que encuentre el menor de varios enteros. Suponga que el primer valor leído especifica
el número de valores que el usuario introducirá.
5.12 Escriba una aplicación que calcule el producto de los enteros impares del 1 al 15.
5.13 Los factoriales se utilizan frecuentemente en los problemas de probabilidad. El factorial de un entero positivo
n (se escribe como n!) es igual al producto de los enteros positivos del 1 a n. Escriba una aplicación que evalúe los fac-
toriales de los enteros del 1 al 5. Muestre los resultados en formato tabular. ¿Qué dificultad podría impedir que usted
calculara el factorial de 20?
5.14 Modifique la aplicación de interés compuesto de la figura 5.6, repitiendo sus pasos para las tasas de interés del
5, 6, 7, 8, 9 y 10%. Use un ciclo for para variar la tasa de interés.
5.15 Escriba una aplicación que muestre los siguientes patrones por separado, uno debajo del otro. Use ciclos for
para generar los patrones. Todos los asteriscos (*) deben imprimirse mediante una sola instrucción de la forma System.
out.print( '*' ); la cual hace que los asteriscos se impriman uno al lado del otro. Puede utilizarse una instrucción
de la forma System.out.println(); para posicionarse en la siguiente línea. Puede usarse una instrucción de la forma
System.out.print( ' ' ); para mostrar un espacio para los últimos dos patrones. No debe haber ninguna otra
instrucción de salida en el programa. [Sugerencia: los últimos dos patrones requieren que cada línea empiece con un
número apropiado de espacios en blanco].
(a) (b) (c) (d)
* ********** ********** *
** ********* ********* **
*** ******** ******** ***
**** ******* ******* ****
***** ****** ****** *****
****** ***** ***** ******
******* **** **** *******
******** *** *** ********
********* ** ** *********
********** * * **********
5.16 Una aplicación interesante de las computadoras es dibujar gráficos convencionales y de barra. Escriba una apli-
cación que lea cinco números, cada uno entre 1 y 30. Por cada número leído, su programa debe mostrar ese número de
asteriscos adyacentes. Por ejemplo, si su programa lee el número 7, debe mostrar *******.
5.17 Un almacén de pedidos por correo vende cinco productos cuyos precios de venta son los siguientes: producto
1, $2.98; producto 2, $4.50; producto 3, $9.98; producto 4, $4.49 y producto 5, $6.87. Escriba una aplicación que lea
una serie de pares de números, como se muestra a continuación:
a) número del producto;
b) cantidad vendida.
Su programa debe utilizar una instrucción switch para determinar el precio de venta de cada producto. Debe calcular y
mostrar el valor total de venta de todos los productos vendidos. Use un ciclo controlado por centinela para determinar
cuándo debe el programa dejar de iterar para mostrar los resultados finales.
208 Capítulo 5 Instrucciones de control: parte 2
5.18 Modifique la aplicación de la figura 5.6, de manera que se utilicen sólo enteros para calcular el interés compues-
to. [Sugerencia: trate todas las cantidades monetarias como números enteros de centavos. Luego divida el resultado en su
porción de dólares y su porción de centavos, utilizando las operaciones de división y residuo, respectivamente. Inserte
un punto entre las porciones de dólares y centavos].
5.19 Suponga que i = 1, j = 2, k = 3 y m = 2. ¿Qué es lo que imprime cada una de las siguientes instruc-
ciones?
a) System.out.println( i == 1 );
b) System.out.println( j == 3 );
c) System.out.println( ( i >= 1 ) && ( j < 4 ) );
d) System.out.println( ( m <= 99 ) & ( k < m ) );
e) System.out.println( ( j >= i ) || ( k == m ) );
f) System.out.println( ( k + m < j ) | ( 3 – j >= k ) );
g) System.out.println( !( k > m ) );
5.20 Calcule el valor de π a partir de la serie infinita
p 4 4
3
--
-
–
4
5
--
- 4
7
--
-
– 4
9
--
-
4
11
-----
-
– …
+ + +
=
Imprima una tabla que muestre el valor aproximado de π, calculando un término de esta serie, dos términos, tres, etcé-
tera. ¿Cuántos términos de esta serie tiene que utilizar para obtener 3.14? ¿3.141? ¿3.1415? ¿3.14159?
5.21 (Triples de Pitágoras) Un triángulo recto puede tener lados cuyas longitudes sean valores enteros. El conjunto
de tres valores enteros para las longitudes de los lados de un triángulo recto se conoce como triple de Pitágoras. Las
longitudes de los tres lados deben satisfacer la relación que establece que la suma de los cuadrados de dos lados es igual
al cuadrado de la hipotenusa. Escriba una aplicación para encontrar todos los triples de Pitágoras para lado1, lado2, y
la hipotenusa, que no sean mayores de 500. Use un ciclo for triplemente anidado para probar todas las posibilidades.
Este método es un ejemplo de la computación de “fuerza bruta”. En cursos de ciencias computacionales más avanzados
aprenderá que existen muchos problemas interesantes para los cuales no hay otra metodología algorítmica conocida,
más que el uso de la fuerza bruta.
5.22 Modifique el ejercicio 5.15 para combinar su código de los cuatro triángulos separados de asteriscos, de manera
que los cuatro patrones se impriman uno al lado del otro. [Sugerencia: utilice astutamente los ciclos for anidados].
5.23 (Leyes de De Morgan) En este capítulo, hemos hablado sobre los operadores lógicos &&, &, ||, |, ^ y !. Algunas
veces, las leyes de De Morgan pueden hacer que sea más conveniente para nosotros expresar una expresión lógica. Estas
leyes establecen que la expresión ! (condición1 && condición2) es lógicamente equivalente a la expresión (!condición1
|| !condición2). También establecen que la expresión !(condición1 || condición2) es lógicamente equivalente a la
expresión (!condición1 && !condición2). Use las leyes de De Morgan para escribir expresiones equivalentes para cada
una de las siguientes expresiones, luego escriba una aplicación que demuestre que, tanto la expresión original como la
nueva expresión, producen en cada caso el mismo valor:
a) !( x < 5 ) && !( y >= 7 )
b) !( a == b ) || !( g != 5 )
c) !( ( x <= 8 ) && ( y > 4 ) )
d) !( ( i > 4 ) || ( j <= 6 ) )
5.24 Escriba una aplicación que imprima la siguiente figura de rombo. Puede utilizar instrucciones de salida que
impriman un solo asterisco (*), un solo espacio o un solo carácter de nueva línea. Maximice el uso de la repetición (con
instrucciones for anidadas), y minimice el número de instrucciones de salida.
Ejercicios 209
*
***
*****
*******
*********
*******
*****
***
*
5.25 Modifique la aplicación que escribió en el ejercicio 5.24, para que lea un número impar en el rango de 1 a 19,
de manera que especifique el número de filas en el rombo. Su programa debe entonces mostrar un rombo del tamaño
apropiado.
5.26 Una crítica de las instrucciones break y continue es que ninguna es estructurada. En realidad, estas instruc-
ciones pueden reemplazarse en todo momento por instrucciones estructuradas, aunque hacerlo podría ser inadecuado.
Describa, en general, cómo eliminaría las instrucciones break de un ciclo en un programa, para reemplazarlas con
alguna de las instrucciones estructuradas equivalentes. [Sugerencia: la instrucción break se sale de un ciclo desde el
cuerpo de éste. La otra forma de salir es que falle la prueba de continuación de ciclo. Considere utilizar en la prueba
de continuación de ciclo una segunda prueba que indique una “salida anticipada debido a una condición de ‘interrup-
ción’”]. Use la técnica que desarrolló aquí para eliminar la instrucción break de la aplicación de la figura 5.12.
5.27 ¿Qué hace el siguiente segmento de programa?
for ( i = 1; i <= 5; i++ )
{
for ( j = 1; j <= 3; j++ )
{
for ( k = 1; k <= 4; k++ )
System.out.print( '*' );
System.out.println();
} // fin del for interior
System.out.println();
} // fin del for exterior
5.28 Describa, en general, cómo eliminaría las instrucciones continue de un ciclo en un programa, para reemplazar-
las con uno de sus equivalentes estructurados. Use la técnica que desarrolló aquí para eliminar la instrucción continue
del programa de la figura 5.13.
5.29 (Canción “Los Doce Días de Navidad”) Escriba una aplicación que utilice instrucciones de repetición y switch
para imprimir la canción “Los Doce Días de Navidad”. Una instrucción switch debe utilizarse para imprimir el día (es
decir, “primer”, “segundo”, etcétera). Una instrucción switch separada debe utilizarse para imprimir el resto de cada
verso. Visite el sitio Web en.wikipedia.org/wiki/Twelvetide para obtener la letra completa de la canción.
210 Capítulo 5 Instrucciones de control: parte 2
Métodos:
un análisis
más detallado
OBJETIVOS
En este capítulo aprenderá a:
Conocer cómo se asocian los métodos y los campos static
con toda una clase, en vez de asociarse con instancias
específicas de la clase.
Utilizar los métodos comunes de Math disponibles en la API
de Java.
Comprender los mecanismos para pasar información entre
métodos.
Comprender cómo se soporta el mecanismo de llamada/retorno
de los métodos mediante la pila de llamadas a métodos y los
registros de activación.
Conocer cómo los paquetes agrupan las clases relacionadas.
Utilizar la generación de números aleatorios para implementar
aplicaciones para juegos.
Comprender cómo se limita la visibilidad de las declaraciones a
regiones específicas de los programas.
Acerca de la sobrecarga de métodos y cómo crear métodos
sobrecargados.
Q
Q
Q
Q
Q
Q
Q
Q
La más grande invención
del siglo diecinueve fue la
invención del método de
la invención.
—Alfred North Whitehead
Llámame Ismael.
—Herman Melville
Cuando me llames así,
sonríe.
—Owen Wister
Respóndeme en una
palabra.
—William Shakespeare
¡Oh! volvió a llamar ayer,
ofreciéndome volver.
—William Shakespeare
Hay un punto en el cual
los métodos se devoran a sí
mismos.
—Frantz Fanon
6
212 Capítulo 6 Métodos: un análisis más detallado
6.1 Introducción
La mayoría de los programas de cómputo que resuelven los problemas reales son mucho más extensos que los
programas que se presentan en los primeros capítulos de este libro. La experiencia ha demostrado que la mejor
manera de desarrollar y mantener un programa extenso es construirlo a partir de pequeñas piezas sencillas, o
módulos. A esta técnica se le llama divide y vencerás. En el capítulo 3 presentamos los métodos, y en éste lo
estudiaremos con más detalle. Haremos énfasis en cómo declarar y utilizar métodos para facilitar el diseño, la
implementación, operación y el mantenimiento de programas extensos.
En breve verá que es posible que ciertos métodos, conocidos como static (métodos estáticos), puedan
llamarse sin necesidad de que exista un objeto de la clase a la que pertenecen. Aprenderá a declarar un método
con más de un parámetro. También aprenderá acerca de cómo Java es capaz de llevar el rastro de qué método se
ejecuta en un momento dado, cómo se mantienen las variables locales de los métodos en memoria y cómo sabe
un método a dónde regresar una vez que termina su ejecución.
Hablaremos brevemente sobre las técnicas de simulación mediante la generación de números aleatorios
y desarrollaremos una versión de un juego de dados conocido como “craps”, el cual utiliza la mayoría de las téc-
nicas de programación que usted ha aprendido hasta este capítulo. Además, aprenderá a declarar valores que no
pueden cambiar (es decir, constantes) en sus programas.
Muchas de las clases que utilizará o creará mientras desarrolla aplicaciones tendrán más de un método con el
mismo nombre. Esta técnica, conocida como sobrecarga, se utiliza para implementar métodos que realizan tareas
similares, para argumentos de distintos tipos, o para un número distinto de argumentos.
En el capítulo 15, Recursividad, continuaremos nuestra discusión sobre los métodos. La recursividad propor-
ciona una manera completamente distinta de pensar acerca de los métodos y los algoritmos.
6.2 Módulos de programas en Java
Existen tres tipos de módulos en Java: métodos, clases y paquetes. Para escribir programas en Java, se combinan
los nuevos métodos y clases que usted escribe con los métodos y clases predefinidos, que están disponibles en la
Interfaz de Programación de Aplicaciones de Java (también conocida como la API de Java o biblioteca de
6.1 Introducción
6.2 Módulos de programas en Java
6.3 Métodos static, campos static y la clase Math
6.4 Declaración de métodos con múltiples parámetros
6.5 Notas acerca de cómo declarar y utilizar los métodos
6.6 Pila de llamadas a los métodos y registros de activación
6.7 Promoción y conversión de argumentos
6.8 Paquetes de la API de Java
6.9 Ejemplo práctico: generación de números aleatorios
6.9.1 Escalamiento y desplazamiento generalizados de números aleatorios
6.9.2 Repetitividad de números aleatorios para prueba y depuración
6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones)
6.11 Alcance de las declaraciones
6.12 Sobrecarga de métodos
6.13 (Opcional) Ejemplo práctico de GUI y gráficos: colores y figuras rellenas
6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones de las clases
6.15 Conclusión
Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios
Pla
n
g
e
ne
r
a
l
clases de Java) y en diversas bibliotecas de clases. Por lo general, las clases relacionadas están agrupadas en paque-
tes, de manera que se pueden importar a los programas y reutilizarse. En el capítulo 8 aprenderá a agrupar sus
propias clases en paquetes. La API de Java proporciona una vasta colección de clases que contienen métodos para
realizar cálculos matemáticos, manipulaciones de cadenas, manipulaciones de caracteres, operaciones de entrada/
salida, comprobación de errores y muchas otras operaciones útiles.
Buena práctica de programación 6.1
Procure familiarizarse con la vasta colección de clases y métodos que proporciona la API de Java (java.sun.com/
javase/6/docs/api/). En la sección 6.8 presentaremos las generalidades acerca de varios paquetes comunes. En el
apéndice J, le explicaremos cómo navegar por la documentación de la API de Java.
Observación de ingeniería de software 6.1
Evite reinventar la rueda. Cuando sea posible, reutilice las clases y métodos de la API de Java. Esto reduce el tiempo
de desarrollo de los programas y evita que se introduzcan errores de programación.
Los métodos (también conocidos como funciones o procedimientos en otros lenguajes) permiten al progra-
mador dividir un programa en módulos, por medio de la separación de sus tareas en unidades autónomas. Usted
ha declarado métodos en todos los programas que ha escrito; a estos métodos se les conoce algunas veces como
métodos declarados por el programador. Las instrucciones en los cuerpos de los métodos se escriben sólo una
vez, y se reutilizan tal vez desde varias ubicaciones en un programa; además, están ocultas de otros métodos.
Una razón para dividir un programa en módulos mediante los métodos es la metodología “divide y vence-
rás”, que hace que el desarrollo de programas sea más fácil de administrar, ya que se pueden construir programas
a partir de piezas pequeñas y simples. Otra razón es la reutilización de software (usar los métodos existentes
como bloques de construcción para crear nuevos programas). A menudo se pueden crear programas a partir de
métodos estandarizados, en vez de tener que crear código personalizado. Por ejemplo, en los programas anteriores
no tuvimos que definir cómo leer los valores de datos del teclado; Java proporciona estas herramientas en la clase
Scanner. Una tercera razón es para evitar la repetición de código. El proceso de dividir un programa en métodos
significativos hace que el programa sea más fácil de depurar y mantener.
Observación de ingeniería de software 6.2
Para promover la reutilización de software, cada método debe limitarse de manera que realice una sola tarea bien
definida, y su nombre debe expresar esa tarea con efectividad. Estos métodos hacen que los programas sean más fáci-
les de escribir, depurar, mantener y modificar.
Tip para prevenir errores 6.1
Un método pequeño que realiza una tarea es más fácil de probar y depurar que un método más grande que realiza
muchas tareas.
Observación de ingeniería de software 6.3
Si no puede elegir un nombre conciso que exprese la tarea de un método, tal vez esté tratando de realizar diversas
tareas en un mismo método. Por lo general, es mejor dividirlo en varias declaraciones de métodos más pequeños.
Un método se invoca mediante una llamada, y cuando el método que se llamó completa su tarea, devuelve
un resultado, o simplemente el control al método que lo llamó. Una analogía a esta estructura de programa es
la forma jerárquica de la administración (figura 6.1). Un jefe (el solicitante) pide a un trabajador (el método lla-
mado) que realice una tarea y que le reporte (devuelva) los resultados después de completar la tarea. El método
jefe, no sabe cómo el método trabajador, realiza sus tareas designadas. Tal vez el trabajador llame a otros métodos
trabajadores, sin que lo sepa el jefe. Este “ocultamiento” de los detalles de implementación fomenta la buena
ingeniería de software. La figura 6.1 muestra al método jefe comunicándose con varios métodos trabajadores en
forma jerárquica. El método jefe divide las responsabilidades entre los diversos métodos trabajadores. Observe
que trabajador1 actúa como “método jefe” de trabajador4 y trabajador5.
6.2 Módulos de programas en Java 213
214 Capítulo 6 Métodos: un análisis más detallado
6.3 Métodos static, campos static y la clase Math
Toda clase proporciona métodos que realizan tareas comunes en objetos de esa clase. Por ejemplo, para introducir
datos mediante el teclado, hemos llamado métodos en un objeto Scanner que se inicializó en su constructor
para obtener la entrada del flujo de entrada estándar (System.in). Como aprenderá en el capítulo 14, Archivos
y flujos, puede inicializar un objeto Scanner que reciba información del flujo de entrada estándar, y un segundo
objeto Scanner que reciba información de un archivo. Cada método de entrada que se llame en el objeto Scan-
ner del flujo de entrada estándar obtendría su entrada del teclado, y cada método de entrada que se llame en el
objeto Scanner del archivo obtendría su entrada del archivo especificado en el disco.
Aunque la mayoría de los métodos se ejecutan en respuesta a las llamadas a métodos en objetos específicos,
éste no es siempre el caso. Algunas veces un método realiza una tarea que no depende del contenido de ningún
objeto. Dicho método se aplica a la clase en la que está declarado como un todo, y se conoce como método sta-
tic o método de clase. Es común que las clases contengan métodos static convenientes para realizar tareas
comunes. Por ejemplo, recuerde que en la figura 5.6 utilizamos el método static pow de la clase Math para elevar
un valor a una potencia. Para declarar un método como static, coloque la palabra clave static antes del tipo de
valor de retorno en la declaración del método. Puede llamar a cualquier método static especificando el nombre
de la clase en la que está declarado el método, seguido de un punto (.) y del nombre del método, como sigue:
NombreClase.nombreMétodo (argumentos)
Aquí utilizaremos varios métodos de la clase Math para presentar el concepto de los métodos static. La
clase Math cuenta con una colección de métodos que nos permiten realizar cálculos matemáticos comunes. Por
ejemplo, podemos calcular la raíz cuadrada de 900.0 con una llamada al siguiente método static:
Math.sqrt( 900.0 )
La expresión anterior se evalúa como 30.0. El método sqrt recibe un argumento de tipo double y devuelve un
resultado del mismo tipo. Para imprimir el valor de la llamada anterior al método en una ventana de comandos,
podríamos escribir la siguiente instrucción:
System.out.println( Math.sqrt( 900.0 ) );
En esta instrucción, el valor que devuelve sqrt se convierte en el argumento para el método println. Observe
que no hubo necesidad de crear un objeto Math antes de llamar al método sqrt. Observe también que todos los
métodos de la clase Math son static; por lo tanto, cada uno se llama anteponiendo al nombre del método el
nombre de la clase Math y el separador punto (.).
Observación de ingeniería de software 6.4
La clase Math es parte del paquete java.lang, que el compilador importa de manera implícita, por lo que no es
necesario importarla para utilizar sus métodos.
Figura 6.1 | Relación jerárquica entre el método jefe y los métodos trabajadores.
Jefe
trabajador2 trabajador3
trabajador1
trabajador5
trabajador4
Los argumentos para los métodos pueden ser constantes, variables o expresiones. Si c = 13.0, d = 3.0 y
f = 4.0, entonces la instrucción
System.out.println( Math.sqrt( c + d * f ) );
calcula e imprime la raíz cuadrada de 13.0 + 3.0 * 4.0 = 25.0; a saber, 5.0. La figura 6.2 sintetiza varios de
los métodos de la clase Math. En la figura 6.2, x y y son de tipo double.
Método Descripción Ejemplo
abs( x ) valor absoluto de x abs( 23.7 ) es 23.7
abs ( 0.0 ) es 0.0
abs( –23.7 ) es 23.7
ceil( x ) redondea x al entero más pequeño que no sea menor de x ceil( 9.2 ) es 10.0
ceil( -9.8 ) es -9.0
cos( x ) coseno trigonométrico de x (x está en radianes) cos( 0.0 ) es 1.0
exp( x ) método exponencial ex exp( 1.0 ) es 2.71828
exp( 2.0 ) es 7.38906
floor( x ) redondea x al entero más grande que no sea mayor de x floor( 9.2 ) es 9.0
floor( -9.8 ) es -10.0
log( x ) logaritmo natural de x (base e) log( Math.E ) es 1.0
log( Math.E * Math.E ) es 2.0
max( x, y ) el valor más grande de x y y max( 2.3, 12.7 ) es 12.7
max( -2.3, -12.7 ) es –2.3
min( x, y ) el valor más pequeño de x y y min( 2.3, 12.7 ) es 2.3
min( -2.3, -12.7 ) es -12.7
pow( x, y ) x elevado a la potencia y (xy) pow( 2.0, 7.0 ) es 128.0
pow( 9.0, 0.5 ) es 3.0
sin( x ) seno trigonométrico de x (x está en radianes) sin( 0.0 ) es 0.0
sqrt( x ) raíz cuadrada de x sqrt( 900.0 ) es 30.0
tan( x ) tangente trigonométrica de x (x está en radianes) tan( 0.0 ) es 0.0
Figura 6.2 | Métodos de la clase Math.
Constantes PI y E de la clase Math
La clase Math también declara dos campos que representan unas constantes matemáticas de uso común: Math.
PI y Math.E. La constante Math.PI (3.14159265358979323846) es la proporción de la circunferencia de un
círculo con su diámetro. La constante Math.E (2.7182818284590452354) es el valor de la base para los logarit-
mos naturales (que se calculan con el método static log de la clase Math). Estos campos se declaran en la clase
Math con los modificadores public, final y static. Al hacerlos public, otros programadores pueden utilizar
estos campos en sus propias clases. Cualquier campo declarado con la palabra clave final es constante; su valor no
puede modificarse después de inicializar el campo. Tanto PI como E se declaran como final, ya que sus valores
nunca cambian. Al hacer a estos campos static, se puede acceder a ellos mediante el nombre de clase Math y un
separador de punto (.), justo igual que los métodos de la clase Math. En la sección 3.5 vimos que cuando cada
objeto de una clase mantiene su propia copia de un atributo, el campo que representa a ese atributo se conoce
también como variable de instancia: cada objeto de la clase tiene una instancia separada de la variable en memoria.
Hay campos para los cuales cada objeto de una clase no tiene una instancia separada de ese campo. Éste es el caso
6.3 Métodos static, campos static y la clase Math 215
216 Capítulo 6 Métodos: un análisis más detallado
con los campos static, que se conocen también como variables de clase. Cuando se crean objetos de una clase
que contiene campos static, todos los objetos de la clase comparten una copia de los campos static de esa
clase. En conjunto, las variables de clase (es decir, las variables static) y las variables de instancia representan a
los campos de una clase. En la sección 8.11 aprenderá más acerca de los campos static.
¿Por qué el método main se declara como static?
¿Por qué main debe declararse como static? Cuando se ejecuta la Máquina Virtual de Java (JVM) con el coman-
do java, la JVM trata de invocar al método main de la clase que usted le especifica; cuando no se han creado obje-
tos de esa clase. Al declarar a main como static, la JVM puede invocar a main sin tener que crear una instancia
de la clase. El método main se declara con el siguiente encabezado:
public static void main( String args[ ] )
Cuando usted ejecuta su aplicación, especifica el nombre de su clase como un argumento para el comando
java, como sigue
java NombreClase argumento1 argumento2 …
La JVM carga la clase especificada por NombreClase y utiliza el nombre de esa clase para invocar al método main.
En el comando anterior, NombreClase es un argumento de línea de comandos para la JVM, que le indica cuál
clase debe ejecutar. Después del NombreClase, también puede especificar una lista de objetos String (separados
por espacios) como argumentos de línea de comandos, que la JVM pasará a su aplicación. Dichos argumen-
tos pueden utilizarse para especificar opciones (por ejemplo, un nombre de archivo) para ejecutar la aplicación.
Como aprenderá en el capítulo 7, Arreglos, su aplicación puede acceder a esos argumentos de línea de comandos
y utilizarlos para personalizar la aplicación.
Comentarios adicionales acerca del método main
En capítulos anteriores, todas las aplicaciones tenían una clase que sólo contenía a main, y posiblemente una
segunda clase que main utilizaba para crear y manipular objetos. En realidad, cualquier clase puede contener
un método main. De hecho, cada uno de nuestros ejemplos con dos clases podría haberse implementado como
una sola clase. Por ejemplo, en la aplicación de las figuras 5.9 y 5.10, el método main (líneas 6 a 16 de la figura
5.10) podría haberse tomado así como estaba, y colocarse en la clase LibroCalificaciones (figura 5.9). Des-
pués, para ejecutar la aplicación, sólo habría que escribir el comando java LibroCalificaciones en la ventana
de comandos; los resultados de la aplicación serían idénticos a los de la versión con dos clases. Puede colocar un
método main en cada clase que declare. La JVM invoca sólo al método main en la clase que se utiliza para ejecutar
la aplicación. Algunos programadores aprovechan esto para crear un pequeño programa de prueba en cada clase
que declaran.
6.4 Declaración de métodos con múltiples parámetros
Los capítulos 3 a 5 presentaron clases que contienen métodos simples, que a lo más tenían un parámetro.
A menudo, los métodos requieren más de una pieza de información para realizar sus tareas. Ahora le mostraremos
cómo escribir métodos con varios parámetros.
La aplicación de las figuras 6.3 y 6.4 utiliza el método maximo, declarado por el programador, para determinar
y devolver el mayor de tres valores double que introduce el usuario. Cuando la aplicación empieza a ejecutarse,
el método main de la clase PruebaBuscadorMaximo (líneas 7 a 11 de la figura 6.4) crea un objeto de la clase
BuscadorMaximo (línea 9) y llama al método determinarMaximo del objeto (línea 10) para producir los resul-
tados del programa. En la clase BuscadorMaximo (figura 6.3), las líneas 14 a18 del método determinarMaxi-
mo piden al usuario que introduzca tres valores double, y después los leen. La línea 21 llama al método maximo
(declarado en las líneas 28 a 41) para determinar el mayor de los tres valores double que se pasan como argumen-
tos para el método. Cuando el método maximo devuelve el resultado a la línea 21, el programa asigna el valor de
retorno de maximo a la variable local resultado. Después, la línea 24 imprime el valor máximo. Al final de esta
sección, hablaremos sobre el uso del operador + en la línea 24.
Considere la declaración del método maximo (líneas 28 a 41). La línea 28 indica que el método devuelve un
valor double, que el nombre del método es maximo y que el método requiere tres parámetros double (x, y y z)
para realizar su tarea. Cuando un método tiene más de un parámetro, éstos se especifican como una lista separada
1 // Fig. 6.3: BuscadorMaximo.java
2 // Método maximo, declarado por el programador.
3 import java.util.Scanner;
4
5 public class BuscadorMaximo
6 {
7 // obtiene tres valores de punto flotante y determina el valor máximo
8 public void determinarMaximo()
9 {
10 // crea objeto Scanner para introducir datos desde la ventana de comandos
11 Scanner entrada = new Scanner( System.in );
12
13 // pide y recibe como entrada tres valores de punto flotante
14 System.out.print(
15 "Escriba tres valores de punto flotante, separados por espacios: " );
16 double numero1 = entrada.nextDouble(); // lee el primer valor double
17 double numero2 = entrada.nextDouble(); // lee el segundo valor double
18 double numero3 = entrada.nextDouble(); // lee el tercer valor double
19
20 // determina el valor máximo
21 double resultado = maximo( numero1, numero2, numero3 );
22
23 // muestra el valor máximo
24 System.out.println( "El maximo es: " + resultado );
25 } // fin del método determinarMaximo
26
27 // devuelve el máximo de sus tres parámetros double
28 public double maximo( double x, double y, double z )
29 {
30 double valorMaximo = x; // asume que x es el mayor para empezar
31
32 // determina si y es mayor que valorMaximo
33 if ( y > valorMaximo )
34 valorMaximo = y;
35
36 // determina si z es mayor que valorMaximo
37 if ( z > valorMaximo )
38 valorMaximo = z;
39
40 return valorMaximo;
41 } // fin del método maximo
42 } // fin de la clase BuscadorMaximo
Figura 6.3 | Método maximo, declarado por el programador, que tiene tres parámetros double.
1 // Fig. 6.4: PruebaBuscadorMaximo.java
2 // Aplicación para evaluar la clase BuscadorMaximo.
3
4 public class PruebaBuscadorMaximo
5 {
6 // punto de inicio de la aplicación
7 public static void main( String args[] )
8 {
9 BuscadorMaximo buscadorMaximo = new BuscadorMaximo();
10 buscadorMaximo.determinarMaximo();
11 } // fin de main
12 } // fin de la clase PruebaBuscadorMaximo
Figura6.4 | Aplicación para evaluar la clase BuscadorMaximo. (Parte 1 de 2).
6.4 Declaración de métodos con múltiples parámetros 217
218 Capítulo 6 Métodos: un análisis más detallado
por comas. Cuando se hace la llamada a maximo en la línea 21, el parámetro x se inicializa con el valor del argu-
mento numero1, el parámetro y se inicializa con el valor del argumento numero2 y el parámetro z se inicializa con
el valor del argumento numero3. Debe haber un argumento en la llamada al método para cada parámetro (algunas
veces conocido como parámetro formal) en la declaración del método. Además, cada argumento debe ser consis-
tente con el tipo del parámetro correspondiente. Por ejemplo, un parámetro de tipo double puede recibir valores
como 7.35, 22 o –0.03456, pero no objetos String como "hola", ni los valores booleanos true o false. En
la sección 6.7 veremos los tipos de argumentos que pueden proporcionarse en la llamada a un método para cada
parámetro de un tipo simple.
Para determinar el valor máximo, comenzamos con la suposición de que el parámetro x contiene el valor más
grande, por lo que la línea 30 declara la variable local valorMaximo y la inicializa con el valor del parámetro x. Des-
de luego, es posible que el parámetro y o z contenga el valor más grande, por lo que debemos comparar cada
uno de estos valores con valorMaximo. La instrucción if en las líneas 33 y 34 determina si y es mayor que
valorMaximo. De ser así, la línea 34 asigna y a valorMaximo. La instrucción if en las líneas 37 y 38 determina
si z es mayor que valorMaximo. De ser así, la línea 38 asigna z a valorMaximo. En este punto, el mayor de los
tres valores reside en valorMaximo, por lo que la línea 40 devuelve ese valor a la línea 21. Cuando el control del
programa regresa al punto en donde se llamó al método maximo, los parámetros x, y y z de maximo ya no están
accesibles en la memoria. Observe que los métodos pueden devolver a lo máximo un valor, pero el valor devuelto
puede ser una referencia a un objeto que contenga muchos valores.
Observe que resultado es una variable local en el método determinarMaximo, ya que se declara en el
bloque que representa el cuerpo del método. Las variables deben declararse como campos de una clase sólo si se
requiere su uso en más de un método de la clase, o si el programa debe almacenar sus valores entre las llamadas a
los métodos de la clase.
Error común de programación 6.1
Declarar parámetros del mismo tipo para un método, como float x, y en vez de float x, float y es un error de
sintaxis; se requiere un tipo para cada parámetro en la lista de parámetros.
Observación de ingeniería de software 6.5
Un método que tiene muchos parámetros puede estar realizando demasiadas tareas. Considere dividir el método
en métodos más pequeños que realicen las tareas separadas. Como lineamiento, trate de ajustar el encabezado del
método en una línea, si es posible.
Implementación del método maximo mediante la reutilización del método Math.max
En la figura 6.2 vimos que la clase Math tiene un método max, el cual puede determinar el mayor de dos valores.
Todo el cuerpo de nuestro método para encontrar el valor máximo podría también implementarse mediante dos
llamadas a Math.max, como se muestra a continuación:
return Math.max( x, Math.max( y, z ) );
La primera llamada a Math.max especifica los argumentos x y Math.max( y, z ). Antes de poder llamar a cual-
quier método, todos sus argumentos deben evaluarse para determinar sus valores. Si un argumento es una llamada
Figura 6.4 | Aplicación para probar la clase BuscadorMaximo. (Parte 2 de 2).
Escriba tres valores de punto flotante, separados por espacios: 6.46 4.12 10.54
El maximo es: 10.54
Escriba tres valores de punto flotante, separados por espacios: 9.35 2.74 5.1
El maximo es: 9.35
Escriba tres valores de punto flotante, separados por espacios: 5.8 12.45 8.32
El maximo es: 12.45
a un método, es necesario realizar esta llamada para determinar su valor de retorno. Por lo tanto, en la instrucción
anterior, primero se evalúa Math.max( y, z ) para determinar el máximo entre y y z. Después el resultado se
pasa como el segundo argumento para la otra llamada a Math.max, que devuelve el mayor de sus dos argumentos.
Éste es un buen ejemplo de la reutilización de software: buscamos el mayor de los tres valores reutilizando Math.
max, el cual busca el mayor de dos valores. Observe lo conciso de este código, en comparación con las líneas 30
a 40 de la figura 6.3.
Ensamblado de cadenas mediante la concatenación
Java permite crear objetos String mediante el ensamblado de objetos string más pequeños para formar objetos
string más grandes, mediante el uso del operador + (o del operador de asignación compuesto +=). A esto se le
conoce como concatenación de objetos string. Cuando ambos operandos del operador + son objetos String,
el operador + crea un nuevo objeto String en el cual los caracteres del operando derecho se colocan al final de
los caracteres en el operando izquierdo. Por ejemplo, la expresión "hola" + "a todos" crea el objeto String
"hola a todos".
En la línea 24 de la figura 6.3, la expresión "El maximo es: " + resultado utiliza el operador + con ope-
randos de tipo String y double. Cada valor primitivo y cada objeto en Java tienen una representación String.
Cuando uno de los operandos del operador + es un objeto String, el otro se convierte en String y después se
concatenan los dos. En la línea 24, el valor double se convierte en su representación string y se coloca al final
del objeto String "El maximo es: ". Si hay ceros a la derecha en un valor double, éstos se descartan cuando el
número se convierte en objeto String. Por ende, el número 9.3500 se representa como 9.35 en el objeto String
resultante.
Los valores primitivos que se utilizan en la concatenación de objetos String se convierten en objetos String. Si
un valor boolean se concatena con un objeto String, el valor boolean se convierte en el objeto String "true"
o "false". Todos los objetos tienen un método llamado toString que devuelve una representación String del
objeto. Cuando se concatena un objeto con un String, se hace una llamada implícita al método toString de
ese objeto para obtener la representación String del mismo. En el capítulo 7, Arreglos, aprenderá más acerca
del método toString.
Cuando se escribe una literal String extensa en el código fuente de un programa, algunas veces los progra-
madores prefieren dividir ese objeto String en varios objetos String más pequeños, para colocarlos en varias
líneas de código y mejorar la legibilidad. En este caso, los objetos String pueden reensamblarse mediante el
uso de la concatenación. En el capítulo 30, Cadenas, caracteres y expresiones regulares, hablaremos sobre los
detalles de los objetos String.
Error común de programación 6.2
Es un error de sintaxis dividir una literal String en varias líneas en un programa. Si una literal String no cabe
en una línea, divídala en objetos String más pequeños y utilice la concatenación para formar la literal String
deseada.
Error común de programación 6.3
Confundir el operador +, que se utiliza para la concatenación de cadenas, con el operador + que se utiliza para la
suma, puede producir resultados extraños. Java evalúa los operandos de un operador de izquierda a derecha. Por
ejemplo, si la variable entera y tiene el valor 5, la expresión "y + 2 = " + y + 2 produce la cadena "y + 2 =
52", no "y + 2 = 7", ya que primero el valor de y (5) se concatena con la cadena "y + 2 =" y después el valor 2 se
concatena con la nueva cadena "y + 2 = 5" más larga. La expresión "y + 2 =" + (y + 2) produce el resultado
deseado "y + 2 = 7".
6.5 Notas acerca de cómo declarar y utilizar los métodos
Hay tres formas de llamar a un método:
1. Utilizando el nombre de un método por sí solo para llamar a otro método de la misma clase, como
maximo( numero1, numero2, numero3 ) en la línea 21 de la figura 6.3.
6.5 Notas acerca de cómo declarar y utilizar los métodos 219
220 Capítulo 6 Métodos: un análisis más detallado
2. Utilizando una variable que contiene una referencia a un objeto, seguida de un punto (.) y del nombre
del método para llamar a un método del objeto al que se hace referencia, como en la línea 10 de la figura
6.4, buscadorMaximo.determinarMaximo(), con lo cual se llama a un método de la clase Buscador-
Maximo desde el método main de PruebaBuscadorMaximo.
3. Utilizando el nombre de la clase y un punto (.) para llamar a un método static de una clase, como
Math.sqrt( 900.0 ) en la sección 6.3.
Observe que un método static sólo puede llamar directamente a otros métodos static de la misma clase
(es decir, usando el nombre del método por sí solo) y solamente puede manipular de manera directa campos
static en la misma clase. Para acceder a los miembros no static de la clase, un método static debe usar una
referencia a un objeto de esa clase. Recuerde que los métodos static se relacionan con una clase como un todo,
mientras que los métodos no static se asocian con una instancia específica (objeto) de la clase y pueden mani-
pular las variables de instancia de ese objeto. Es posible que existan muchos objetos de una clase al mismo tiempo,
cada uno con sus propias copias de las variables de instancia. Suponga que un método static invoca a un método
no static en forma directa. ¿Cómo sabría el método qué variables de instancia manipular de cuál objeto? ¿Qué
ocurriría si no existieran objetos de la clase en el momento en el que se invocara el método no static? Es evidente
que tal situación sería problemática. Por lo tanto, Java no permite que un método static acceda directamente a
los miembros no static de la misma clase.
Existen tres formas de regresar el control a la instrucción que llama a un método. Si el método no devuelve
un resultado, el control regresa cuando el flujo del programa llega a la llave derecha de terminación del método,
o cuando se ejecuta la instrucción
return;
si el método devuelve un resultado, la instrucción
return expresión;
evalúa la expresión y después devuelve el resultado al método que hizo la llamada.
Error común de programación 6.4
Declarar un método fuera del cuerpo de la declaración de una clase, o dentro del cuerpo de otro método es un error
de sintaxis.
Error común de programación 6.5
Omitir el tipo de valor de retorno en la declaración de un método es un error de sintaxis.
Error común de programación 6.6
Colocar un punto y coma después del paréntesis derecho que encierra la lista de parámetros de la declaración de un
método es un error de sintaxis.
Error común de programación 6.7
Volver a declarar el parámetro de un método como una variable local en el cuerpo de ese método es un error de
compilación.
Error común de programación 6.8
Olvidar devolver un valor de un método que debe devolver un valor es un error de compilación. Si se especifica un
tipo de valor de retorno distinto de void, el método debe contener una instrucción return que devuelva un valor
consistente con el tipo de valor de retorno del método. Devolver un valor de un método cuyo tipo de valor de retorno
se haya declarado como void es un error de compilación.
6.6 Pila de llamadas a los métodos y registros de activación
Para comprender la forma en que Java realiza las llamadas a los métodos, necesitamos considerar primero una
estructura de datos (es decir, una colección de elementos de datos relacionados) conocida como pila. Los estu-
diantes pueden considerar una pila como una analogía de una pila de platos. Cuando se coloca un plato en la
pila, por lo general se coloca en la parte superior (lo que se conoce como meter el plato en la pila). De manera
similar, cuando se extrae un plato de la pila, siempre se extrae de la parte superior (lo que se conoce como sacar
el plato de la pila). Las pilas se denominan estructuras de datos “último en entrar, primero en salir” (UEPS;
LIFO, por las siglas en inglés de last-in, first-out); el último elemento que se mete (inserta) en la pila es el primero
que se saca (extrae) de ella.
Cuando una aplicación llama a un método, el método llamado debe saber cómo regresar al que lo llamó, por
lo que la dirección de retorno del método que hizo la llamada se mete en la pila de ejecución del programa (tam-
bién conocida como pila de llamadas a los métodos). Si ocurre una serie de llamadas a métodos, las direcciones
de retorno sucesivas se meten en la pila, en el orden “último en entrar, primero en salir”, para que cada método
pueda regresar al que lo llamó.
La pila de ejecución del programa también contiene la memoria para las variables locales que se utilizan en
cada invocación de un método, durante la ejecución de un programa. Estos datos, que se almacenan como una
porción de la pila de ejecución del programa, se conocen como el registro de activación o marco de pila de la
llamada a un método. Cuando se hace la llamada a un método, el registro de activación para la llamada se mete
en la pila de ejecución del programa. Cuando el método regresa al que lo llamó, el registro de activación para esa
llamada al método se saca de la pila y esas variables locales ya no son conocidas para el programa. Si una variable
local que contiene una referencia a un objeto es la única variable en el programa con una referencia a ese objeto,
cuando se saca de la pila el registro de activación que contiene a esa variable local, el programa ya no puede acce-
der a ese objeto, y la JVM lo eliminará de la memoria en algún momento dado, durante la “recolección de basura”.
En la sección 8.10 hablaremos sobre la recolección de basura.
Desde luego que la cantidad de memoria en una computadora es finita, por lo que sólo puede utilizarse cierta
cantidad de memoria para almacenar los registros de activación en la pila de ejecución del programa. Si ocurren
más llamadas a métodos de las que se puedan almacenar sus registros de activación en la pila de ejecución del
programa, se produce un error conocido como desbordamiento de pila.
6.7 Promoción y conversión de argumentos
Otra característica importante de las llamadas a los métodos es la promoción de argumentos: convertir el valor
de un argumento al tipo que el método espera recibir en su correspondiente parámetro. Por ejemplo, una aplica-
ción puede llamar al método sqrt de Math con un argumento entero, aun cuando el método espera recibir un
argumento double (pero no viceversa, como pronto veremos). La instrucción
System.out.println( Math.sqrt( 4 ) );
evalúa Math.sqrt( 4 ) correctamente e imprime el valor 2.0. La lista de parámetros de la declaración del méto-
do hace que Java convierta el valor int 4 en el valor double 4.0 antes de pasar ese valor a sqrt. Tratar de realizar
estas conversiones puede ocasionar errores de compilación, si no se satisfacen las reglas de promoción de Java.
Las reglas de promoción especifican qué conversiones son permitidas; esto es, qué conversiones pueden realizarse
sin perder datos. En el ejemplo anterior de sqrt, un int se convierte en double sin modificar su valor. No obs-
tante, la conversión de un double a un int trunca la parte fraccionaria del valor double; por consecuencia, se
pierde parte del valor. La conversión de tipos de enteros largos a tipos de enteros pequeños (por ejemplo, de long
a int) puede también producir valores modificados.
Las reglas de promoción se aplican a las expresiones que contienen valores de dos o más tipos simples, y a los
valores de tipos simples que se pasan como argumentos para los métodos. Cada valor se promueve al tipo “más
alto” en la expresión. (En realidad, la expresión utiliza una copia temporal de cada valor; los tipos de los valores
originales permanecen sin cambios). La figura 6.5 lista los tipos primitivos y los tipos a los cuales se puede promo-
ver cada uno de ellos. Observe que las promociones válidas para un tipo dado siempre se realizan a un tipo más
alto en la tabla. Por ejemplo, un int puede promoverse a los tipos más altos long, float y double.
Al convertir valores a tipos inferiores en la tabla de la figura 6.5, se producirán distintos valores si el tipo
inferior no puede representar el valor del tipo superior (por ejemplo, el valor int 2000000 no puede represen-
tarse como un short, y cualquier número de punto flotante con dígitos después de su punto decimal no pueden
6.7 Promoción y conversión de argumentos 221
222 Capítulo 6 Métodos: un análisis más detallado
representarse en un tipo entero como long, int o short). Por lo tanto, en casos en los que la información puede
perderse debido a la conversión, el compilador de Java requiere que utilicemos un operador de conversión (el
cual presentamos en la sección 4.9) para forzar explícitamente la conversión; en caso contrario, ocurre un error
de compilación. Eso nos permite “tomar el control” del compilador. En esencia decimos, “Sé que esta conver-
sión podría ocasionar pérdida de información, pero para mis fines aquí, eso está bien”. Suponga que el método
cuadrado calcula el cuadrado de un entero y por ende requiere un argumento int. Para llamar a cuadrado con un
argumento double llamado valorDouble, tendríamos que escribir la llamada al método de la siguiente forma:
cuadrado( (int) valorDouble )
La llamada a este método convierte explícitamente el valor de valorDouble a un entero, para usarlo en el método
cuadrado. Por ende, si el valor de valorDouble es 4.5, el método recibe el valor 4 y devuelve 16, no 20.25.
Tipo Promociones válidas
double Ninguna
float double
long float o double
int long, float o double
char int, long, float o double
short int, long, float o double (pero no char)
byte short, int, long, float o double (pero no char)
boolean Ninguna (los valores boolean no se consideran números en Java)
Figura 6.5 | Promociones permitidas para los tipos primitivos.
Error común de programación 6.9
Convertir un valor de tipo primitivo a otro tipo primitivo puede modificar ese valor, si el nuevo tipo no es una
promoción válida. Por ejemplo, convertir un valor de punto flotante a un valor entero puede introducir errores de
truncamiento (pérdida de la parte fraccionaria) en el resultado.
6.8 Paquetes de la API de Java
Como hemos visto, Java contiene muchas clases predefinidas que se agrupan en categorías de clases relacionadas,
llamadas paquetes. En conjunto, nos referimos a estos paquetes como la Interfaz de programación de aplicaciones
de Java (API de Java), o biblioteca de clases de Java.
A lo largo del texto, las declaraciones import especifican las clases requeridas para compilar un programa en
Java. Por ejemplo, un programa incluye la declaración
import java.util.Scanner;
para especificar que el programa utiliza la clase Scanner del paquete java.util. Esto permite a los programa-
dores utilizar el nombre de la clase Scanner, en vez de tener que usar el nombre completo calificado de la clase,
java.util.Scanner, en el código. Uno de los puntos más fuertes de Java es el extenso número de clases en
los paquetes de la API de Java. Algunos paquetes clave se describen en la figura 6.6, que representa sólo una
pequeña parte de los componentes reutilizables en la API de Java. Mientras esté aprendiendo este lenguaje, invier-
ta una parte de su tiempo explorando las descripciones de los paquetes y las clases en la documentación para la
API de Java (java.sun.com/javase/6/docs/api/).
El conjunto de paquetes disponibles en Java SE 6 es bastante extenso. Además de los paquetes sintetizados en
la figura 6.6, Java SE 6 incluye paquetes para gráficos complejos, interfaces gráficas de usuario avanzadas, impre-
Paquete Descripción
java.applet El Paquete Applet de Java contiene una clase y varias interfaces requeridas para crear applets
de Java; programas que se ejecutan en los navegadores Web. (En el capítulo 20, Introducción
a las applets de Java, hablaremos sobre las applets; en el capítulo 10, Programación orientada a
objetos: polimorfismo, hablaremos sobre las interfaces).
java.awt El Paquete Abstract Window Toolkit de Java contiene las clases e interfaces requeridas para
crear y manipular GUIs en Java 1.0 y 1.1. En las versiones actuales de Java, se utilizan con
frecuencia los componentes de la GUI de Swing, incluidos en los paquetes javax.swing.
(Algunos elementos del paquete java.awt se describen en el capítulo 11, Componentes de
la GUI: parte 1, en el capítulo 12, Gráficos y Java 2D™, y en el capítulo 22, Componentes
de la GUI: parte 2).
java.awt.event El Paquete Abstract Window Toolkit Event de Java contiene clases e interfaces que habili-
tan el manejo de eventos para componentes de la GUI en los paquetes java.awt y javax.
swing. (Aprenderá más acerca de este paquete en el capítulo 11, Componentes de la GUI:
parte 1, y en el capítulo 22, Componentes de la GUI: parte 2).
java.io El Paquete de Entrada/Salida de Java contiene clases e interfaces que permiten a los progra-
mas recibir datos de entrada y mostrar datos de salida. (Aprenderá más acerca de este paquete
en el capítulo 14, Archivos y flujos).
java.lang El Paquete del Lenguaje Java contiene clases e interfaces (descritas a lo largo de este texto)
requeridas por muchos programas de Java. Este paquete es importado por el compilador en
todos los programas, por lo que usted no necesita hacerlo.
java.net El Paquete de Red de Java contiene clases e interfaces que permiten a los programas comu-
nicarse mediante redes de computadoras, como Internet. (Aprenderá más acerca de esto en el
capítulo 24, Redes).
java.text El Paquete de Texto de Java contiene clases e interfaces que permiten a los programas
manipular números, fechas, caracteres y cadenas. El paquete proporciona herramientas de
internacionalización que permiten la personalización de un programa con respecto a una
configuración regional específica (por ejemplo, un programa puede mostrar cadenas en dis-
tintos lenguajes, con base en el país del usuario).
java.util El Paquete de Utilerías de Java contiene clases e interfaces utilitarias, que permiten acciones
como manipulaciones de fecha y hora, procesamiento de números aleatorios (clase Random),
almacenar y procesar grandes cantidades de datos y descomponer cadenas en piezas más
pequeñas llamadas tokens (clase StringTokenizer). (Aprenderá más acerca de las caracte-
rísticas de este paquete en el capítulo 19, Colecciones).
javax.swing El Paquete de Componentes GUI Swing de Java contiene clases e interfaces para los com-
ponentes de la GUI Swing de Java, los cuales ofrecen soporte para GUIs portables. (Apren-
derá más acerca de este paquete en el capítulo 11, Componentes de la GUI: parte 1, y en el
capítulo 22, Componentes de la GUI: parte 2).
javax.swing.event El Paquete Swing Event de Java contiene clases e interfaces que permiten el manejo de
eventos (por ejemplo, responder a los clics del ratón) para los componentes de la GUI en el
paquete javax.swing. (Aprenderá más acerca de este paquete en el capítulo 11, Compo-
nentes de la GUI: parte 1, y en el capítulo 22, Componentes de la GUI: parte 2).
Figura 6.6 | Paquetes de la API de Java (un subconjunto).
sión, redes avanzadas, seguridad, procesamiento de bases de datos, multimedia, accesibilidad (para personas con
discapacidades) y muchas otras funciones. Para una visión general de los paquetes en Java SE 6, visite:
java.sun.com/javase/6/docs/api/overview-summary.html
Además, muchos otros paquetes están disponibles para descargarse en java.sun.com.
6.8 Paquetes de la API de Java 223
224 Capítulo 6 Métodos: un análisis más detallado
Puede localizar información adicional acerca de los métodos de una clase predefinida de Java en la docu-
mentación para la API de Java, en java.sun.com/javase/6/docs/api/. Cuando visite este sitio, haga clic en el
vínculo Index para ver un listado en orden alfabético de todas las clases y los métodos en la API de Java. Localice
el nombre de la clase y haga clic en su vínculo para ver la descripción en línea de la clase. Haga clic en el vínculo
METHOD para ver una tabla de los métodos de la clase. Cada método static se enlistará con la palabra "static"
antes del tipo de valor de retorno del método. Para una descripción más detallada acerca de cómo navegar por la
documentación para la API de Java, consulte el apéndice J, Uso de la documentación para la API de Java.
Buena práctica de programación 6.2
Es fácil buscar información en la documentación en línea de la API de Java; además proporciona los detalles acerca
de cada clase. Al estudiar una clase en este libro, es conveniente que tenga el hábito de buscar la clase en la documen-
tación en línea, para obtener información adicional.
6.9 Ejemplo práctico: generación de números aleatorios
Ahora analizaremos de manera breve una parte divertida de un tipo popular de aplicaciones de la programación:
simulación y juegos. En ésta y en la siguiente sección desarrollaremos un programa de juego bien estructurado
con varios métodos. El programa utiliza la mayoría de las instrucciones de control presentadas hasta este punto
en el libro, e introduce varios conceptos de programación nuevos.
Hay algo en el ambiente de un casino de apuestas que anima a las personas: desde las elegantes mesas de
caoba y fieltro para tirar dados, hasta las máquinas tragamonedas. Es el elemento de azar, la posibilidad de que la
suerte convierta un bolsillo lleno de dinero en una montaña de riquezas. El elemento de azar puede introducirse
en un programa mediante un objeto de la clase Random (paquete java.util), o mediante el método static
llamado random, de la clase Math. Los objetos de la clase Random pueden producir valores aleatorios de tipo
boolean, byte, float, double, int, long y gaussianos, mientras que el método random de la clase Math puede
producir sólo valores de tipo double en el rango 0.0 ≤ x < 1.0, donde x es el valor regresado por el método ran-
dom. En los siguientes ejemplos, usamos objetos de tipo Random para producir valores aleatorios.
Se puede crear un nuevo objeto generador de números aleatorios de la siguiente manera:
Random numerosAleatorios = new Random();
Después, el objeto generador de números aleatorios puede usarse para generar valores boolean, byte, float,
double, int, long y gaussianos; aquí sólo hablaremos sobre los valores int aleatorios. Para obtener más infor-
mación sobre la clase Random, vaya a java.sun.com/javase/6/docs/api/java/util/Random.html.
Considere la siguiente instrucción:
int valorAleatorio = numerosAleatorios.nextInt();
El método nextInt de la clase Random genera un valor int aleatorio en el rango de –2,147,483,648 a
+2,147,483,647. Si el método nextInt verdaderamente produce valores aleatorios, entonces cualquier valor en
ese rango debería tener una oportunidad (o probabilidad) igual de ser elegido cada vez que se llame al método
nextInt. Los valores devueltos por nextInt son en realidad números seudoaleatorios (una secuencia de valores
producidos por un cálculo matemático complejo). Ese cálculo utiliza la hora actual del día (que, desde luego,
cambia constantemente) para sembrar el generador de números aleatorios, de tal forma que cada ejecución de un
programa produzca una secuencia distinta de valores aleatorios.
El rango de valores producidos directamente por el método nextInt es a menudo distinto del rango de
valores requeridos en una aplicación de Java particular. Por ejemplo, un programa que simula el lanzamiento
de una moneda sólo requiere 0 para “águila” y 1 para “sol”. Un programa para simular el tiro de un dado de seis
lados requeriría enteros aleatorios en el rango de 1 a 6. Un programa que adivine en forma aleatoria el siguiente
tipo de nave espacial (de cuatro posibilidades distintas) que volará a lo largo del horizonte en un videojuego reque-
riría números aleatorios en el rango de 1 a 4. Para casos como éstos, la clase Random cuenta con otra versión del
método nextInt, que recibe un argumento int y devuelve un valor desde 0 hasta (pero sin incluir) el valor
del argumento. Por ejemplo, para simular el lanzamiento de monedas, podría utilizar la instrucción
int valorAleatorio = numerosAleatorios.nextInt( 2 );
que devuelve 0 o 1.
Tirar un dado de seis lados
Para demostrar los números aleatorios, desarrollaremos un programa que simula 20 tiros de un dado de seis lados,
y que muestra el valor de cada tiro. Para empezar, usaremos nextInt para producir valores aleatorios en el rango
de 0 a 5, como se muestra a continuación:
cara = numerosAleatorios.nextInt( 6 );
El argumento 6 (que se conoce como el factor de escala) representa el número de valores únicos que nextInt
debe producir (en este caso, seis: 0, 1, 2, 3, 4 y 5). A esta manipulación se le conoce como escalar el rango de
valores producidos por el método nextInt de Random.
Un dado de seis lados tiene los números del 1 al 6 en sus caras, no del 0 al 5. Por lo tanto, desplazamos el
rango de números producidos sumando un valor de desplazamiento (en este caso, 1) a nuestro resultado ante-
rior, como en
cara = 1 + numerosAleatorios.nextInt( 6 );
El valor de desplazamiento (1) especifica el primer valor en el conjunto deseado de enteros aleatorios. La instruc-
ción anterior asigna a cara un entero aleatorio en el rango de 1 a 6.
La figura 6.7 muestra dos resultados de ejemplo, los cuales confirman que los resultados del cálculo anterior
son enteros en el rango de 1 a 6, y que cada ejecución del programa puede producir una secuencia distinta de
números aleatorios. La línea 3 importa la clase Random del paquete java.util. La línea 9 crea el objeto nume-
rosAleatorios de la clase Random para producir valores aleatorios. La línea 16 se ejecuta 20 veces en un ciclo
para tirar el dado. La instrucción if (líneas 21 y 22) en el ciclo empieza una nueva línea de salida después de cada
cinco números.
1 // Fig. 6.7: EnterosAleatorios.java
2 // Enteros aleatorios desplazados y escalados.
3 import java.util.Random; // el programa usa la clase Random
4
5 public class EnterosAleatorios
6 {
7 public static void main( String args[] )
8 {
9 Random numerosAleatorios = new Random(); // generador de números aleatorios
10 int cara; // almacena cada entero aleatorio generado
11
12 // itera 20 veces
13 for ( int contador = 1; contador <= 20; contador++ )
14 {
15 // elige entero aleatorio del 1 al 6
16 cara = 1 + numerosAleatorios.nextInt( 6 );
17
18 System.out.printf( "%d ", cara ); // muestra el valor generado
19
20 // si contador es divisible entre 5, empieza una nueva línea de salida
21 if ( contador % 5 == 0 )
22 System.out.println();
23 } // fin de for
24 } // fin de main
25 } // fin de la clase EnterosAleatorios
Figura 6.7 | Enteros aleatorios desplazados y escalados. (Parte 1 de 2).
1 5 3 6 2
5 2 6 5 2
4 4 4 2 6
3 1 6 2 2
6.9 Ejemplo práctico: generación de números aleatorios 225
226 Capítulo 6 Métodos: un análisis más detallado
Tirar un dado de seis lados 6000 veces
Para mostrar que los números que produce nextInt ocurren con una probabilidad aproximadamente igual,
simularemos 6000 tiros de un dado con la aplicación de la figura 6.8. Cada entero de 1 a 6 debe aparecer aproxi-
madamente 1000 veces.
Como se muestra en los dos bloques de resultados, al escalar y desplazar los valores producidos por el método
nextInt, el programa puede simular de manera realista el tiro de un dado de seis lados. La aplicación utiliza ins-
trucciones de control anidadas (la instrucción switch está anidada dentro del for) para determinar el número de
ocurrencias de cada lado del dado. La instrucción for (líneas 21 a 47) itera 6000 veces. Durante cada iteración,
la línea 23 produce un valor aleatorio del 1 al 6. Después, ese valor se utiliza como la expresión de control (línea
26) de la instrucción switch (líneas 26 a 46). Con base en el valor de cara, la instrucción switch incrementa
una de las seis variables contadores durante cada iteración del ciclo. Cuando veamos los arreglos en el capítulo 7,
¡le mostraremos una forma elegante de reemplazar toda la instrucción switch en este programa con una sola ins-
trucción! Observe que la instrucción switch no tiene un caso default, ya que hemos creado una etiqueta case
para todos los posibles valores que puede producir la expresión en la línea 23. Ejecute el programa varias veces, y
observe los resultados. Como verá, cada vez que ejecute el programa, producirá distintos resultados.
1 // Fig. 6.8: TirarDado.java
2 // Tirar un dado de seis lados 6000 veces.
3 import java.util.Random;
4
5 public class TirarDado
6 {
7 public static void main( String args[] )
8 {
9 Random numerosAleatorios = new Random(); // generador de números aleatorios
10
11 int frecuencia1 = 0; // cuenta de veces que se tiró 1
12 int frecuencia2 = 0; // cuenta de veces que se tiró 2
13 int frecuencia3 = 0; // cuenta de veces que se tiró 3
14 int frecuencia4 = 0; // cuenta de veces que se tiró 4
15 int frecuencia5 = 0; // cuenta de veces que se tiró 5
16 int frecuencia6 = 0; // cuenta de veces que se tiró 6
17
18 int cara; // almacena el valor que se tiró más recientemente
19
20 // sintetiza los resultados de tirar un dado 6000 veces
21 for ( int tiro = 1; tiro <= 6000; tiro++ )
22 {
23 cara = 1 + numerosAleatorios.nextInt( 6 ); // número del 1 al 6
24
25 // determina el valor del tiro de 1 a 6 e incrementa el contador apropiado
26 switch ( cara )
27 {
28 case 1:
29 ++frecuencia1; // incrementa el contador de 1s
30 break;
Figura 6.8 | Tirar un dado de seis lados 6000 veces. (Parte 1 de 2).
Figura 6.7 | Enteros aleatorios desplazados y escalados. (Parte 2 de 2).
6 5 4 2 6
1 2 5 1 3
6 3 2 2 1
6 4 2 6 4
31 case 2:
32 ++frecuencia2; // incrementa el contador de 2s
33 break;
34 case 3:
35 ++frecuencia3; // incrementa el contador de 3s
36 break;
37 case 4:
38 ++frecuencia4; // incrementa el contador de 4s
39 break;
40 case 5:
41 ++frecuencia5; // incrementa el contador de 5s
42 break;
43 case 6:
44 ++frecuencia6; // incrementa el contador de 6s
45 break; // opcional al final del switch
46 } // fin de switch
47 } // fin de for
48
49 System.out.println( "CaratFrequencia" ); // encabezados de salida
50 System.out.printf( "1t%dn2t%dn3t%dn4t%dn5t%dn6t%dn",
51 frecuencia1, frecuencia2, frecuencia3, frecuencia4,
52 frecuencia5, frecuencia6 );
53 } // fin de main
54 } // fin de la clase TirarDado
Figura 6.8 | Tirar un dado de seis lados 6000 veces. (Parte 2 de 2).
Cara Frecuencia
1 1029
2 994
3 1017
4 1007
5 972
6 981
Cara Frecuencia
1 982
2 1001
3 1015
4 1005
5 1009
6 988
6.9.1 Escalamiento y desplazamiento generalizados de números aleatorios
Anteriormente demostramos la instrucción
cara = 1 + numerosAleatorios.nextInt( 6 );
la cual simula el tiro de un dado de seis caras. Esta instrucción siempre asigna a la variable cara un entero en el
rango 1 ≤ cara ≤ 6. La amplitud de este rango (es decir, el número de enteros consecutivos en el rango) es 6, y
el número inicial en el rango es 1. Si hacemos referencia a la instrucción anterior, podemos ver que la amplitud
del rango se determina en base al número 6 que se pasa como argumento para el método nextInt de Random,
y que el número inicial del rango es el número 1 que se suma a numerosAleatorios.nextInt( 6 ). Podemos
generalizar este resultado de la siguiente manera:
numero = valorDesplazamiento + numerosAleatorios.nextInt( factorEscala );
en donde valorDesplazamiento especifica el primer número en el rango deseado de enteros consecutivos y factorEs-
cala especifica cuántos números hay en el rango.
6.9 Ejemplo práctico: generación de números aleatorios 227
228 Capítulo 6 Métodos: un análisis más detallado
También es posible elegir enteros al azar, a partir de conjuntos de valores distintos a los rangos de enteros
consecutivos. Por ejemplo, para obtener un valor aleatorio de la secuencia 2, 5, 8, 11 y 14, podríamos utilizar la
siguiente instrucción:
numero = 2 + 3 * numerosAleatorios.nextInt( 5 );
En este caso, numerosAleatorios.nextInt( 5 ) produce valores en el rango de 0 a 4. Cada valor producido
se multiplica por 3 para producir un número en la secuencia 0, 3, 6, 9 y 12. Después sumamos 2 a ese valor
para desplazar el rango de valores y obtener un valor de la secuencia 2, 5, 8, 11 y 14. Podemos generalizar este
resultado así:
numero = valorDesplazamiento +
diferenciaEntreValores * numerosAleatorios.nextInt( factorEscala );
en donde valorDesplazamiento especifica el primer número en el rango deseado de valores, diferenciaEntreValores
representa la diferencia entre números consecutivos en la secuencia y factorEscala especifica cuántos números hay
en el rango.
6.9.2 Repetitividad de números aleatorios para prueba y depuración
Como mencionamos en la sección 6.9, los métodos de la clase Random en realidad generan números seudoalea-
torios con base en cálculos matemáticos complejos. Si se llama repetidas veces a cualquiera de los métodos de
Random, se produce una secuencia de números que parecen ser aleatorios. El cálculo que producen los números
seudoaleatorios utiliza la hora del día como valor de semilla para cambiar el punto inicial de la secuencia. Cada
nuevo objeto Random se siembra a sí mismo con un valor basado en el reloj del sistema computacional al momen-
to en que se crea el objeto, con lo cual se permite que cada ejecución de un programa produzca una secuencia
distinta de números aleatorios.
Al depurar una aplicación, algunas veces es útil repetir la misma secuencia exacta de números seudoaleatorios
durante cada ejecución del programa. Esta repetitividad nos permite probar que la aplicación esté funcionando
para una secuencia específica de números aleatorios, antes de evaluar el programa con distintas secuencias de
números aleatorios. Cuando la repetitividad es importante, podemos crear un objeto Random de la siguiente
manera:
Random numerosAleatorios = new Random( valorSemilla );
El argumento valorSemilla (de tipo long) siembra el cálculo del número aleatorio. Si se utiliza siempre el
mismo valor para valorSemilla, el objeto Random produce la misma secuencia de números aleatorios. Para
establecer la semilla de un objeto Random en cualquier momento durante la ejecución de un programa, podemos
llamar al método setSeed del objeto, como en
numerosAleatorios.setSeed( valorSemilla );
Tip de prevención de errores 6.2
Mientras un programa esté en desarrollo, cree el objeto Random con un valor de semilla específico para producir una
secuencia repetible de números aleatorios cada vez que se ejecute el programa. Si se produce un error lógico, corrija el
error y evalúe el programa otra vez con el mismo valor de semilla; esto le permitirá reconstruir la misma secuencia de
números aleatorios que produjeron el error. Una vez que se hayan eliminado los errores lógicos, cree el objeto Random
sin utilizar un valor de semilla, para que el objeto Random genere una nueva secuencia de números aleatorios cada
vez que se ejecute el programa.
6.10 Ejemplo práctico: un juego de probabilidad
(introducción a las enumeraciones)
Un juego de azar popular es el juego de dados conocido como “craps”, el cual se juega en casinos y callejones por
todo el mundo. Las reglas del juego son simples:
Un jugador tira dos dados. Cada dado tiene seis caras, las cuales contienen uno, dos, tres cuatro, cinco
y seis puntos negros, respectivamente. Una vez que los dados dejan de moverse, se calcula la suma de los
puntos negros en las dos caras superiores. Si la suma es 7 u 11 en el primer tiro, el jugador gana. Si la
suma es 2, 3 o 12 en el primer tiro (llamado “craps”), el jugador pierde (es decir, la “casa” gana). Si
la suma es 4, 5, 6, 8, 9 o 10 en el primer tiro, esta suma se convierte en el “punto” del jugador. Para
ganar, el jugador debe seguir tirando los dados hasta que salga otra vez “su punto” (es decir, que tire ese
mismo valor de punto). El jugador pierde si tira un 7 antes de llegar a su punto.
La aplicación en las figuras 6.9 y 6.10 simula el juego de craps, utilizando varios métodos para definir la lógica del
juego. En el método main de la clase PruebaCraps (figura 6.10), la línea 8 crea un objeto de la clase Craps (figura
6.9) y la línea 9 llama a su método jugar para iniciar el juego. El método jugar (figura 6.9, líneas 21 a 65) llama
al método tirarDado (figura 6.9, líneas 68 a 81) según sea necesario para tirar los dos dados y calcular su suma.
Los cuatro resultados de ejemplo en la figura 6.10 muestran que se ganó en el primer tiro, se perdió en el primer
tiro, se ganó en un tiro subsiguiente y se perdió en un tiro subsiguiente, en forma respectiva.
1 // Fig. 6.9: Craps.java
2 // La clase Craps simula el juego de dados "craps".
3 import java.util.Random;
4
5 public class Craps
6 {
7 // crea un generador de números aleatorios para usarlo en el método tirarDado
8 private Random numerosAleatorios = new Random();
9
10 // enumeración con constantes que representan el estado del juego
11 private enum Estado { CONTINUA, GANO, PERDIO };
12
13 // constantes que representan tiros comunes del dado
14 private final static int DOS_UNOS = 2;
15 private final static int TRES = 3;
16 private final static int SIETE = 7;
17 private final static int ONCE = 11;
18 private final static int DOCE = 12;
19
20 // ejecuta un juego de craps
21 public void jugar()
22 {
23 int miPunto = 0; // punto si no gana o pierde en el primer tiro
24 Estado estadoJuego; // puede contener CONTINUA, GANO o PERDIO
25
26 int sumaDeDados = tirarDados(); // primer tiro de los dados
27
28 // determina el estado del juego y el punto con base en el primer tiro
29 switch ( sumaDeDados )
30 {
31 case SIETE: // gana con 7 en el primer tiro
32 case ONCE: // gana con 11 en el primer tiro
33 estadoJuego = Estado.GANO;
34 break;
35 case DOS_UNOS: // pierde con 2 en el primer tiro
36 case TRES: // pierde con 3 en el primer tiro
37 case DOCE: // pierde con 12 en el primer tiro
38 estadoJuego = Estado.PERDIO;
39 break;
40 default: // no ganó ni perdió, por lo que guarda el punto
41 estadoJuego = Estado.CONTINUA; // no ha terminado el juego
42 miPunto = sumaDeDados; // guarda el punto
43 System.out.printf( "El punto es %dn", miPunto );
Figura 6.9 | La clase Craps simula el juego de dados “craps”. (Parte 1 de 2).
6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones) 229
230 Capítulo 6 Métodos: un análisis más detallado
Hablaremos sobre la declaración de la clase Craps en la figura 6.9. En las reglas del juego, el jugador debe
tirar dos dados en el primer tiro y debe hacer lo mismo en todos los tiros subsiguientes. Declaramos el método
tirarDados (líneas 68 a 81) para tirar el dado y calcular e imprimir su suma. El método tirarDados se declara
una vez, pero se llama desde dos lugares (líneas 26 y 50) en el método jugar, el cual contiene la lógica para un
juego completo de craps. El método tirarDados no tiene argumentos, por lo cual su lista de parámetros está
vacía. Cada vez que se llama, tirarDados devuelve la suma de los dados, por lo que se indica el tipo de valor
de retorno int en el encabezado del método (línea 68). Aunque las líneas 71 y 72 se ven iguales (excepto por el
nombre de los dados), no necesariamente producen el mismo resultado. Cada una de estas instrucciones produce
un valor aleatorio en el rango de 1 a 6. Observe que numerosAleatorios (se utiliza en las líneas 71 y 72) no se
declara en el método, sino que se declara como una variable de instancia private de la clase y se inicializa en la
línea 8. Esto nos permite crear un objeto Random que se reutiliza en cada llamada a tirarDados.
El juego es razonablemente complejo. El jugador puede ganar o perder en el primer tiro, o puede ganar o
perder en cualquier tiro subsiguiente. El método jugar (líneas 21 a 65) utiliza a la variable local miPunto (línea
23) para almacenar el “punto” si el jugador no gana o pierde en el primer tiro, a la variable local estadoJuego
(línea 24) para llevar el registro del estado del juego en general y a la variable local sumaDeDados (línea 26) para
44 break; // opcional al final del switch
45 } // fin de switch
46
47 // mientras el juego no esté terminado
48 while ( estadoJuego == Estado.CONTINUA ) // no GANO ni PERDIO
49 {
50 sumaDeDados = tirarDados(); // tira los dados de nuevo
51
52 // determina el estado del juego
53 if ( sumaDeDados == miPunto ) // gana haciendo un punto
54 estadoJuego = Estado.GANO;
55 else
56 if ( sumaDeDados == SIETE ) // pierde al tirar 7 antes del punto
57 estadoJuego = Estado.PERDIO;
58 } // fin de while
59
60 // muestra mensaje de que ganó o perdió
61 if ( estadoJuego == Estado.GANO )
62 System.out.println( "El jugador gana" );
63 else
64 System.out.println( “El jugador pierde" );
65 } // fin del método jugar
66
67 // tira los dados, calcula la suma y muestra los resultados
68 public int tirarDados()
69 {
70 // elige valores aleatorios para los dados
71 int dado1 = 1 + numerosAleatorios.nextInt( 6 ); // primer tiro del dado
72 int dado2 = 1 + numerosAleatorios.nextInt( 6 ); // segundo tiro del dado
73
74 int suma = dado1 + dado2; // suma de los valores de los dados
75
76 // muestra los resultados de este tiro
77 System.out.printf( "El jugador tiro %d + %d = %dn",
78 dado1, dado2, suma );
79
80 return suma; // devuelve la suma de los dados
81 } // fin del método tirarDados
82 } // fin de la clase Craps
Figura 6.9 | La clase Craps simula el juego de dados “craps”. (Parte 2 de 2).
1 // Fig. 6.10: PruebaCraps.java
2 // Aplicación para probar la clase Craps.
3
4 public class PruebaCraps
5 {
6 public static void main( String args[] )
7 {
8 Craps juego = new Craps();
9 juego.jugar(); // juega un juego de craps
10 } // fin de main
11 } // fin de la clase PruebaCraps
El jugador tiro 5 + 6 = 11
El jugador gana
Figura 6.10 | Aplicación para probar la clase Craps.
El jugador tiro 2 + 6 = 8
El punto es 8
El jugador tiro 5 + 1 = 6
El jugador tiro 2 + 1 = 3
El jugador tiro 1 + 6 = 7
El jugador pierde
El jugador tiro 1 + 2 = 3
El jugador pierde
El jugador tiro 5 + 4 = 9
El punto es 9
El jugador tiro 2 + 2 = 4
El jugador tiro 2 + 6 = 8
El jugador tiro 4 + 2 = 6
El jugador tiro 3 + 6 = 9
El jugador gana
almacenar la suma de los dados para el tiro más reciente. Observe que miPunto se inicializa con 0 para asegurar
que la aplicación se compile. Si no inicializa miPunto, el compilador genera un error ya que miPunto no reci-
be un valor en todas las etiquetas case de la instrucción switch y, en consecuencia, el programa podría tratar
de utilizar miPunto antes de que se le asigne un valor. En contraste, estadoJuego no requiere inicialización, ya
que se le asigna un valor en cada etiqueta case de la instrucción switch; por lo tanto, se garantiza que se inicialice
antes de usarse.
Observe que la variable local estadoJuego (línea 24) se declara como de un nuevo tipo llamado Estado, el
cual declaramos en la línea 11. El tipo Estado se declara como un miembro private de la clase Craps, ya que
sólo se utiliza en esa clase. Estado es un tipo declarado por el programador, denominado enumeración, que en
su forma más simple declara un conjunto de constantes representadas por identificadores. Una enumeración es
un tipo especial de clase, que se introduce mediante la palabra clave enum y un nombre para el tipo (en este caso,
Estado). Al igual que con una clase, las llaves ({ y }) delimitan el cuerpo de una declaración de enum. Dentro
de las llaves hay una lista, separada por comas, de constantes de enumeración, cada una de las cuales representa
un valor único. Los identificadores en una enum deben ser únicos (en el capítulo 8 aprenderá más acerca de las
enumeraciones).
Buena práctica de programación 6.3
Use sólo letras mayúsculas en los nombres de las constantes de enumeración. Esto hace que resalten y le recuerdan que
las constantes de enumeración no son variables.
6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones) 231
232 Capítulo 6 Métodos: un análisis más detallado
A las variables de tipo Estado se les debe asignar sólo una de las tres constantes declaradas en la enumeración
(línea 11), o se producirá un error de compilación. Cuando el jugador gana el juego, el programa asigna a la
variable local estadoJuego el valor Estado.GANO (líneas 33 y 54). Cuando el jugador pierde el juego, la aplica-
ción asigna a la variable local estadoJuego el valor Estado.PERDIO (líneas 38 y 57). En cualquier otro caso, el
programa asigna a la variable local estadoJuego el valor Estado.CONTINUA (línea 41) para indicar que el juego
no ha terminado y hay que tirar los dados otra vez.
Buena práctica de programación 6.4
El uso de constantes de enumeración (como Estado.GANO, Estado.PERDIO y Estado.CONTINUA) en vez de valores
enteros literales (como 0, 1 y 2) puede hacer que los programas sean más fáciles de leer y de mantener.
La línea 26 en el método jugar llama a tirarDados, el cual elige dos valores aleatorios del 1 al 6, muestra
el valor del primer dado, el del segundo y la suma de los dos dados, y devuelve esa suma. Después el método
jugar entra a la instrucción switch en las líneas 29 a 45, que utiliza el valor de sumaDeDados de la línea 26
para determinar si el jugador ganó o perdió el juego, o si debe continuar con otro tiro. Las sumas de los dados
que ocasionan que se gane o pierda el juego en el primer tiro se declaran como constantes public final static
int en las líneas 14 a 18. Estos valores se utilizan en las etiquetas case de la instrucción switch. Los nombres
de los identificadores utilizan los términos comunes en el casino para estas sumas. Observe que estas constantes,
al igual que las constantes enum, se declaran todas con letras mayúsculas por convención, para que resalten en el
programa. Las líneas 31 a 34 determinan si el jugador ganó en el primer tiro con SIETE (7) u ONCE (11). Las líneas
35 a 39 determinan si el jugador perdió en el primer tiro con DOS_UNOS (2), TRES (3) o DOCE (12). Después del
primer tiro, si el juego no se ha terminado, el caso default (líneas 40 a 44) establece estadoJuego en Estado.
CONTINUA, guarda sumaDeDados en miPunto y muestra el punto.
Si aún estamos tratando de “hacer nuestro punto” (es decir, el juego continúa de un tiro anterior), se ejecuta
el ciclo de las líneas 48 a 58. En la línea 50 se tira el dado otra vez. Si sumaDeDados concuerda con miPunto en la
línea 53, la línea 54 establece estadoJuego en Estado.GANO y el ciclo termina, ya que el juego está terminado.
En la línea 56, si sumaDeDados es igual a SIETE (7), la línea 57 asigna el valor Estado.PERDIO a estadoJuego y
el ciclo termina, ya que se acabó el juego. Cuando termina el juego, las líneas 61 a 64 muestran un mensaje en el
que se indica si el jugador ganó o perdió, y el programa termina.
Observe el uso de varios mecanismos de control del programa que hemos visto antes. La clase Craps, en con-
junto con la clase PruebaCraps, utiliza tres métodos: main, jugar (que se llama desde main) y tirarDados (que
se llama dos veces desde jugar), y las instrucciones de control switch, while, if…else e if anidado. Observe
también el uso de múltiples etiquetas case en la instrucción switch para ejecutar las mismas instrucciones para
las sumas de SIETE y ONCE (líneas 31 y 32), y para las sumas de DOS_UNOS, TRES y DOCE (líneas 35 a 37).
Tal vez se esté preguntando por qué declaramos las sumas de los dados como constantes public final sta-
tic int en vez de constantes enum. La respuesta está en el hecho de que el programa debe comparar la variable
int llamada sumaDeDados (línea 26) con estas constantes para determinar el resultado de cada tiro. Suponga que
declararemos constantes que contengan enum Suma (por ejemplo, Suma.DOS_UNOS) para representar las cinco
sumas utilizadas en el juego, y que después usaremos estas constantes en las etiquetas case de la instrucción
switch (líneas 29 a 45). Hacer esto evitaría que pudiéramos usar sumaDeDados como la expresión de control de
la instrucción switch, ya que Java no permite que un int se compare con una constante de enumeración. Para
lograr la misma funcionalidad que el programa actual, tendríamos que utilizar una variable sumaActual de tipo
Suma como expresión de control para el switch. Por desgracia, Java no proporciona una manera fácil de convertir
un valor int en una constante enum específica. Podríamos traducir un int en una constante enum mediante una
instrucción switch separada. Sin duda, esto sería complicado y no mejoraría la legibilidad del programa (lo cual
echaría a perder el propósito de usar una enum).
6.11 Alcance de las declaraciones
Ya hemos visto declaraciones de varias entidades de Java como las clases, los métodos, las variables y los paráme-
tros. Las declaraciones introducen nombres que pueden utilizarse para hacer referencia a dichas entidades de Java.
El alcance de una declaración es la porción del programa que puede hacer referencia a la entidad declarada por
su nombre. Se dice que dicha entidad está “dentro del alcance” para esa porción del programa. En esta sección
introduciremos varias cuestiones importantes relacionadas con el alcance. (Para obtener más información sobre
el alcance, consulte la Especificación del lenguaje Java, sección 6.3: Alcance de una declaración, en java.sun.com/
docs/books/jls/second_edition/html/names.doc.html#103228).
Las reglas básicas de alcance son las siguientes:
1. El alcance de la declaración de un parámetro es el cuerpo del método en el que aparece la declaración.
2. El alcance de la declaración de una variable local es a partir del punto en el cual aparece la declaración,
hasta el final de ese bloque.
3. El alcance de la declaración de una variable local que aparece en la sección de inicialización del encabeza-
do de una instrucción for es el cuerpo de la instrucción for y las demás expresiones en el encabezado.
4. El alcance de un método o campo de una clase es todo el cuerpo de la clase. Esto permite a los métodos
no static de la clase utilizar cualquiera de los campos y otros métodos de la clase.
Cualquier bloque puede contener declaraciones de variables. Si una variable local o parámetro en un método
tiene el mismo nombre que un campo, el campo se “oculta” hasta que el bloque termina su ejecución; a esto se le
llama ocultación de variables (shadowing). En el capítulo 8 veremos cómo acceder a los campos ocultos.
Error común de programación 6.10
Cuando una variable local se declara más de una vez en un método, se produce un error de compilación.
Tip de prevención de errores 6.3
Use nombres distintos para los campos y las variables locales, para ayudar a evitar los errores lógicos sutiles que se
producen cuando se hace la llamada a un método y una variable local de ese método oculta un campo con el mismo
nombre en la clase.
La aplicación en las figuras 6.11 y 6.12 demuestra las cuestiones de alcance con los campos y las variables
locales. Cuando la aplicación empieza a ejecutarse, el método main de la clase PruebaAlcance (figura 6.12, líneas
7 a 11) crea un objeto de la clase Alcance (línea 9) y llama al método iniciar del objeto (línea 10) para producir
el resultado de la aplicación (el cual se muestra en la figura 6.12).
1 // Fig. 6.11: Alcance.java
2 // La clase Alcance demuestra los alcances de los campos y las variables locales.
3
4 public class Alcance
5 {
6 // campo accesible para todos los métodos de esta clase
7 private int x = 1;
8
9 // el método iniciar crea e inicializa la variable local x
10 // y llama a los métodos usarVariableLocal y usarCampo
11 public void iniciar()
12 {
13 int x = 5; // la variable local x del método oculta al campo x
14
15 System.out.printf( "la x local en el metodo iniciar es %dn", x );
16
17 usarVariableLocal(); // usarVariableLocal tiene la x local
18 usarCampo(); // usarCampo usa el campo x de la clase Alcance
19 usarVariableLocal(); // usarVariableLocal reinicia a la x local
20 usarCampo(); // el campo x de la clase Alcance retiene su valor
21
Figura 6.11 | La clase Alcance demuestra los alcances de los campos y las variables locales. (Parte 1 de 2).
6.11 Alcance de las declaraciones 233
234 Capítulo 6 Métodos: un análisis más detallado
1 // Fig. 6.12: PruebaAlcance.java
2 // Aplicación para probar la clase Alcance.
3
4 public class PruebaAlcance
5 {
6 // punto inicial de la aplicación
7 public static void main( String args[] )
8 {
9 Alcance alcancePrueba = new Alcance();
10 alcancePrueba.iniciar();
11 } // fin de main
12 } // fin de la clase PruebaAlcance
Figura 6.12 | Aplicación para probar la clase Alcance.
la x local en el metodo iniciar es 5
la x local al entrar al metodo usarVariableLocal es 25
la x local antes de salir del metodo usarVariableLocal es 26
el campo x al entrar al metodo usarCampo es 1
el campo x antes de salir del metodo usarCampo es 10
la x local al entrar al metodo usarVariableLocal es 25
la x local antes de salir del metodo usarVariableLocal es 26
el campo x al entrar al metodo usarCampo es 10
el campo x antes de salir del metodo usarCampo es 100
la x local en el metodo iniciar es 5
Figura 6.11 | La clase Alcance demuestra los alcances de los campos y las variables locales. (Parte 2 de 2).
22 System.out.printf( "nla x local en el metodo iniciar es %dn”, x );
23 } // fin del método iniciar
24
25 // crea e inicializa la variable local x durante cada llamada
26 public void usarVariableLocal()
27 {
28 int x = 25; // se inicializa cada vez que se llama a usarVariableLocal
29
30 System.out.printf(
31 "nla x local al entrar al metodo usarVariableLocal es %dn", x );
32 ++x; // modifica la variable x local de este método
33 System.out.printf(
34 "la x local antes de salir del metodo usarVariableLocal es %dn", x );
35 } // fin del método usarVariableLocal
36
37 // modifica el campo x de la clase Alcance durante cada llamada
38 public void usarCampo()
39 {
40 System.out.printf(
41 "nel campo x al entrar al metodo usarCampo es %dn", x );
42 x *= 10; // modifica el campo x de la clase Alcance
43 System.out.printf(
44 "el campo x antes de salir del metodo usarCampo es %dn", x );
45 } // fin del método usarCampo
46 } // fin de la clase Alcance
En la clase Alcance, la línea 7 declara e inicializa el campo x en 1. Este campo se oculta en cualquier bloque
(o método) que declare una variable local llamada x. El método iniciar (líneas 11 a 23) declara una variable
local x (línea 13) y la inicializa en 5. El valor de esta variable local se imprime para mostrar que el campo x (cuyo
valor es 1) se oculta en el método iniciar. El programa declara otros dos métodos: usarVariableLocal (líneas
26 a 35) y usarCampo (líneas 38 a 45); cada uno de ellos no tiene argumentos y no devuelve resultados. El méto-
do iniciar llama a cada método dos veces (líneas 17 a 20). El método usarVariableLocal declara la variable
local x (línea 28). Cuando se llama por primera vez a usarVariableLocal (línea 17), crea una variable local x y la
inicializa en 25 (línea 28), muestra en pantalla el valor de x (líneas 30 y 31), incrementa x (línea 32) y muestra en
pantalla el valor de x otra vez (líneas 33 y 34). Cuando se llama a usarVariableLocal por segunda vez (línea 19),
vuelve a crear la variable local x y la reinicializa con 25, por lo que la salida de cada llamada a usarVariableLocal
es idéntica.
El método usarCampo no declara variables locales. Por lo tanto, cuando hace referencia a x, se utiliza el cam-
po x (línea 7) de la clase. Cuando el método usarCampo se llama por primera vez (línea 18), muestra en pantalla
el valor (1) del campo x (líneas 40 y 41), multiplica el campo x por 10 (línea 42) y muestra en pantalla el valor
(10) del campo x otra vez (líneas 43 y 44) antes de regresar. La siguiente vez que se llama al método usarCampo
(línea 20), el campo x tiene el valor modificado de 10, por lo que el método muestra en pantalla un 10 y después
un 100. Por último, en el método iniciar el programa muestra en pantalla el valor de la variable local x otra vez
(línea 22), para mostrar que ninguna de las llamadas a los métodos modificó la variable local x de iniciar, ya
que todos los métodos hicieron referencia a las variables llamadas x en otros alcances.
6.12 Sobrecarga de métodos
Pueden declararse métodos con el mismo nombre en la misma clase, siempre y cuando tengan distintos conjun-
tos de parámetros (determinados en base al número, tipos y orden de los parámetros). A esto se le conoce como
sobrecarga de métodos. Cuando se hace una llamada a un método sobrecargado, el compilador de Java seleccio-
na el método apropiado mediante un análisis del número, tipos y orden de los argumentos en la llamada. Por lo
general, la sobrecarga de métodos se utiliza para crear varios métodos con el mismo nombre que realicen la misma
tarea o tareas similares, pero con distintos tipos o distintos números de argumentos. Por ejemplo, los métodos
abs, min y max de Math (sintetizados en la sección 6.3) se sobrecargan con cuatro versiones cada uno:
1. Uno con dos parámetros double.
2. Uno con dos parámetros float.
3. Uno con dos parámetros int.
4. Uno con dos parámetros long.
Nuestro siguiente ejemplo demuestra cómo declarar e invocar métodos sobrecargados. En el capítulo 8 presenta-
remos ejemplos de constructores sobrecargados.
Declaración de métodos sobrecargados
En nuestra clase SobrecargaMetodos (figura 6.13) incluimos dos versiones sobrecargadas de un método lla-
mado cuadrado: una que calcula el cuadrado de un int (y devuelve un int) y otra que calcula el cuadrado de
un double (y devuelve un double). Aunque estos métodos tienen el mismo nombre, además de listas de paráme-
tros y cuerpos similares, podemos considerarlos simplemente como métodos diferentes. Puede ser útil si considera-
mos los nombres de los métodos como “cuadrado de int” y “cuadrado de double”, respectivamente. Cuando
la aplicación empieza a ejecutarse, el método main de la clase PruebaSobrecargaMetodos (figura 6.14, líneas 6
a 10) crea un objeto de la clase SobrecargaMetodos (línea 8) y llama al método probarMetodosSobrecargados
del objeto (línea 9) para producir la salida del programa (figura 6.14).
En la figura 6.13, la línea 9 invoca al método cuadrado con el argumento 7. Los valores enteros literales
se tratan como de tipo int, por lo que la llamada al método en la línea 9 invoca a la versión de cuadrado de las
líneas 14 a 19, la cual especifica un parámetro int. De manera similar, la línea 10 invoca al método cuadrado con
el argumento 7.5. Los valores de las literales de punto flotante se tratan como de tipo double, por lo que la llama-
da al método en la línea 10 invoca a la versión de cuadrado de las líneas 22 a 27, la cual especifica un parámetro
double. Cada método imprime en pantalla primero una línea de texto, para mostrar que se llamó al método
6.12 Sobrecarga de métodos 235
236 Capítulo 6 Métodos: un análisis más detallado
1 // Fig. 6.13: SobrecargaMetodos.java
2 // Declaraciones de métodos sobrecargados.
3
4 public class SobrecargaMetodos
5 {
6 // prueba los métodos cuadrado sobrecargados
7 public void probarMetodosSobrecargados()
8 {
9 System.out.printf( "El cuadrado del entero 7 es %dn", cuadrado( 7 ) );
10 System.out.printf( "El cuadrado del double 7.5 es %fn", cuadrado( 7.5 ) );
11 } // fin del método probarMetodosSobrecargados
12
13 // método cuadrado con argumento int
14 public int cuadrado( int valorInt )
15 {
16 System.out.printf( "nSe llamo a cuadrado con argumento int: %dn",
17 valorInt );
18 return valorInt * valorInt;
19 } // fin del método cuadrado con argumento int
20
21 // método cuadrado con argumento double
22 public double cuadrado( double valorDouble )
23 {
24 System.out.printf( "nSe llamo a cuadrado con argumento double: %fn",
25 valorDouble );
26 return valorDouble * valorDouble;
27 } // fin del método cuadrado con argumento double
28 } // fin de la clase SobrecargaMetodos
Figura 6.13 | Declaraciones de métodos sobrecargados.
1 // Fig. 6.14: PruebaSobrecargaMetodos.java
2 // Aplicación para probar la clase SobrecargaMetodos.
3
4 public class PruebaSobrecargaMetodos
5 {
6 public static void main( String args[] )
7 {
8 SobrecargaMetodos sobrecargaMetodos = new SobrecargaMetodos();
9 sobrecargaMetodos.probarMetodosSobrecargados();
10 } // fin de main
11 } // fin de la clase PruebaSobrecargaMetodos
Se llamo a cuadrado con argumento int: 7
El cuadrado del entero 7 es 49
Se llamo a cuadrado con argumento double: 7.500000
El cuadrado del double 7.5 es 56.250000
Figura 6.14 | Aplicación para probar la clase SobrecargaMetodos.
apropiado en cada caso. Observe que los valores en las líneas 10 y 24 se muestran con el especificador de formato
%f y que no especificamos una precisión en ninguno de los dos casos. De manera predeterminada, los valores de
punto flotante se muestran con seis dígitos de precisión, si ésta no se especifica en el especificador de formato.
Cómo se diferencian los métodos sobrecargados entre sí
El compilador diferencia los métodos sobrecargados en base a su firma: una combinación del nombre del método
y del número, tipos y orden de sus parámetros. Si el compilador sólo se fijara en los nombres de los métodos
durante la compilación, el código de la figura 6.13 sería ambiguo; el compilador no sabría cómo distinguir entre
los dos métodos cuadrado (líneas 14 a 19 y 22 a 27). De manera interna, el compilador utiliza nombres de
métodos más largos que incluyen el nombre del método original, el tipo de cada parámetro y el orden exacto
de los parámetros para determinar si los métodos en una clase son únicos en esa clase.
Por ejemplo, en la figura 6.13 el compilador podría utilizar el nombre lógico “cuadrado de int” para el
método cuadrado que especifica un parámetro int, y el método “cuadrado de double” para el método cua-
drado que especifica un parámetro double (los nombres reales que utiliza el compilador son más complicados).
Si la declaración de metodo1 empieza así:
void metodo1( int a, float b )
entonces el compilador podría usar el nombre lógico “metodo1 de int y float”. Si los parámetros se especifi-
caran así:
void metodo1( float a, int b )
entonces el compilador podría usar el nombre lógico “metodo1 de float e int”. Observe que el orden de los
tipos de los parámetros es importante; el compilador considera que los dos encabezados anteriores de metodo1
son distintos.
Tipos de valores de retorno de los métodos sobrecargados
Al hablar sobre los nombres lógicos de los métodos que utiliza el compilador, no mencionamos los tipos de valo-
res de retorno de los métodos. Esto se debe a que las llamadas a los métodos no pueden diferenciarse en base al
tipo de valor de retorno. El programa de la figura 6.15 ilustra los errores que genera el compilador cuando dos
métodos tienen la misma firma, pero distintos tipos de valores de retorno. Los métodos sobrecargados pueden
tener tipos de valor de retorno distintos si los métodos tienen distintas listas de parámetros. Además, los métodos
sobrecargados no necesitan tener el mismo número de parámetros.
1 // Fig. 6.15: SobrecargaMetodos.java
2 // Los métodos sobrecargados con firmas idénticas producen errores de
3 // compilación, aun si los tipos de valores de retorno son distintos.
4
5 public class ErrorSobrecargaMetodos
6 {
7 // declaración del método cuadrado con argumento int
8 public int cuadrado( int x )
9 {
10 return x * x;
11 }
12
13 // la segunda declaración del método cuadrado con argumento int produce un error
14 // de compilación, aun cuando los tipos de valores de retorno son distintos
15 public double cuadrado( int y )
16 {
17 return y * y;
18 }
19 } // fin de la clase ErrorSobrecargaMetodos
ErrorSobrecargaMetodos.java:15: cuadrado(int) is already defined in ErrorSobrecargaMetodos
public double cuadrado( int y )
^
1 error
Figura 6.15 | Las declaraciones de métodos sobrecargados con firmas idénticas producen errores de compilación, aun
si los tipos de valores de retorno son distintos.
6.12 Sobrecarga de métodos 237
238 Capítulo 6 Métodos: un análisis más detallado
Error común de programación 6.11
Declarar métodos sobrecargados con listas de parámetros idénticas es un error de compilación, sin importar que los
tipos de los valores de retorno sean distintos.
6.13 (Opcional) Ejemplo práctico de GUI y gráficos: colores
y figuras rellenas
Aunque podemos crear muchos diseños interesantes sólo con líneas y figuras básicas, la clase Graphics pro-
porciona muchas herramientas más. Las siguientes dos herramientas que presentaremos son los colores y las
figuras rellenas. El color agrega otra dimensión a los dibujos que ve un usuario en la pantalla de la computadora.
Las figuras rellenas cubren regiones completas con colores sólidos, en vez de dibujar sólo contornos.
Los colores que se muestran en las pantallas de las computadoras se definen en base a sus componentes rojo,
verde y azul. Estos componentes, llamados valores RGB, tienen valores enteros de 0 a 255. Entre más alto sea
el valor de un componente específico, más intensidad de color tendrá esa figura. Java utiliza la clase Color en el
paquete java.awt para representar colores usando sus valores RGB. Por conveniencia, el objeto Color contie-
ne 13 objetos static Color predefinidos: Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Co-
lor.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED,
Color.WHITE y Color.YELLOW. La clase Color también contiene un constructor de la forma:
public Color( int r, int g, int b )
de manera que podemos crear colores específicos, con sólo especificar valores para los componentes individuales
rojo, verde y azul de un color.
Los rectángulos y los óvalos rellenos se dibujan usando los métodos fillRect y fillOval de Graphics,
respectivamente. Estos dos métodos tienen los mismos parámetros que sus contrapartes drawRect y drawOval
sin relleno: los primeros dos parámetros son las coordenadas para la esquina superior izquierda de la figura, mien-
tras que los otros dos parámetros determinan su anchura y su altura. El ejemplo de las figuras 6.16 y 6.17 demues-
tra los colores y las figuras rellenas, al dibujar y mostrar una cara sonriente amarilla (esto lo verá en su pantalla).
1 // Fig. 6.16: DibujarCaraSonriente.java
2 // Demuestra las figuras rellenas.
3 import java.awt.Color;
4 import java.awt.Graphics;
5 import javax.swing.JPanel;
6
7 public class DibujarCaraSonriente extends JPanel
8 {
9 public void paintComponent( Graphics g )
10 {
11 super.paintComponent( g );
12
13 // dibuja la cara
14 g.setColor( Color.YELLOW );
15 g.fillOval( 10, 10, 200, 200 );
16
17 // dibuja los ojos
18 g.setColor( Color.BLACK );
19 g.fillOval( 55, 65, 30, 30 );
20 g.fillOval( 135, 65, 30, 30 );
21
22 // dibuja la boca
23 g.fillOval( 50, 110, 120, 60 );
24
25 // convierte la boca en una sonrisa
Figura 6.16 | Cómo dibujar una cara sonriente, usando colores y figuras rellenas. (Parte 1 de 2).
26 g.setColor( Color.YELLOW );
27 g.fillRect( 50, 110, 120, 30 );
28 g.fillOval( 50, 120, 120, 40 );
29 } // fin del método paintComponent
30 } // fin de la clase DibujarCaraSonriente
Figura 6.16 | Cómo dibujar una cara sonriente, usando colores y figuras rellenas. (Parte 2 de 2).
1 // Fig. 6.17: PruebaDibujarCaraSonriente.java
2 // Aplicación de prueba que muestra una cara sonriente.
3 import javax.swing.JFrame;
4
5 public class PruebaDibujarCaraSonriente
6 {
7 public static void main( String args[] )
8 {
9 DibujarCaraSonriente panel = new DibujarCaraSonriente();
10 JFrame aplicacion = new JFrame();
11
12 aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
13 aplicacion.add( panel );
14 aplicacion.setSize( 230, 250 );
15 aplicacion.setVisible( true );
16 } // fin de main
17 } // fin de la clase PruebaDibujarCaraSonriente
Figura 6.17 | Creación de un objeto JFrame para mostrar una cara sonriente.
Las instrucciones import en las líneas 3 a 5 de la figura 6.16 importan las clases Color, Graphics y JPanel.
La clase DibujarCaraSonriente (líneas 7 a 30) utiliza la clase Color para especificar los colores, y utiliza la clase
Graphics para dibujar. La clase JPanel proporciona de nuevo el área en la que vamos a dibujar. La línea 14 en
el método paintComponent utiliza el método setColor de Graphics para establecer el color actual para dibujar
en Color.YELLOW. El método setColor requiere un argumento, el Color a establecer como el color para dibu-
jar. En este caso, utilizamos el objeto predefinido Color.YELLOW. La línea 15 dibuja un círculo con un diámetro
de 200 para representar la cara; cuando los argumentos anchura y altura son idénticos, el método fillOval dibuja
un círculo. A continuación, la línea 18 establece el color en Color.BLACK, y las líneas 19 y 20 dibujan los ojos.
La línea 23 dibuja la boca como un óvalo, pero esto no es exactamente lo que queremos. Para crear una cara feliz,
vamos a “retocar” la boca. La línea 26 establece el color en Color.YELLOW, de manera que cualquier figura que
dibujemos se mezcle con la cara. La línea 27 dibuja un rectángulo con la mitad de altura que la boca. Esto “borra”
la mitad superior de la boca, dejando sólo la mitad inferior. Para crear una mejor sonrisa, la línea 28 dibuja otro
óvalo para cubrir ligeramente la porción superior de la boca. La clase PruebaDibujarCaraSonriente (figura
6.17) crea y muestra un objeto JFrame que contiene el dibujo. Cuando se muestra el objeto JFrame, el sistema
llama al método paintComponent para dibujar la cara sonriente.
6.13 (Opcional) Ejemplo práctico de GUI y gráficos: colores y figuras rellenas 239
240 Capítulo 6 Métodos: un análisis más detallado
Ejercicios del ejemplo práctico de GUI y gráficos
6.1 Usando el método fillOval, dibuje un tiro al blanco que alterne entre dos colores aleatorios, como en la figura
6.18. Use el constructor Color( int r, int g, int b ) con argumentos aleatorios para generar colores aleatorios.
6.2 Cree un programa para dibujar 10 figuras rellenas al azar en colores, posiciones y tamaños aleatorios (figura
6.19). El método paintComponent debe contener un ciclo que itere 10 veces. En cada iteración, el ciclo debe determi-
nar si se dibujará un rectángulo o un óvalo relleno, crear un color aleatorio y elegir las coordenadas y las medidas al azar.
Las coordenadas deben elegirse con base en la anchura y la altura del panel. Las longitudes de los lados deben limitarse
a la mitad de la anchura o altura de la ventana.
Figura 6.18 | Un tiro al blanco con dos colores alternantes al azar.
Figura 6.19 | Figuras generadas al azar.
6.14 (Opcional) Ejemplo práctico de Ingeniería de Software:
identificación de las operaciones de las clases
En las secciones del Ejemplo práctico de Ingeniería de Software al final de los capítulos 3 a 5, llevamos a cabo los
primeros pasos en el diseño orientado a objetos de nuestro sistema ATM. En el capítulo 3 identificamos las clases
que necesitaremos implementar, y creamos nuestro primer diagrama de clases. En el capítulo 4 describimos varios
atributos de nuestras clases. En el capítulo 5 examinamos los estados de nuestros objetos y modelamos sus transi-
ciones de estado y actividades. En esta sección determinaremos algunas de las operaciones (o comportamientos)
de las clases que son necesarias para implementar el sistema ATM.
Identificar las operaciones
Una operación es un servicio que proporcionan los objetos de una clase a los clientes (usuarios) de esa clase. Con-
sidere las operaciones de algunos objetos reales. Las operaciones de un radio incluyen el sintonizar su estación y
ajustar su volumen (que, por lo general, lo hace una persona que ajusta los controles del radio). Las operaciones
de un automóvil incluyen acelerar (operación invocada por el conductor cuando oprime el pedal del acelerador),
desacelerar (operación invocada por el conductor cuando oprime el pedal del freno o cuando suelta el pedal del
acelerador), dar vuelta y cambiar velocidades. Los objetos de software también pueden ofrecer operaciones; por
ejemplo, un objeto de gráficos de software podría ofrecer operaciones para dibujar un círculo, dibujar una línea,
dibujar un cuadrado, etcétera. Un objeto de software de hoja de cálculo podría ofrecer operaciones como impri-
mir la hoja de cálculo, totalizar los elementos en una fila o columna, y graficar la información de la hoja de cálculo
como un gráfico de barras o de pastel.
Podemos derivar muchas de las operaciones de cada clase mediante un análisis de los verbos y las frases ver-
bales clave en el documento de requerimientos. Después relacionamos cada una de ellas con las clases específicas
en nuestro sistema (figura 6.20). Las frases verbales en la figura 6.20 nos ayudan a determinar las operaciones de
cada clase.
Modelar las operaciones
Para identificar las operaciones, analizamos las frases verbales que se listan para cada clase en la figura 6.20. La
frase “ejecuta transacciones financieras” asociada con la clase ATM implica que esta clase instruye a las transacciones
a que se ejecuten. Por lo tanto, cada una de las clases SolicitudSaldo, Retiro y Deposito necesitan una ope-
ración para proporcionar este servicio al ATM. Colocamos esta operación (que hemos nombrado ejecutar) en
Clase Verbos y frases verbales
ATM ejecuta transacciones financieras
SolicitudSaldo [ninguna en el documento de requerimientos]
Retiro [ninguna en el documento de requerimientos]
Deposito [ninguna en el documento de requerimientos]
BaseDatosBanco autentica a un usuario, obtiene el saldo de una cuenta, abona un monto de depósito a una
cuenta, carga un monto de retiro a una cuenta
Cuenta obtiene el saldo de una cuenta, abona un monto de depósito a una cuenta, carga un monto
de retiro a una cuenta
Pantalla muestra un mensaje al usuario
Teclado recibe entrada numérica del usuario
DispensadorEfectivo dispensa efectivo, indica si contiene suficiente efectivo para satisfacer una solicitud de retiro
RanuraDeposito recibe un sobre de depósito
Figura 6.20 | Verbos y frases verbales para cada clase en el sistema ATM.
6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones... 241
242 Capítulo 6 Métodos: un análisis más detallado
el tercer compartimiento de las tres clases de transacciones en el diagrama de clases actualizado de la figura 6.21.
Durante una sesión con el ATM, el objeto ATM invocará estas operaciones de transacciones, según sea necesario.
Para representar las operaciones (que se implementan en forma de métodos en Java), UML lista el nombre de
la operación, seguido de una lista separada por comas de parámetros entre paréntesis, un signo de punto y coma
y el tipo de valor de retorno:
nombreOperación( parámetro1, parámetro2, …, parámetroN ) : tipo de valor de retorno
Cada parámetro en la lista separada por comas consiste en un nombre de parámetro, seguido de un signo de dos
puntos y del tipo del parámetro:
nombreParámetro : tipoParámetro
Por el momento, no listamos los parámetros de nuestras operaciones; en breve identificaremos y modelare-
mos los parámetros de algunas de las operaciones. Para algunas de estas operaciones no conocemos todavía los
tipos de valores de retorno, por lo que también las omitiremos del diagrama. Estas omisiones son perfectamente
normales en este punto. A medida que avancemos en nuestro proceso de diseño e implementación, agregaremos
el resto de los tipos de valores de retorno.
La figura 6.20 lista la frase “autentica a un usuario” enseguida de la clase BaseDatosBanco; la base de datos
es el objeto que contiene la información necesaria de la cuenta para determinar si el número de cuenta y el NIP
introducidos por un usuario concuerdan con los de una cuenta en el banco. Por lo tanto, la clase BaseDatos-
Banco necesita una operación que proporcione un servicio de autenticación al ATM. Colocamos la operación
Figura 6.21 | Las clases en el sistema ATM, con atributos y operaciones.
ATM
usuarioAutenticado : Boolean = false
SolicitudSaldo
numeroCuenta : Integer
DispensadorEfectivo
cuenta : Integer = 500
RanuraDeposito
Pantalla
Teclado
Retiro
numeroCuenta : Integer
monto : Double
BaseDatosBanco
Deposito
numeroCuenta : Integer
monto : Double
autenticarUsuario() : Boolean
obtenerSaldoDisponible() : Double
obtenerSaldoTotal() : Double
abonar()
cargar()
Cuenta
numeroCuenta : Integer
nip : Integer
saldoDisponible : Double
saldoTotal : Double
validarNIP() : Boolean
obtenerSaldoDisponible() : Double
obtenerSaldoTotal() : Double
abonar()
cargar()
ejecutar()
ejecutar()
mostrarMensaje()
dispensarEfectivo()
haySuficienteEfectivoDisponible() : Boolean
obtenerEntrada() : Integer
ejecutar()
seRecibioSobre() : Boolean
autenticarUsuario en el tercer compartimiento de la clase BaseDatosBanco (figura 6.21). No obstante, un
objeto de la clase Cuenta y no de la clase BaseDatosBanco es el que almacena el número de cuenta y el NIP a los
que se debe acceder para autenticar a un usuario, por lo que la clase Cuenta debe proporcionar un servicio para
validar un NIP obtenido como entrada del usuario, y compararlo con un NIP almacenado en un objeto Cuenta.
Por ende, agregamos una operación validarNIP a la clase Cuenta. Observe que especificamos un tipo de valor
de retorno Boolean para las operaciones autenticarUsuario y validarNIP. Cada operación devuelve un
valor que indica que la operación tuvo éxito al realizar su tarea (es decir, un valor de retorno true) o que no tuvo
éxito (es decir, un valor de retorno false).
La figura 6.20 lista varias frases verbales adicionales para la clase BaseDatosBanco: “extrae el saldo de una
cuenta”, “abona un monto de depósito a una cuenta” y “carga un monto de retiro a una cuenta”. Al igual que
“autentica a un usuario”, estas frases restantes se refieren a los servicios que debe proporcionar la base de datos al
ATM, ya que la base de datos almacena todos los datos de las cuentas que se utilizan para autenticar a un usuario
y realizar transacciones con el ATM. No obstante, los objetos de la clase Cuenta son los que en realidad realizan
las operaciones a las que se refieren estas frases. Por ello, asignamos una operación tanto a la clase BaseDatos-
Banco como a la clase Cuenta, que corresponda con cada una de estas frases. En la sección 3.10 vimos que, como
una cuenta de banco contiene información delicada, no permitimos que el ATM acceda a las cuentas en forma
directa. La base de datos actúa como un intermediario entre el ATM y los datos de la cuenta, evitando el acceso
no autorizado. Como veremos en la sección 7.14, la clase ATM invoca las operaciones de la clase BaseDatosBanco,
cada una de las cuales a su vez invoca a la operación con el mismo nombre en la clase Cuenta.
La frase “obtiene el saldo de una cuenta” sugiere que las clases BaseDatosBanco y Cuenta necesitan una ope-
ración obtenerSaldo. Sin embargo, recuerde que creamos dos atributos en la clase Cuenta para representar un
saldo: saldoDisponible y saldoTotal. Una solicitud de saldo requiere el acceso a estos dos atributos del saldo,
de manera que pueda mostrarlos al usuario, pero un retiro sólo requiere verificar el valor de saldoDisponible.
Para permitir que los objetos en el sistema obtengan cada atributo de saldo en forma individual, agregamos las
operaciones obtenerSaldoDisponible y obtenerSaldoTotal al tercer compartimiento de las clases Base-
DatosBanco y Cuenta (figura 6.21). Especificamos un tipo de valor de retorno Double para estas operaciones,
debido a que los atributos de los saldos que van a obtener son de tipo Double.
Las frases “abona un monto de depósito a una cuenta” y “carga un monto de retiro a una cuenta” indican que
las clases BaseDatosBanco y Cuenta deben realizar operaciones para actualizar una cuenta durante un depósito y
un retiro, respectivamente. Por lo tanto, asignamos las operaciones abonar y cargar a las clases BaseDatosBanco
y Cuenta. Tal vez recuerde que cuando se abona a una cuenta (como en un depósito) se suma un monto sólo al
atributo saldoTotal. Por otro lado, cuando se carga a una cuenta (como en un retiro) se resta el monto tanto
del saldo total como del saldo disponible. Ocultamos estos detalles de implementación dentro de la clase Cuenta.
Éste es un buen ejemplo de encapsulamiento y ocultamiento de información.
Si éste fuera un sistema ATM real, las clases BaseDatosBanco y Cuenta también proporcionarían un con-
junto de operaciones para permitir que otro sistema bancario actualizara el saldo de la cuenta de un usuario des-
pués de confirmar o rechazar todo, o parte de, un depósito. Por ejemplo, la operación confirmarMontoDeposito
sumaría un monto al atributo saldoDisponible, y haría que los fondos depositados estuvieran disponibles para
retirarlos. La operación rechazarMontoDeposito restaría un monto al atributo saldoTotal para indicar que un
monto especificado, que se había depositado recientemente a través del ATM y se había sumado al saldoTotal,
no se encontró en el sobre de depósito. El banco invocaría esta operación después de determinar que el usuario no
incluyó el monto correcto de efectivo o que algún cheque no fue validado (es decir, que “rebotó”). Aunque al
agregar estas operaciones nuestro sistema estaría más completo, no las incluiremos en nuestros diagramas de clases
ni en nuestra implementación, ya que se encuentran más allá del alcance de este ejemplo práctico.
La clase Pantalla “muestra un mensaje al usuario” en diversos momentos durante una sesión con el ATM.
Toda la salida visual se produce a través de la pantalla del ATM. El documento de requerimientos describe
muchos tipos de mensajes (por ejemplo, un mensaje de bienvenida, un mensaje de error, un mensaje de agra-
decimiento) que la pantalla muestra al usuario. El documento de requerimientos también indica que la pantalla
muestra indicadores y menús al usuario. No obstante, un indicador es en realidad sólo un mensaje que describe
lo que el usuario debe introducir a continuación, y un menú es en esencia un tipo de indicador que consiste en
una serie de mensajes (es decir, las opciones del menú) que se muestran en forma consecutiva. Por lo tanto, en vez
de asignar a la clase Pantalla una operación individual para mostrar cada tipo de mensaje, indicador y menú,
basta con crear una operación que pueda mostrar cualquier mensaje especificado por un parámetro. Colocamos
esta operación (mostrarMensaje) en el tercer compartimiento de la clase Pantalla en nuestro diagrama de
6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones... 243
clases (figura 6.21). Observe que no nos preocupa el parámetro de esta operación en estos momentos; lo mode-
laremos más adelante en esta sección.
De la frase “recibe entrada numérica del usuario” listada por la clase Teclado en la figura 6.20, podemos con-
cluir que la clase Teclado debe realizar una operación obtenerEntrada. A diferencia del teclado de una compu-
tadora, el teclado del ATM sólo contiene los números del 0 al 9, por lo cual especificamos que esta operación
devuelve un valor entero. Si recuerda, en el documento de requerimientos vimos que en distintas situaciones, tal
vez se requiera que el usuario introduzca un tipo distinto de número (por ejemplo, un número de cuenta, un NIP,
el número de una opción del menú, un monto de depósito como número de centavos). La clase Teclado sólo
obtiene un valor numérico para un cliente de la clase; no determina si el valor cumple con algún criterio específi-
co. Cualquier clase que utilice esta operación debe verificar que el usuario haya introducido un número apropiado
según el caso, y después debe responder de manera acorde (por ejemplo, mostrar un mensaje de error a través de
la clase Pantalla). [Nota: cuando implementemos el sistema, simularemos el teclado del ATM con el teclado
de una computadora y, por cuestión de simpleza, asumiremos que el usuario no escribirá datos de entrada que no
sean números, usando las teclas en el teclado de la computadora que no aparezcan en el teclado del ATM].
La figura 6.20 lista la frase “dispensa efectivo” para la clase DispensadorEfectivo. Por lo tanto, creamos la
operación dispensarEfectivo y la listamos bajo la clase DispensadorEfectivo en la figura 6.21. La clase Dis-
pensadorEfectivo también “indica si contiene suficiente efectivo para satisfacer una solicitud de retiro”. Para
esto incluimos a haySuficienteEfectivoDisponible, una operación que devuelve un valor de tipo Boolean
de UML, en la clase DispensadorEfectivo. La figura 6.20 también lista la frase “recibe un sobre de depósito”
para la clase RanuraDeposito. La ranura de depósito debe indicar si recibió un sobre, por lo que colocamos una
operación seRecibioSobre, la cual devuelve un valor Boolean, en el tercer compartimiento de la clase Ranu-
raDeposito. [Nota: es muy probable que una ranura de depósito de hardware real envíe una señal al ATM para
indicarle que se recibió un sobre. No obstante, simularemos este comportamiento con una operación en la clase
RanuraDeposito, que la clase ATM pueda invocar para averiguar si la ranura de depósito recibió un sobre].
No listamos ninguna operación para la clase ATM en este momento. Todavía no sabemos de algún servicio que
proporcione la clase ATM a otras clases en el sistema. No obstante, cuando implementemos el sistema en código
de Java, tal vez emerjan las operaciones de esta clase junto con las operaciones adicionales de las demás clases en
el sistema.
Identificar y modelar los parámetros de operación
Hasta ahora no nos hemos preocupado por los parámetros de nuestras operaciones; sólo hemos tratado de obtener
una comprensión básica de las operaciones de cada clase. Ahora daremos un vistazo más de cerca a varios paráme-
tros de operación. Para identificar los parámetros de una operación, analizamos qué datos requiere la operación
para realizar su tarea asignada.
Considere la operación autenticarUsuario de la clase BaseDatosBanco. Para autenticar a un usuario, esta
operación debe conocer el número de cuenta y el NIP que suministra el usuario. Por lo tanto, especificamos que
la operación autenticarUsuario debe recibir los parámetros enteros numeroCuentaUsuario y nipUsuario,
que la operación debe comparar con el número de cuenta y el NIP de un objeto Cuenta en la base de datos.
Colocaremos después de estos nombres de parámetros la palabra Usuario, para evitar confusión entre los nom-
bres de los parámetros de la operación y los nombres de los atributos que pertenecen a la clase Cuenta. Listamos
estos parámetros en el diagrama de clases de la figura 6.22, el cual modela sólo a la clase BaseDatosBanco. [Nota:
es perfectamente normal modelar sólo una clase en un diagrama de clases. En este caso lo que más nos preocu-
pa es analizar los parámetros de esta clase específica, por lo que omitimos las demás clases. Más adelante en los
diagramas de clase de este ejemplo práctico, en donde los parámetros dejarán de ser el centro de nuestra atención,
los omitiremos para ahorrar espacio. No obstante, recuerde que las operaciones que se listan en estos diagramas
siguen teniendo parámetros].
Recuerde que para modelar a cada parámetro en una lista de parámetros separados por comas, UML lista el
nombre del parámetro, seguido de un signo de dos puntos y el tipo del parámetro (en notación de UML). Así,
la figura 6.22 especifica que la operación autenticarUsuario recibe dos parámetros: numeroCuentaUsuario y
nipUsuario, ambos de tipo Integer. Cuando implementemos el sistema en Java, representaremos estos pará-
metros con valores int.
Las operaciones obtenerSaldoDisponible, obtenerSaldoTotal, abonar y cargar de la clase BaseDa-
tosBanco también requieren un parámetro nombreCuentaUsuario para identificar la cuenta a la cual la base de
datos debe aplicar las operaciones, por lo que incluimos estos parámetros en el diagrama de clases de la figura
244 Capítulo 6 Métodos: un análisis más detallado
6.22. Además, las operaciones abonar y cargar requieren un parámetro Double llamado monto, para especificar
el monto de dinero que se abonará o cargará, respectivamente.
El diagrama de clases de la figura 6.23 modela los parámetros de las operaciones de la clase Cuenta. La ope-
ración validarNIP sólo requiere un parámetro nipUsuario, el cual contiene el NIP especificado por el usuario,
que se comparará con el NIP asociado a la cuenta. Al igual que sus contrapartes en la clase BaseDatosBanco, las
operaciones abonar y cargar en la clase Cuenta requieren un parámetro Double llamado monto, el cual indica
la cantidad de dinero involucrada en la operación. Las operaciones obtenerSaldoDisponible y obtenerSal-
doTotal en la clase Cuenta no requieren datos adicionales para realizar sus tareas. Observe que las operaciones de
la clase Cuenta no requieren un parámetro de número de cuenta para diferenciar una cuenta de otra, ya que cada
una de estas operaciones se puede invocar sólo en un objeto Cuenta específico.
La figura 6.24 modela la clase Pantalla con un parámetro especificado para la operación mostrarMensaje.
Esta operación requiere sólo un parámetro String llamado mensaje, el cual indica el texto que debe mostrarse en
pantalla. Recuerde que los tipos de los parámetros que se enlistan en nuestros diagramas de clases están en nota-
ción de UML, por lo que el tipo String que se enlista en la figura 6.24 se refiere al tipo de UML. Cuando imple-
mentemos el sistema en Java, utilizaremos de hecho la clase String de Java para representar este parámetro.
El diagrama de clases de la figura 6.25 especifica que la operación dispensarEfectivo de la clase Dispen-
sadorEfectivo recibe un parámetro Double llamado monto para indicar el monto de efectivo (en dólares) que se
dispensará al usuario. La operación haySuficienteEfectivoDisponible también recibe un parámetro Double
llamado monto para indicar el monto de efectivo en cuestión.
figura 6.22 | La clase BaseDatosBanco con parámetros de operación.
BaseDatosBanco
autenticarUsuario(nombreCuentaUsuario : Integer, nipUsuario : Integer) : Boolean
obtenerSaldoDisponible(numeroCuentaUsuario : Integer) : Double
obtenerSaldoTotal(numeroCuentaUsuario : Integer) : Double
abonar(numeroCuentaUsuario : Integer, monto : Double)
cargar(numeroCuentaUsuario : Integer, monto : Double)
Figura 6.23 | La clase Cuenta con parámetros de operación.
Cuenta
numeroCuenta : Integer
nip : Integer
saldoDisponible : Double
saldoTotal : Double
validarNIP (nipUsuario : Integer) : Boolean
obtenerSaldoDisponible() : Double
obtenerSaldoTotal() : Double
abonar(monto : Double)
cargar(monto : Double)
Figura 6.24 | La clase Pantalla con parámetros de operación.
Pantalla
mostrarMensaje( mensaje : String )
6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones... 245
Observe que no hablamos sobre los parámetros para la operación ejecutar de las clases SolicitudSaldo,
Retiro y Depósito, de la operación obtenerEntrada de la clase Teclado y la operación seRecibioSobre de la
clase RanuraDeposito. En este punto de nuestro proceso de diseño, no podemos determinar si estas operaciones
requieren datos adicionales para realizar sus tareas, por lo que dejaremos sus listas de parámetros vacías. A medida
que avancemos por el ejemplo práctico, tal vez decidamos agregar parámetros a estas operaciones.
En esta sección hemos determinado muchas de las operaciones que realizan las clases en el sistema ATM.
Identificamoslosparámetrosylostiposdevaloresderetornodealgunasoperaciones.Amedidaquecontinuemoscon
nuestro proceso de diseño, el número de operaciones que pertenezcan a cada clase puede variar; podríamos
descubrir que se necesitan nuevas operaciones o que ciertas operaciones actuales no son necesarias; y podría-
mos determinar que algunas de las operaciones de nuestras clases necesitan parámetros adicionales y tipos de
valores de retorno distintos.
Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
6.1 ¿Cuál de las siguientes opciones no es un comportamiento?
a) Leer datos de un archivo.
b) Imprimir los resultados.
c) Imprimir texto.
d) Obtener la entrada del usuario.
6.2 Si quisiera agregar al sistema ATM una operación que devuelva el atributo monto de la clase Retiro, ¿cómo y
en dónde especificaría esta operación en el diagrama de clases de la figura 6.21?
6.3 Describa el significado del siguiente listado de operaciones, el cual podría aparecer en un diagrama de clases
para el diseño orientado a objetos de una calculadora:
sumar( x : Integer, y : Integer ) : Integer
Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software
6.1 c.
6.2 Para especificar una operación que obtenga el atributo monto de la clase Retiro, se debe colocar el siguiente
listado de operaciones en el (tercer) compartimiento de operaciones de la clase Retiro:
obtenerMonto( ) : Double
6.3 Este listado de operaciones indica una operación llamada sumar, la cual recibe los enteros x y y como paráme-
tros y devuelve un valor entero.
6.15 Conclusión
En este capítulo aprendió más acerca de los detalles de la declaración de métodos. También conoció la diferencia
entre los métodos static y los no static, y le mostramos cómo llamar a los métodos static, anteponiendo
al nombre del método el nombre de la clase en la cual aparece, y el separador punto (.). Aprendió a utilizar el
operador + para realizar concatenaciones de cadenas. Aprendió a declarar constantes con nombre, usando los
tipos enum y las variables public final static. Vio cómo usar la clase Random para generar conjuntos de núme-
ros aleatorios, que pueden usarse para simulaciones. También aprendió acerca del alcance de los campos y las
variables locales en una clase. Por último, aprendió que varios métodos en una clase pueden sobrecargarse, al
proporcionar métodos con el mismo nombre y distintas firmas. Dichos métodos pueden usarse para realizar las
mismas tareas, o tareas similares, usando distintos tipos o distintos números de parámetros.
Figura 6.25 | La clase DispensadorEfectivo con parámetros de operación.
DispensadorEfectivo
dispensarEfectivo( monto : Double )
haySuficienteEfectivoDisponible( monto : Double ) : Boolean
cuenta : Integer = 500
246 Capítulo 6 Métodos: un análisis más detallado
En el capítulo 7 aprenderá a mantener listas y tablas de datos en arreglos. Verá una implementación más
elegante de la aplicación que tira un dado 6000 veces, y dos versiones mejoradas de nuestro ejemplo práctico
LibroCalificaciones que estudió en los capítulos 3 a 5. También aprenderá cómo acceder a los argumentos de
línea de comandos de una aplicación, los cuales se pasan al método main cuando una aplicación comienza su
ejecución.
Resumen
Sección 6.1 Introducción
• La experiencia ha demostrado que la mejor forma de desarrollar y mantener un programa extenso es construirlo a
partir de piezas pequeñas y simples, o módulos. A esta técnica se le conoce como “divide y vencerás”.
Sección 6.2 Módulos de programas en Java
• Hay tres tipos de módulos en Java: métodos, clases y paquetes. Los métodos se declaran dentro de las clases. Por lo
general, las clases se agrupan en paquetes para que puedan importarse en los programas y reutilizarse.
• Los métodos nos permiten dividir un programa en módulos, al separar sus tareas en unidades autocontenidas. Las
instrucciones en un método se escriben sólo una vez, y se ocultan de los demás métodos.
• Utilizar los métodos existentes como bloques de construcción para crear nuevos programas es una forma de reutili-
zación del software, que nos permite evitar repetir código dentro de un programa.
Sección 6.3 Métodos static, campos static y la clase Math
• Una llamada a un método especifica el nombre del método a llamar y proporciona los argumentos que el método
al que se llamó requiere para realizar su tarea. Cuando termina la llamada al método, éste devuelve un resultado o
simplemente devuelve el control al método que lo llamó.
• Una clase puede contener métodos static para realizar tareas comunes que no requieren un objeto de la clase.
Cualquier información que pueda requerir un método static para realizar sus tareas se le puede enviar en forma de
argumentos, en una llamada al método. Para llamar a un método static, se especifica el nombre de la clase en la cual
está declarado el método, seguido de un punto (.) y del nombre del método, como en
NombreClase.nombreMétodo( argumentos )
• Los argumentos para los métodos pueden ser constantes, variables o expresiones.
• La clase Math cuenta con métodos static para realizar cálculos matemáticos comunes; además, declara dos
campos que representan constantes matemáticas de uso común: Math.PI y Math.E. La constante Math.PI
(3.14159265358979323846) es la relación entre la circunferencia de un círculo y su diámetro. La constante Math.E
(2.7182818284590452354) es el valor de la base para los logaritmos naturales (que se calculan con el método sta-
tic Math log).
• Math.PI y Math.E se declaran con los modificadores public, final y static. Al hacerlos public, otros progra-
madores pueden usar estos campos en sus propias clases. Cualquier campo declarado con la palabra clave final es
constante; su valor no se puede modificar una vez que se inicializa el campo. Tanto PI como E se declaran final, ya
que sus valores nunca cambian. Al hacer a estos campos static, se puede acceder a ellos a través del nombre de la
clase Math y un separador punto (.), justo igual que con los métodos de la clase Math.
• Cuando se crean objetos de una clase que contiene campos static (variables de clase), todos los objetos de esa clase
comparten una copia de los campos static. En conjunto, las variables de clase y las variables de instancia de la
clase representan sus campos. En la sección 8.11 aprenderá más acerca de los campos static.
• Al ejecutar la Máquina Virtual de Java (JVM) con el comando java, la JVM trata de invocar al método main de la
clase que usted le especifique. La JVM carga la clase especificada por NombreClase y utiliza el nombre de esa clase
para invocar al método main. Puede especificar una lista opcional de objetos String (separados por espacios) como
argumentos de línea de comandos, que la JVM pasará a su aplicación.
• Puede colocar un método main en cualquier clase que declare; sólo se llamará al método main en la clase que usted
utilice para ejecutar la aplicación. Algunos programadores aprovechan esto para crear un pequeño programa de
prueba en cada clase que declaran.
Resumen 247
248 Capítulo 6 Métodos: un análisis más detallado
Sección 6.4 Declaración de métodos con múltiples parámetros
• Cuando se hace una llamada a un método, el programa crea una copia de los valores de los argumentos del método
y los asigna a los parámetros correspondientes del mismo, que se crean e inicializan cuando se hace la llamada al
método. Cuando el control del programa regresa al punto en el que se hizo la llamada al método, los parámetros del
mismo se eliminan de la memoria.
• Un método puede devolver a lo más un valor, pero el valor devuelto podría ser una referencia a un objeto que con-
tenga muchos valores.
• Las variables deben declararse como campos de una clase, sólo si se requieren para usarlos en más de un método de
la clase, o si el programa debe guardar sus valores entre distintas llamadas a los métodos de la clase.
Sección 6.5 Notas acerca de cómo declarar y utilizar los métodos
• Hay tres formas de llamar a un método: usar el nombre de un método por sí solo para llamar a otro método de la
misma clase; usar una variable que contenga una referencia a un objeto, seguida de un punto (.) y del nombre del
método, para llamar a un método del objeto al que se hace referencia; y usar el nombre de la clase y un punto (.)
para llamar a un método static de una clase.
• Hay tres formas de devolver el control a una instrucción que llama a un método. Si el método no devuelve un resul-
tado, el control regresa cuando el flujo del programa llega a la llave derecha de terminación del método, o cuando se
ejecuta la instrucción
return;
si el método devuelve un resultado, la instrucción
return expresión;
evalúa la expresión, y después regresa de inmediato el valor resultante al método que hizo la llamada.
• Cuando un método tiene más de un parámetro, los parámetros se especifican como una lista separada por comas.
Debe haber un argumento en la llamada al método para cada parámetro en su declaración. Además, cada argumen-
to debe ser consistente con el tipo del parámetro correspondiente. Si un método no acepta argumentos, la lista de
parámetros está vacía.
• Los objetos String se pueden concatenar mediante el uso del operador +, que coloca los caracteres del operando
derecho al final de los que están en el operando izquierdo.
• Cada valor primitivo y objeto en Java tiene una representación String. Cuando se concatena un objeto con un
String, el objeto se convierte en un String y después, los dos String se concatenan.
• Para los valores primitivos que se utilizan en la concatenación de cadenas, la JVM maneja la conversión de los valores
primitivos a objetos String. Si un valor boolean se concatena con un objeto String, se utiliza la palabra "true"
o la palabra "false" para representar el valor boolean. Si hay ceros a la derecha en un valor de punto flotante, se
descartan cuando el número se concatena a un objeto String.
• Todos los objetos en Java tienen un método especial, llamado toString, el cual devuelve una representación String
del contenido del objeto. Cuando se concatena un objeto con un String, la JVM llama de manera implícita al
método toString del objeto, para obtener la representación String del mismo.
• Cuando se escribe una literal String extensa en el código fuente de un programa, algunas veces los programadores
dividen esa literal String en varias literales String más pequeñas, y las colocan en varias líneas de código para
mejorar la legibilidad, y después vuelven a ensamblar las literales String mediante la concatenación.
Sección 6.6 Pila de llamadas a los métodos y registros de activación
• Las pilas se conocen como estructuras de datos tipo “último en entrar, primero en salir (UEPS)”; el último elemento
que se mete (inserta) en la pila es el primer elemento que se saca (extrae) de ella.
• Un método al que se llama debe saber cómo regresar al método que lo llamó, por lo que la dirección de retorno del
método que hace la llamada se mete en la pila de ejecución del programa cuando se llama al método. Si ocurre una
serie de llamadas a métodos, las direcciones de retorno sucesivas se meten en la pila, en el orden último en entrar,
primero en salir, de manera que el último método en ejecutarse sea el primero en regresar al método que lo llamó.
• La pila de ejecución del programa contiene la memoria para las variables locales que se utilizan en cada invocación
de un método, durante la ejecución de un programa. Este dato se conoce como el registro de activación, o marco de
pila, de la llamada al método. Cuando se hace una llamada a un método, el registro de activación para la llamada
a ese método se mete en la pila de ejecución del programa. Cuando el método regresa al método que lo llamó, el
registro de activación para esta llamada al método se saca de la pila, y esas variables locales ya no son conocidas para
el programa. Si una variable local que contiene una referencia a un objeto es la única variable en el programa con una
Resumen 249
referencia a ese objeto, cuando el registro de activación que contiene esa variable local se saca de la pila, el programa
ya no puede acceder al objeto y, en un momento dado, la JVM lo eliminará de la memoria durante la “recolección
de basura”.
• La cantidad de memoria en una computadora es finita, por lo que sólo puede utilizarse cierta cantidad de memoria
para almacenar registros de activación en la pila de ejecución del programa. Si hay más llamadas a métodos de las
que se puedan almacenar en sus registros de activación en la pila de ejecución del programa, se produce un error
conocido como desbordamiento de pila. La aplicación se compilará correctamente, pero su ejecución producirá un
desbordamiento de pila.
Sección 6.7 Promoción y conversión de argumentos
• Una característica importante de las llamadas a métodos es la promoción de argumentos: convertir el valor de un
argumento al tipo que el método espera recibir en su parámetro correspondiente.
• Hay un conjunto de reglas de promoción que se aplican a las expresiones que contienen valores de dos o más tipos
primitivos, y a los valores de tipos primitivos que se pasan como argumentos para los métodos. Cada valor se pro-
mueve al tipo “más alto” en la expresión. En casos en los que se puede perder información debido a la conversión,
el compilador de Java requiere que utilicemos un operador de conversión de tipos para obligar explícitamente a que
ocurra la conversión.
Sección 6.9 Ejemplo práctico: generación de números aleatorios
• Los objetos de la clase Random (paquete java.util) pueden producir valores int, long, float o double. El método
random de Math puede producir valores double en el rango 0.0 ≤ x < 1.0, en donde x es el valor devuelto por el
método random.
• El método nextInt de Random genera un valor int aleatorio en el rango de –2,147,483,648 a +2,147,483,647. Los
valores devueltos por nextInt son en realidad números seudoaleatorios: una secuencia de valores producidos por
un cálculo matemático complejo. Ese cálculo utiliza la hora actual del día para sembrar el generador de números
aleatorios, de tal forma que cada ejecución del programa produzca una secuencia diferente de valores aleatorios.
• La clase Random cuenta con otra versión del método nextInt, la cual recibe un argumento int y devuelve un valor
desde 0 hasta el valor del argumento (pero sin incluirlo).
• Los números aleatorios en un rango pueden generarse mediante
numero = valorDesplazamiento + numerosAleatorios.nextInt( factorEscala );
en donde valorDesplazamiento especifica el primer número en el rango deseado de enteros consecutivos, y factorEs-
cala especifica cuántos números hay en el rango.
• Los números aleatorios pueden elegirse a partir de rangos de enteros no consecutivos, como en
numero = valorDesplazamiento +
diferenciaEntreValores * numerosAleatorios.nextInt( factorEscala );
en donde valorDesplazamiento especifica el primer número en el rango de valores, diferenciaEntreValores representa
la diferencia entre números consecutivos en la secuencia y factorEscala especifica cuántos números hay en el rango.
• Para depurar, algunas veces es conveniente repetir la misma secuencia de números seudoaleatorios durante cada
ejecución del programa, para demostrar que su aplicación funciona para una secuencia específica de números aleato-
rios, antes de probar el programa con distintas secuencias de números aleatorios. Cuando la repetitividad es impor-
tante, puede crear un objeto Random al pasar un valor entero long al constructor. Si se utiliza la misma semilla cada
vez que se ejecuta el programa, el objeto Random produce la misma secuencia de números aleatorios. También puede
establecer la semilla de un objeto Random en cualquier momento, llamando al método setSeed del objeto.
Sección 6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones)
• Una enumeración se introduce mediante la palabra clave enum y el nombre de un tipo. Al igual que con cualquier
clase, las llaves ({ y }) delimitan el cuerpo de una declaración enum. Dentro de las llaves hay una lista separada por
comas de constantes de enumeración, cada una de las cuales representa un valor único. Los identificadores en una
enum deben ser únicos. A las variables de tipo enum sólo se les pueden asignar constantes de ese tipo enum.
• Las constantes también pueden declararse como variables public final static. Dichas constantes se declaran todas
con letras mayúsculas por convención, para hacer que resalten en el programa.
Sección 6.11 Alcance de las declaraciones
• El alcance es la porción del programa en la que se puede hacer referencia a una entidad, como una variable o un
método, por su nombre. Se dice que dicha entidad está “dentro del alcance” para esa porción del programa.
250 Capítulo 6 Métodos: un análisis más detallado
• El alcance de la declaración de un parámetro es el cuerpo del método en el que aparece esa declaración.
• El alcance de la declaración de una variable local es a partir del punto en el que aparece la declaración, hasta el final
de ese bloque.
• El alcance de una etiqueta en una instrucción break o continue etiquetada es el cuerpo de la instrucción etique-
tada.
• El alcance de la declaración de una variable local que aparece en la sección de inicialización del encabezado de una
instrucción for es el cuerpo de la instrucción for, junto con las demás expresiones en el encabezado.
• El alcance de un método o campo de una clase es todo el cuerpo de la clase. Esto permite que los métodos de una
clase utilicen nombres simples para llamar a los demás métodos de la clase y acceder a los campos de la misma.
• Cualquier bloque puede contener declaraciones de variables. Si una variable local o parámetro en un método tiene
el mismo nombre que un campo, éste se oculta hasta que el bloque termina de ejecutarse.
Sección 6.12 Sobrecarga de métodos
• Java permite que se declaren varios métodos con el mismo nombre en una clase, siempre y cuando los métodos
tengan distintos conjuntos de parámetros (lo cual se determina en base al número, orden y tipos de los parámetros).
A esta técnica se le conoce como sobrecarga de métodos.
• Los métodos sobrecargados se distinguen por sus firmas: combinaciones de los nombres de los métodos y el número,
tipos y orden de sus parámetros. Los métodos no pueden distinguirse en base al tipo de valor de retorno.
Terminología
alcance de una declaración
argumento de línea de comandos
bloque
campos “ocultos”
Color, clase
componentes de software reutilizables
concatenación de cadenas
constante de enumeración
declaración de un método
desbordamiento de pila
desplazar un rango (números aleatorios)
dividir en módulos un programa con métodos
documentación de la API de Java
elemento de probabilidad
enum, palabra clave
enumeración
extraer (de una pila)
factor de escala (números aleatorios)
fillOval, método de la clase Graphics
fillRect, método de la clase Graphics
final, palabra clave
firma de un método
función
insertar (en una pila)
interfaz de programación de aplicaciones (API)
Interfaz de programación de aplicaciones de Java (API)
invocar a un método
lista de parámetros
lista de parámetros separados por comas
llamada a método
marco de pila
método “divide y vencerás”
método de clase
método declarado por el programador
módulo
nextInt, método de la clase Random
número seudoaleatorio
números aleatorios
ocultar los detalles de implementación
ocultar un campo
paquete
parámetro
parámetro formal
pila
pila de ejecución del programa
pila de llamadas a métodos
procedimiento
promoción de argumentos
promociones de tipos primitivos
random de la clase Math
Random, clase
registro de activación
reglas de promoción
relación jerárquica método jefe/método trabajador
return, palabra clave
reutilización de software
setColor, método de la clase Graphics
setSeed, método de la clase Random
simulación
sobrecarga de métodos
sobrecargar un método
último en entrar, primero en salir (UEPS), estructura de
datos
valor de desplazamiento (números aleatorios)
valor de semilla (números aleatorios)
valores RGB
variable de clase
variable local
Ejercicios de autoevaluación
6.1 Complete las siguientes oraciones:
a) Un método se invoca con un _________________.
b) A una variable que se conoce sólo dentro del método en el que está declarada, se le llama ____________.
c) La instrucción _________________ en un método llamado puede usarse para regresar el valor de una
expresión, al método que hizo la llamada.
d) La palabra clave _________________ indica que un método no devuelve ningún valor.
e) Los datos pueden agregarse o eliminarse sólo desde _________________ de una pila.
f) Las pilas se conocen como estructuras de datos_________________: el último elemento que se mete
(inserta) en la pila es el primer elemento que se saca (extrae) de ella.
g) Las tres formas de regresar el control de un método llamado a un solicitante son _________________,
_________________ y _________________.
h) Un objeto de la clase _________________ produce números aleatorios.
i) La pila de ejecución del programa contiene la memoria para las variables locales en cada invocación de
un método, durante la ejecución de un programa. Estos datos, almacenados como una parte de la pila
de ejecución del programa, se conocen como _________________ o _________________ de la llamada
al método.
j) Si hay más llamadas a métodos de las que puedan almacenarse en la pila de ejecución del programa, se
produce un error conocido como _________________.
k) El _________________ de una declaración es la porción del programa que puede hacer referencia a la
entidad en la declaración, por su nombre.
l) En Java, es posible tener varios métodos con el mismo nombre, en donde cada uno opere con distintos tipos
o números de argumentos. A esta característica se le llama _________________ de métodos.
m) La pila de ejecución del programa también se conoce como la pila de _________________.
6.2 Para la clase Craps de la figura 6.9, indique el alcance de cada una de las siguientes entidades:
a) la variable numerosAleatorios.
b) la variable dado1.
c) el método tirarDado.
d) el método jugar.
e) la variable sumaDeDados.
6.3 Escriba una aplicación que pruebe si los ejemplos de las llamadas a los métodos de la clase Math que se muestran
en la figura 6.2 realmente producen los resultados indicados.
6.4 Proporcione el encabezado para cada uno de los siguientes métodos:
a) El método hipotenusa, que toma dos argumentos de punto flotante con doble precisión, llamados lado1
y lado2, y que devuelve un resultado de punto flotante, con doble precisión.
b) El método menor, que toma tres enteros x, y y z, y devuelve un entero.
c) El método instrucciones, que no toma argumentos y no devuelve ningún valor. (Nota: estos métodos se
utilizan comúnmente para mostrar instrucciones a un usuario).
d) El método intAFloat, que toma un argumento entero llamado numero y devuelve un resultado de punto
flotante.
6.5 Encuentre el error en cada uno de los siguientes segmentos de programas. Explique cómo se puede corregir el
error.
a) int g()
{
System.out.println( "Dentro del metodo g" );
int h()
{
System.out.println( "Dentro del método h" );
}
}
Ejercicios de autoevaluación 251
252 Capítulo 6 Métodos: un análisis más detallado
b) int suma( int x, int y )
{
int resultado;
resultado = x + y;
}
c) voit f( float a );
{
float a;
System.out.println( a );
}
d) voit producto()
{
int a = 6, b = 5, c = 4, resultado;
resultado = a * b * c;
System.out.printf( "El resultado es %dn", resultado );
return resultado;
}
6.6 Escriba una aplicación completa en Java que pida al usuario el radio de tipo double de una esfera, y que llame
al método volumenEsfera para calcular y mostrar el volumen de esa esfera. Utilice la siguiente asignación para calcular
el volumen:
double volumen = ( 4.0 / 3.0 ) * Math.PI * Math.pow( radio, 3 )
Respuestas a los ejercicios de autoevaluación
6.1 a) llamada a un método. b) variable local. c) return. d) void. e) cima. f) último en entrar, primero en salir
(UEPS). g) return; o return expresión; o encontrar la llave derecha de cierre de un método. h) Random. i) registro
de activación. j) desbordamiento de pila. k) alcance. l) sobrecarga de métodos. m) llamadas a métodos.
6.2 a) el cuerpo de la clase. b) el bloque que define el cuerpo del método tirarDado. c) el cuerpo de la clase.
d) el cuerpo de la clase. e) el bloque que define el cuerpo del método jugar.
6.3 La siguiente solución demuestra el uso de los métodos de la clase Math de la figura 6.2:
1 // Ejercicio 6.3: PruebaMath.java
2 // Prueba de los métodos de la clase Math.
3
4 public class PruebaMath
5 {
6 public static void main( String args[] )
7 {
8 System.out.printf( "Math.abs( 23.7 ) = %fn", Math.abs( 23.7 ) );
9 System.out.printf( "Math.abs( 0.0 ) = %fn", Math.abs( 0.0 ) );
10 System.out.printf( "Math.abs( -23.7 ) = %fn", Math.abs( -23.7 ) );
11 System.out.printf( "Math.ceil( 9.2 ) = %fn", Math.ceil( 9.2 ) );
12 System.out.printf( "Math.ceil( -9.8 ) = %fn", Math.ceil( -9.8 ) );
13 System.out.printf( "Math.cos( 0.0 ) = %fn", Math.cos( 0.0 ) );
14 System.out.printf( "Math.exp( 1.0 ) = %fn", Math.exp( 1.0 ) );
15 System.out.printf( "Math.exp( 2.0 ) = %fn", Math.exp( 2.0 ) );
16 System.out.printf( "Math.floor( 9.2 ) = %fn", Math.floor( 9.2 ) );
17 System.out.printf( "Math.floor( -9.8 ) = %fn",
18 Math.floor( -9.8 ) );
19 System.out.printf( "Math.log( Math.E ) = %fn",
20 Math.log( Math.E ) );
21 System.out.printf( "Math.log( Math.E * Math.E ) = %fn",
22 Math.log( Math.E * Math.E ) );
6.4 a) double hipotenusa( double lado1, double lado2 )
b) int menor( int x, int y, int z )
c) void instrucciones()
d) float intAFloat( int numero )
6.5 a) Error: el método h está declarado dentro del método g.
Corrección: mueva la declaración de h fuera de la declaración de g.
b) Error: se supone que el método debe devolver un entero, pero no es así.
Corrección: elimine la variable resultado, y coloque la instrucción
return x + y;
en el método, o agregue la siguiente instrucción al final del cuerpo del método:
return resultado;
c) Error: el punto y coma que va después del paréntesis derecho de la lista de parámetros es incorrecto, y el
parámetro a no debe volver a declararse en el método.
Corrección: elimine el punto y coma que va después del paréntesis derecho de la lista de parámetros, y
elimine la declaración float a;.
Math.abs( 23.7 ) = 23.700000
Math.abs( 0.0 ) = 0.000000
Math.abs( -23.7 ) = 23.700000
Math.ceil( 9.2 ) = 10.000000
Math.ceil( -9.8 ) = -9.000000
Math.cos( 0.0 ) = 1.000000
Math.exp( 1.0 ) = 2.718282
Math.exp( 2.0 ) = 7.389056
Math.floor( 9.2 ) = 9.000000
Math.floor( -9.8 ) = -10.000000
Math.log( Math.E ) = 1.000000
Math.log( Math.E * Math.E ) = 2.000000
Math.max( 2.3, 12.7 ) = 12.700000
Math.max( -2.3, -12.7 ) = -2.300000
Math.min( 2.3, 12.7 ) = 2.300000
Math.min( -2.3, -12.7 ) = -12.700000
Math.pow( 2.0, 7.0 ) = 128.000000
Math.pow( 9.0, 0.5 ) = 3.000000
Math.sin( 0.0 ) = 0.000000
Math.sqrt( 900.0 ) = 30.000000
Math.sqrt( 9.0 ) = 3.000000
Math.tan( 0.0 ) = 0.000000
23 System.out.printf( "Math.max( 2.3, 12.7 ) = %fn",
24 Math.max( 2.3, 12.7 ) );
25 System.out.printf( "Math.max( -2.3, -12.7 ) = %fn",
26 Math.max( -2.3, -12.7 ) );
27 System.out.printf( "Math.min( 2.3, 12.7 ) = %fn",
28 Math.min( 2.3, 12.7 ) );
29 System.out.printf( "Math.min( -2.3, -12.7 ) = %fn",
30 Math.min( -2.3, -12.7 ) );
31 System.out.printf( "Math.pow( 2.0, 7.0 ) = %fn",
32 Math.pow( 2.0, 7.0 ) );
33 System.out.printf( "Math.pow( 9.0, 0.5 ) = %fn",
34 Math.pow( 9.0, 0.5 ) );
35 System.out.printf( "Math.sin( 0.0 ) = %fn", Math.sin( 0.0 ) );
36 System.out.printf( "Math.sqrt( 900.0 ) = %fn",
37 Math.sqrt( 900.0 ) );
38 System.out.printf( "Math.sqrt( 9.0 ) = %fn", Math.sqrt( 9.0 ) );
39 System.out.printf( "Math.tan( 0.0 ) = %fn", Math.tan( 0.0 ) );
40 } // fin de main
41 } // fin de la clase PruebaMath
Ejercicios de autoevaluación 253
254 Capítulo 6 Métodos: un análisis más detallado
d) Error: el método devuelve un valor cuando no debe hacerlo.
Corrección: cambie el tipo de valor de retorno de void a int.
6.6 La siguiente solución calcula el volumen de una esfera, utilizando el radio introducido por el usuario:
Escriba el radio de la esfera: 4
El volumen es 268.082573
1 // Ejercicio 6.6: Esfera.java
2 // Calcula el volumen de una esfera.
3 import java.util.Scanner;
4
5 public class Esfera
6 {
7 // obtiene el radio del usuario y muestra el volumen de la esfera
8 public void determinarVolumenEsfera()
9 {
10 Scanner entrada = new Scanner( System.in );
11
12 System.out.print( "Escriba el radio de la esfera: " );
13 double radio = entrada.nextDouble();
14
15 System.out.printf( "El volumen es %fn", volumenEsfera( radio ) );
16 } // fin del método determinarVolumenEsfera
17
18 // calcula y devuelve el volumen de una esfera
19 public double volumenEsfera( double radio )
20 {
21 double volumen = ( 4.0 / 3.0 ) * Math.PI * Math.pow( radio, 3 );
22 return volumen;
23 } // fin del método volumenEsfera
24 } // fin de la clase Esfera
1 // Ejercicio 6.6: PruebaEsfera.java
2 // Calcula el volumen de una esfera.
3
4 public class PruebaEsfera
5 {
6 // punto de inicio de la aplicación
7 public static void main( String args[] )
8 {
9 Esfera miEsfera = new Esfera();
10 miEsfera.determinarVolumenEsfera();
11 } // fin de main
12 } // fin de la clase PruebaEsfera
Ejercicios
6.7 ¿Cuál es el valor de x después de que se ejecuta cada una de las siguientes instrucciones?
a) x = Math.abs( 7.5 );
b) x = Math.floor( 7.5 );
c) x = Math.abs( 0.0 );
d) x = Math.ceil( 0.0 );
e) x = Math.abs( -6.4 );
f) x = Math.ceil( -6.4 );
g) x = Math.ceil( -Math.abs( -8 + Math.floor( -5.5 ) ) );
6.8 Un estacionamiento cobra una cuota mínima de $2.00 por estacionarse hasta tres horas. El estacionamiento
cobra $0.50 adicionales por cada hora o fracción que se pase de tres horas. El cargo máximo para cualquier periodo dado
de 24 horas es de $10.00. Suponga que ningún automóvil se estaciona durante más de 24 horas a la vez. Escriba una
aplicación que calcule y muestre los cargos por estacionamiento para cada cliente que se haya estacionado ayer. Debe
introducir las horas de estacionamiento para cada cliente. El programa debe mostrar el cargo para el cliente actual y
debe calcular y mostrar el total corriente de los recibos de ayer. El programa debe utilizar el método calcularCargos
para determinar el cargo para cada cliente.
6.9 Una aplicación del método Math.floor es redondear un valor al siguiente entero. La instrucción
y = Math.floor( x + 0.5 );
redondea el número x al entero más cercano y asigna el resultado a y. Escriba una aplicación que lea valores double y
que utilice la instrucción anterior para redondear cada uno de los números a su entero más cercano. Para cada número
procesado, muestre tanto el número original como el redondeado.
6.10 Math.floor puede utilizarse para redondear un número hasta un lugar decimal específico. La instrucción
y = Math.floor( x * 10 + 0.5 ) / 10;
redondea x en la posición de las décimas (es decir, la primera posición a la derecha del punto decimal). La instrucción
y = Math.floor( x * 100 + 0.5 ) / 100;
redondea x en la posición de las centésimas (es decir, la segunda posición a la derecha del punto decimal). Escriba una
aplicación que defina cuatro métodos para redondear un número x en varias formas:
a) redondearAInteger( numero )
b) redondearADecimas( numero )
c) redondearACentesimas( numero )
d) redondearAMilesimas( numero )
Para cada valor leído, su programa debe mostrar el valor original, el número redondeado al entero más cercano, el
número redondeado a la décima más cercana, el número redondeado a la centésima más cercana y el número redon-
deado a la milésima más cercana.
6.11 Responda a cada una de las siguientes preguntas:
a) ¿Qué significa elegir números “al azar”?
b) ¿Por qué es el método nextInt de la clase Random útil para simular juegos al azar?
c) ¿Por qué es a menudo necesario escalar o desplazar los valores producidos por un objeto Random?
d) ¿Por qué es la simulación computarizada de las situaciones reales una técnica útil?
6.12 Escriba instrucciones que asignen enteros aleatorios a la variable n en los siguientes rangos:
a) 1 ≤ n ≤ 2.
b) 1 ≤ n ≤ 100.
c) 0 ≤ n ≤ 9.
d) 1000 ≤ n ≤ 1112.
e) –1 ≤ n ≤ 1.
f) –3 ≤ n ≤ 11.
6.13 Para cada uno de los siguientes conjuntos de enteros, escriba una sola instrucción que imprima un número al
azar del conjunto:
a) 2, 4, 6, 8, 10.
b) 3, 5, 7, 9, 11.
c) 6, 10, 14, 18, 22.
6.14 Escriba un método llamado enteroPotencia( base, exponente ) que devuelva el valor de
baseexponente
Por ejemplo, enteroPotencia( 3, 4 ) calcula 34 (o 3 * 3 * 3 * 3 ). Suponga que exponente es un entero positivo
distinto de cero y que base es un entero. El método enteroPotencia debe utilizar un ciclo for o while para controlar
el cálculo. No utilice ningún método de la biblioteca de matemáticas. Incorpore este método en una aplicación que lea
valores enteros para base y exponente, y que realice el cálculo con el método enteroPotencia.
Ejercicios 255
256 Capítulo 6 Métodos: un análisis más detallado
6.15 Defina un método llamado hipotenusa que calcule la longitud de la hipotenusa de un triángulo rectángulo,
cuando se proporcionen las longitudes de los otros dos lados. (Utilice los datos de ejemplo de la figura 6.26.) El método
debe tomar dos argumentos de tipo double y devolver la hipotenusa como un valor double. Incorpore este método en
una aplicación que lea los valores para lado1 y lado2, y que realice el cálculo con el método hipotenusa. Determine
la longitud de la hipotenusa para cada uno de los triángulos de la figura 6.26.
6.16 Escriba un método llamado multiplo que determine, para un par de enteros, si el segundo entero es múltiplo
del primero. El método debe tomar dos argumentos enteros y devolver true si el segundo es múltiplo del primero, y
false en caso contrario. [Sugerencia: utilice el operador residuo]. Incorpore este método en una aplicación que reciba
como entrada una serie de pares de enteros (un par a la vez) y determine si el segundo valor en cada par es un múltiplo
del primero.
6.17 Escriba un método llamado esPar que utilice el operador residuo (%) para determinar si un entero dado es par.
El método debe tomar un argumento entero y devolver true si el entero es par, y false en caso contrario. Incorpore
este método en una aplicación que reciba como entrada una secuencia de enteros (uno a la vez), y que determine si cada
uno es par o impar.
6.18 Escriba un método llamado cuadradoDeAsteriscos que muestre un cuadrado relleno (el mismo número de
filas y columnas) de asteriscos cuyo lado se especifique en el parámetro entero lado. Por ejemplo, si lado es 4, el método
debe mostrar:
****
****
****
****
Incorpore este método a una aplicación que lea un valor entero para el parámetro lado que teclea el usuario, y desplie-
gue los asteriscos con el método cuadradoDeAsteriscos.
6.19 Modifique el método creado en el ejercicio 6.18 para formar el cuadrado de cualquier carácter que esté conte-
nido en el parámetro tipo carácter caracterRelleno. Por ejemplo, si lado es 5 y caracterRelleno es “#”, el método
debe imprimir
####
####
####
####
####
6.20 Escriba una aplicación que pida al usuario el radio de un círculo y que utilice un método llamado circuloArea
para calcular e imprimir el área de ese círculo.
6.21 Escriba segmentos de programas que realicen cada una de las siguientes tareas:
a) Calcular la parte entera del cociente, cuando el entero a se divide entre el entero b.
b) Calcular el residuo entero cuando el entero a se divide entre el entero b.
c) Utilizar las piezas de los programas desarrollados en las partes (a) y (b) para escribir un método llamado
mostrarDigitos, que reciba un entero entre 1 y 99999, y que lo muestre como una secuencia de dígitos,
separando cada par de dígitos por dos espacios. Por ejemplo, el entero 4562 debe aparecer como
4 5 6 2
d) Incorpore el método desarrollado en la parte (c) en una aplicación que reciba como entrada un entero y que
llame al método mostrarDigitos, pasándole a este método el entero introducido. Muestre los resultados.
Triángulo Lado 1 Lado 2
1 3.0 4.0
2 5.0 12.0
3 8.0 15.0
figura 6.26 | Valores para los lados de los triángulos del ejercicio 6.15.
6.22 Implemente los siguientes métodos enteros:
a) El método centigrados que devuelve la equivalencia en grados centígrados de una temperatura en grados
fahrenheit, utilizando el cálculo
centigrados = 5.0 / 9.0 * ( fahrenheit – 32 );
b) El método fahrenheit que devuelve la equivalencia en grados fahrenheit de una temperatura en grados
centígrados, utilizando el cálculo
fahrenheit = 9.0 / 5.0 * centigrados + 32;
c) Utilice los métodos de las partes (a) y (b) para escribir una aplicación que permita al usuario, ya sea escribir
una temperatura en grados fahrenheit y mostrar su equivalente en grados centígrados, o escribir una tem-
peratura en grados centígrados y mostrar su equivalente en grados fahrenheit.
6.23 Escriba un método llamado minimo3 que devuelva el menor de tres números de punto flotante. Use el método
Math.min para implementar minimo3. Incorpore el método en una aplicación que reciba como entrada tres valores por
parte del usuario, determine el valor menor y muestre el resultado.
6.24 Se dice que un número entero es un número perfecto si sus factores, incluyendo 1 (pero no el número entero),
al sumarse dan como resultado el número entero. Por ejemplo, 6 es un número perfecto ya que 6 = 1 + 2 + 3. Escriba
un método llamado perfecto que determine si el parámetro numero es un número perfecto. Use este método en una
aplicación que determine y muestre todos los números perfectos entre 1 y 1000. Imprima los factores de cada número
perfecto para confirmar que el número sea realmente perfecto. Ponga a prueba el poder de su computadora, evaluando
números más grandes que 1000. Muestre los resultados.
6.25 Se dice que un entero es primo si puede dividirse solamente por 1 y por sí mismo. Por ejemplo, 2, 3, 5 y 7 son
primos, pero 4, 6, 8 y 9 no.
a) Escriba un método que determine si un número es primo.
b) Use este método en una aplicación que determine e imprima todos los números primos menores que
10,000. ¿Cuántos números hasta 10,000 tiene que probar para asegurarse de encontrar todos los números
primos?
c) Al principio podría pensarse que n/2 es el límite superior para evaluar si un número es primo, pero lo máxi-
mo que se necesita es ir hasta la raíz cuadrada de n. ¿Por qué? Vuelva a escribir el programa y ejecútelo de
ambas formas.
6.26 Escriba un método que tome un valor entero y devuelva el número con sus dígitos invertidos. Por ejemplo,
para el número 7631, el método debe regresar 1367. Incorpore el método en una aplicación que reciba como entrada
un valor del usuario y muestre el resultado.
6.27 El máximo común divisor (MCD) de dos enteros es el entero más grande que puede dividir uniformemente a
cada uno de los dos números. Escriba un método llamado mcd que devuelva el máximo común divisor de dos enteros.
[Sugerencia: tal vez sea conveniente que utilice el algoritmo de Euclides. Puede encontrar información acerca de este
algoritmo en es.wikipedia.org/wiki/Algoritmo_de_Euclides]. Incorpore el método en una aplicación que reciba
como entrada dos valores del usuario y muestre el resultado.
6.28 Escriba un método llamado puntosCalidad que reciba como entrada el promedio de un estudiante y devuelva
4 si el promedio se encuentra entre 90 y 100, 3 si el promedio se encuentra entre 80 y 89, 2 si el promedio se encuentra
entre 70 y 79, 1 si el promedio se encuentra entre 60 y 69, y 0 si el promedio es menor de 60. Incorpore el método en
una aplicación que reciba como entrada un valor del usuario y muestre el resultado.
6.29 Escriba una aplicación que simule el lanzamiento de monedas. Deje que el programa lance una moneda cada
vez que el usuario seleccione la opción del menú “Lanzar moneda”. Cuente el número de veces que aparezca cada uno
de los lados de la moneda. Muestre los resultados. El programa debe llamar a un método separado, llamado tirar, que
no tome argumentos y devuelva false en caso de cara, y true en caso de cruz. [Nota: si el programa simula en forma
realista el lanzamiento de monedas, cada lado de la moneda debe aparecer aproximadamente la mitad del tiempo.]
6.30 Las computadoras están tomando un papel cada vez más importante en la educación. Escriba un programa que
ayude a un estudiante de escuela primaria, para que aprenda a multiplicar. Use un objeto Random para producir dos
enteros positivos de un dígito. El programa debe entonces mostrar una pregunta al usuario, como:
¿Cuánto es 6 por 7?
Ejercicios 257
258 Capítulo 6 Métodos: un análisis más detallado
El estudiante entonces debe escribir la respuesta. Luego, el programa debe verificar la respuesta del estudiante. Si es
correcta, dibuje la cadena "Muy bien!" y haga otra pregunta de multiplicación. Si la respuesta es incorrecta, dibuje la
cadena "No. Por favor intenta de nuevo." y deje que el estudiante intente la misma pregunta varias veces, hasta
que esté correcta. Debe utilizarse un método separado para generar cada pregunta nueva. Este método debe llamarse
una vez cuando la aplicación empiece a ejecutarse, y cada vez que el usuario responda correctamente a la pregunta.
6.31 El uso de las computadoras en la educación se conoce como instrucción asistida por computadora (CAI, por sus
siglas en inglés). Un problema que se desarrolla en los entornos CAI es la fatiga de los estudiantes. Este problema puede
eliminarse si se varía el diálogo de la computadora para mantener la atención del estudiante. Modifique el programa
del ejercicio 6.30 de manera que los diversos comentarios se impriman para cada respuesta correcta e incorrecta, de la
siguiente manera:
Contestaciones a una respuesta correcta:
Muy bien!
Excelente!
Buen trabajo!
Sigue asi!
Contestaciones a una respuesta incorrecta:
No. Por favor intenta de nuevo.
Incorrecto. Intenta una vez mas.
No te rindas!
No. Sigue intentando.
Use la generación de números aleatorios para elegir un número entre 1 y 4 que se utilice para seleccionar una
contestación apropiada a cada respuesta. Use una instrucción switch para emitir las contestaciones.
6.32 Los sistemas de instrucción asistida por computadora más sofisticados supervisan el rendimiento del estudiante
durante cierto tiempo. La decisión de empezar un nuevo tema se basa a menudo en el éxito del estudiante con los
temas anteriores. Modifique el programa del ejercicio 6.31 para contar el número de respuestas correctas e incorrectas
por parte del estudiante. Una vez que el estudiante escriba 10 respuestas, su programa debe calcular el porcentaje de
respuestas correctas. Si éste es menor del 75%, imprima Por favor pida ayuda adicional a su instructor y re-
inicie el programa, para que otro estudiante pueda probarlo.
6.33 Escriba una aplicación que juegue a “adivina el número” de la siguiente manera: su programa elige el número
a adivinar, seleccionando un entero aleatorio en el rango de 1 a 1000. La aplicación muestra el indicador Adivine un
número entre 1 y 1000. El jugador escribe su primer intento. Si la respuesta del jugador es incorrecta, su programa
debe mostrar el mensaje Demasiado alto. Intente de nuevo. o Demasiado bajo. Intente de nuevo., para
ayudar a que el jugador “se acerque” a la respuesta correcta. El programa debe pedir al usuario que escriba su siguiente
intento. Cuando el usuario escriba la respuesta correcta, muestre el mensaje Felicidades. Adivino el numero! y
permita que el usuario elija si desea jugar otra vez. [Nota: la técnica para adivinar empleada en este problema es similar
a una búsqueda binaria, que veremos en el capítulo 16, Búsqueda y ordenamiento].
6.34 Modifique el programa del ejercicio 6.33 para contar el número de intentos que haga el jugador. Si el número es
10 o menos, imprima el mensaje O ya sabia usted el secreto, o tuvo suerte! Si el jugador adivina el número
en 10 intentos, imprima el mensaje Aja! Sabía usted el secreto! Si el jugador hace más de 10 intentos, imprima
el mensaje Deberia haberlo hecho mejor! ¿Por qué no se deben requerir más de 10 intentos? Bueno, en cada “buen
intento”, el jugador debe poder eliminar la mitad de los números, después la mitad de los números restantes, y así en lo
sucesivo.
6.35 En los ejercicios 6.30 al 6.32 se desarrolló un programa de instrucción asistida por computadora para enseñar
a un estudiante de escuela primara cómo multiplicar. Realice las siguientes mejoras:
a) Modifique el programa para que permita al usuario introducir un nivel de capacidad escolar. Un nivel de 1
significa que el programa debe usar sólo números de un dígito en los problemas, un nivel 2 significa que el
programa debe utilizar números de dos dígitos máximo, etcétera.
b) Modifique el programa para permitir al usuario que elija el tipo de problemas aritméticos que desea estu-
diar. Una opción de 1 significa problemas de suma solamente, 2 significa problemas de resta, 3 significa pro-
blemas de multiplicación, 4 significa problemas de división y 5 significa una mezcla aleatoria de problemas
de todos estos tipos.
6.36 Escriba un método llamado distancia, para calcular la distancia entre dos puntos (x1, y1) y (x2, y2). Todos los
números y valores de retorno deben ser de tipo double. Incorpore este método en una aplicación que permita al usuario
introducir las coordenadas de los puntos.
6.37 Modifique el programa Craps de la figura 6.9 para permitir apuestas. Inicialice la variable saldoBanco con
$1000. Pida al jugador que introduzca una apuesta. Compruebe que esa apuesta sea menor o igual al saldoBanco y,
si no lo es, haga que el usuario vuelva a introducir la apuesta hasta que se introduzca un valor válido. Después de esto,
comience un juego de craps. Si el jugador gana, agregue la apuesta al saldoBanco e imprima el nuevo saldoBanco.
Si el jugador pierde, reste la apuesta al saldoBanco, imprima el nuevo saldoBanco, compruebe si saldoBanco se ha
vuelto cero y, de ser así, imprima el mensaje "Lo siento. Se quedo sin fondos!" A medida que el juego progrese,
imprima varios mensajes para crear algo de “charla”, como "Oh, se esta yendo a la quiebra, verdad?", o "Oh,
vamos, arriesguese!", o "La hizo en grande. Ahora es tiempo de cambiar sus fichas por efectivo!".
Implemente la “charla” como un método separado que seleccione en forma aleatoria la cadena a mostrar.
6.38 Escriba una aplicación que muestre una tabla de los equivalentes en binario, octal y hexadecimal de los números
decimales en el rango de 1 al 256. Si no está familiarizado con estos sistemas numéricos, lea el apéndice E primero.
Ejercicios 259
Arreglos
OBJETIVO S
En este capítulo aprenderá a:
Conocer qué son los arreglos.
Utilizar arreglos para almacenar datos en, y obtenerlos de listas
y tablas de valores.
Declarar arreglos, inicializarlos y hacer referencia a elementos
individuales de los arreglos.
Utilizar la instrucción for mejorada para iterar a través de los
arreglos.
Pasar arreglos a los métodos.
Declarar y manipular arreglos multidimensionales.
Escribir métodos que utilicen listas de argumentos de longitud
variable.
Leer los argumentos de línea de comandos en un programa.
Q
Q
Q
Q
Q
Q
Q
Q
Ahora ve, escríbelo
ante ellos en una tabla,
y anótalo en un libro.
—Isaías 30:8
Ir más allá es tan malo
como no llegar.
—Confucio
Comienza en el principio…
y continúa hasta que llegues
al final; después detente.
—Lewis Carroll
7
7.1 Introducción
En este capítulo presentamos el importante tema de las estructuras de datos: colecciones de elementos de datos
relacionados. Los arreglos son estructuras de datos que consisten de elementos de datos relacionados, del mismo
tipo. Los arreglos son entidades de longitud fija; conservan la misma longitud una vez creados, aunque puede
reasignarse una variable tipo arreglo de tal forma que haga referencia a un nuevo arreglo de distinta longitud. En
los capítulos 17 a 19 estudiaremos con detalle las estructuras de datos.
Después de hablar acerca de cómo se declaran, crean y inicializan los arreglos, presentaremos una serie de
ejemplos prácticos que demuestran varias manipulaciones comunes de los arreglos. También presentaremos
un ejemplo práctico en el que se examina la forma en que los arreglos pueden ayudar a simular los procesos de
barajar y repartir cartas, para utilizarlos en una aplicación que implementa un juego de cartas. Después presen-
taremos la instrucción for mejorada de java, la cual permite que un programa acceda a los datos en un arreglo
con más facilidad que la instrucción for controlada por contador, que presentamos en la sección 5.3. Hay dos
secciones de este capítulo en las que se amplía el ejemplo práctico de la clase LibroCalificaciones de los capítu-
los 3 a 5. En especial, utilizaremos los arreglos para permitir que la clase mantenga un conjunto de calificaciones
en memoria y analizar las calificaciones que obtuvieron los estudiantes en distintos exámenes en un semestre; dos
herramientas que no están presentes en las versiones anteriores de la clase. Éstos y otros ejemplos del capítulo
demostrarán las formas en las que los arreglos permiten a los programadores organizar y manipular datos.
7.2 Arreglos
En Java, un arreglo es un grupo de variables (llamadas elementos o componentes) que contienen valores, todos
del mismo tipo. Recuerde que los tipos en Java se dividen en dos categorías: tipos primitivos y tipos de referen-
cia. Los arreglos son objetos, por lo que se consideran como tipos de referencia. Como veremos pronto, lo que
consideramos generalmente como un arreglo es en realidad una referencia a un objeto arreglo en memoria. Los
elementos de un arreglo pueden ser tipos primitivos o de referencia (incluyendo arreglos, como veremos en la
sección 7.9). Para hacer referencia a un elemento específico en un arreglo, debemos especificar el nombre de
la referencia al arreglo y el número de la posición del elemento en el arreglo. El número de la posición del elemen-
to se conoce formalmente como el índice o subíndice del elemento.
En la figura 7.1 se muestra una representación lógica de un arreglo de enteros, llamado c. Este arreglo con-
tiene 12 elementos. Un programa puede hacer referencia a cualquiera de estos elementos mediante una expresión
de acceso a un arreglo que incluye el nombre del arreglo, seguido por el índice del elemento específico encerrado
7.1 Introducción
7.2 Arreglos
7.3 Declaración y creación de arreglos
7.4 Ejemplos acerca del uso de los arreglos
7.5 Ejemplo práctico: simulación para barajar y repartir cartas
7.6 Instrucción for mejorada
7.7 Paso de arreglos a los métodos
7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones
7.9 Arreglos multidimensionales
7.10 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo bidimensional
7.11 Listas de argumentos de longitud variable
7.12 Uso de argumentos de línea de comandos
7.13 (Opcional) Ejemplo práctico de GUI y gráficos: cómo dibujar arcos
7.14 (Opcional) Ejemplo práctico de Ingeniería de Software: colaboración entre los objetos
7.15 Conclusión
Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios
| Sección especial: construya su propia computadora
Pla
n
g
e
ne
r
a
l
7.2 Arreglos 261
262 Capítulo 7 Arreglos
Figura 7.1 | Un arreglo con 12 elementos.
-45
62
-3
1
6453
78
0
-89
1543
72
0
6
c[ 0 ]
Nombre del arreglo (c)
Índice (o subíndice) del
elemento en el arreglo c
c[ 7 ]
c[ 8 ]
c[ 9 ]
c[ 10 ]
c[ 11 ]
c[ 6 ]
c[ 5 ]
c[ 4 ]
c[ 3 ]
c[ 2 ]
c[ 1 ]
entre corchetes ([]). El primer elemento en cualquier arreglo tiene el índice cero, y algunas veces se le denomina
elemento cero. Por lo tanto, los elementos del arreglo c son c[ 0 ], c[ 1 ], c[ 2 ], y así en lo sucesivo. El
mayor índice en el arreglo c es 11: 1 menos que 12, el número de elementos en el arreglo. Los nombres de los
arreglos siguen las mismas convenciones que los demás nombres de variables.
Un índice debe ser un entero positivo. Un programa puede utilizar una expresión como índice. Por ejemplo,
si suponemos que la variable a es 5 y que b es 6, entonces la instrucción
c[ a + b ] += 2;
suma 2 al elemento c[ 11 ] del arreglo. Observe que el nombre del arreglo con subíndice es una expresión de
acceso al arreglo. Dichas expresiones pueden utilizarse en el lado izquierdo de una asignación, para colocar un
nuevo valor en un elemento del arreglo.
Error común de programación 7.1
Usar un valor de tipo long como índice de un arreglo produce un error de compilación. Un índice debe ser un valor
int, o un valor de un tipo que pueda promoverse a int; a saber, byte, short o char, pero no long.
Examinaremos el arreglo c de la figura 7.1 con más detalle. El nombre del arreglo es c. Cada instancia de
un objeto conoce su propia longitud y mantiene esta información en un campo length. La expresión c.length
accede al campo length del arreglo c para determinar la longitud del arreglo. Observe que, aun cuando el miem-
bro length de un arreglo es public, no puede cambiarse, ya que es una variable final. La manera en que se hace
referencia a los 12 elementos de este arreglo es: c[ 0 ], c[ 1 ], c[ 2 ], …, c[ 11 ]. El valor de c[ 0 ] es
-45, el valor de c[ 1 ] es 6, el de c[ 2 ] es 0, el de c[ 7 ] es 62 y el de c[ 11 ] es 78. Para calcular la suma
de los valores contenidos en los primeros tres elementos del arreglo c y almacenar el resultado en la variable
suma, escribiríamos lo siguiente:
suma = c[ 0 ] + c[ 1 ] + c[ 2 ];
Para dividir el valor de c[ 6 ] entre 2 y asignar el resultado a la variable x, escribiríamos lo siguiente:
x = c[ 6 ] / 2;
7.3 Declaración y creación de arreglos
Los objetos arreglo ocupan espacio en memoria. Al igual que los demás objetos, los arreglos se crean con la
palabra clave new. Para crear un objeto arreglo, el programador especifica el tipo de cada elemento y el número
7.3 Declaración y creación de arreglos 263
de elementos que se requieren para el arreglo, como parte de una expresión para crear un arreglo que utiliza la
palabra clave new. Dicha expresión devuelve una referencia que puede almacenarse en una variable tipo arreglo.
La siguiente declaración y expresión crea un objeto arreglo, que contiene 12 elementos int, y almacena la refe-
rencia del arreglo en la variable c:
int c[] = new int[ 12 ];
Esta expresión puede usarse para crear el arreglo que se muestra en la figura 7.1. Esta tarea también puede reali-
zarse en dos pasos, como se muestra a continuación:
int c[ ]; // declara la variable arreglo
c = new int[ 12 ]; // crea el arreglo; lo asigna a la variable tipo arreglo
En la declaración, los corchetes que van después del nombre de la variable c indican que c es una variable que hará
referencia a un arreglo de valores int (es decir, c almacenará una referencia a un objeto arreglo). En la instrucción
de asignación, la variable arreglo c recibe la referencia a un nuevo objeto arreglo de 12 elementos int. Al crear un
arreglo, cada uno de sus elementos recibe un valor predeterminado: cero para los elementos numéricos de tipos
primitivos, false para los elementos boolean y null para las referencias (cualquier tipo no primitivo). Como
pronto veremos, podemos proporcionar valores iniciales para los elementos no específicos ni predeterminados al
crear un arreglo.
Error común de programación 7.2
En la declaración de un arreglo, si se especifica el número de elementos en los corchetes de la declaración (por ejemplo,
int c[ 12 ];) se produce un error de sintaxis.
Un programa puede crear varios arreglos en una sola declaración. La siguiente declaración de un arreglo
String reserva 100 elementos para b y 27 para x:
String b[] = new String[ 100 ], x[] = new String[ 27 ];
En este caso, se aplica el nombre de la clase String a cada variable en la declaración. Por cuestión de legibilidad,
es preferible declarar sólo una variable en cada declaración, como en:
String b[] = new string[ 100 ]; // crea el arreglo b
String x[] = new string[ 27 ]; // crea el arreglo x
Buena práctica de programación 7.1
Por cuestión de legibilidad, declare sólo una variable en cada declaración. Mantenga cada declaración en una línea
separada e incluya un comentario que describa a la variable que está declarando.
Cuando se declara un arreglo, su tipo y los corchetes pueden combinarse al principio de la declaración para
indicar que todos los identificadores en la declaración son variables tipo arreglo. Por ejemplo, la declaración
double[] arreglo1, arreglo2;
indica que arreglo1 y arreglo2 son variables tipo “arreglo de double”. La anterior declaración es equiva-
lente a:
double arreglo1[];
double arreglo2[];
o
double[] arreglo1;
double[] arreglo2;
Los pares anteriores de declaraciones son equivalentes; cuando se declara sólo una variable en cada declaración, los
corchetes pueden colocarse ya sea antes del tipo, o después del nombre de la variable tipo arreglo.
264 Capítulo 7 Arreglos
Error común de programación 7.3
Declarar múltiples variables tipo arreglo en una sola declaración puede provocar errores sutiles. Considere la declara-
ción int[] a, b, c;. Si a, b y c deben declararse como variables tipo arreglo, entonces esta declaración es correcta;
al colocar corchetes directamente después del tipo, indicamos que todos los identificadores en la declaración son
variables tipo arreglo. No obstante, si sólo a debe ser una variable tipo arreglo, y b y c deben ser variables int indi-
viduales, entonces esta declaración es incorrecta; la declaración int a[], b, c; lograría el resultado deseado.
Un programa puede declarar arreglos de cualquier tipo. Cada elemento de un arreglo de tipo primitivo
contiene un valor del tipo declarado del arreglo. De manera similar, en un arreglo de un tipo de referencia, cada
elemento es una referencia a un objeto del tipo declarado del arreglo. Por ejemplo, cada elemento de un arreglo
int es un valor int, y cada elemento de un arreglo String es una referencia a un objeto String.
7.4 Ejemplos acerca del uso de los arreglos
En esta sección presentaremos varios ejemplos que muestran cómo declarar, crear e inicializar arreglos y cómo
manipular sus elementos.
Cómo crear e inicializar un arreglo
En la aplicación de la figura 7.2 se utiliza la palabra clave new para crear un arreglo de 10 elementos int, los cuales
inicialmente tienen el valor cero (el valor predeterminado para las variables int).
En la línea 8 se declara arreglo, una referencia capaz de referirse a un arreglo de elementos int. En la línea
10 se crea el objeto arreglo y se asigna su referencia a la variable arreglo. La línea 12 imprime los encabezados
de las columnas. La primera columna representa el índice (0 a 9) para cada elemento del arreglo, y la segunda el
valor predeterminado (0) de cada elemento del arreglo.
1 // Fig. 7.2: InicArreglo.java
2 // Creación de un arreglo.
3
4 public class InicArreglo
5 {
6 public static void main( String args[] )
7 {
8 int arreglo[]; // declara un arreglo con el mismo nombre
9
10 arreglo = new int[ 10 ]; // crea el espacio para el arreglo
11
12 System.out.printf( "%s%8sn", "Indice", "Valor" ); // encabezados de columnas
13
14 // imprime el valor de cada elemento del arreglo
15 for ( int contador = 0; contador < arreglo.length; contador++ )
16 System.out.printf( "%5d%8dn", contador, arreglo[ contador ] );
17 } // fin de main
18 } // fin de la clase InicArreglo
Figura 7.2 | Inicialización de los elementos de un arreglo con valores predeterminados de cero.
Indice Valor
0 0
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 0
La instrucción for en las líneas 15 y 16 imprime el número de índice (representado por contador) y el valor
de cada elemento del arreglo (representado por arreglo[ contador ]). Observe que al principio la variable de
control del ciclo contador es 0 (los valores de los índices empiezan en 0, por lo que al utilizar un conteo con base
cero se permite al ciclo acceder a todos los elementos del arreglo. La condición de continuación de ciclo de la
instrucción for utiliza la expresión arreglo.length (línea 15) para determinar la longitud del arreglo. En este
ejemplo la longitud del arreglo es de 10, por lo que el ciclo continúa ejecutándose mientras el valor de la variable
de control contador sea menor que 10. El valor más alto para el subíndice de un arreglo de 10 elementos es 9,
por lo que al utilizar el operador “menor que” en la condición de continuación de ciclo se garantiza que el ciclo no
trate de acceder a un elemento más allá del final del arreglo (es decir, durante la iteración final del ciclo, contador
es 9). Pronto veremos lo que hace Java cuando encuentra un subíndice fuera de rango en tiempo de ejecución.
Uso de un inicializador de arreglo
Un programa puede crear un arreglo e inicializar sus elementos con un inicializador de arreglo, que es una lista
de expresiones separadas por comas (la cual se conoce también como lista inicializadora) encerrada entre llaves
({ y }); la longitud del arreglo se determina en base al número de elementos en la lista inicializadora. Por ejemplo,
la declaración
int n[] = { 10, 20, 30, 40, 50 };
crea un arreglo de cinco elementos con los valores de índices 0, 1, 2, 3 y 4. El elemento n[ 0 ] se inicializa con
10, n[ 1 ] se inicializa con 20, y así en lo sucesivo. Esta declaración no requiere que new cree el objeto arreglo.
Cuando el compilador encuentra la declaración de un arreglo que incluye una lista inicializadora, cuenta el
número de inicializadores en la lista para determinar el tamaño del arreglo, y después establece la operación new
apropiada “detrás de las cámaras”.
La aplicación de la figura 7.3 inicializa un arreglo de enteros con 10 valores (línea 9) y muestra el arreglo en
formato tabular. El código para mostrar los elementos del arreglo (líneas 14 y 15) es idéntico al de la figura 7.2
(líneas 15 y 16).
7.4 Ejemplos acerca del uso de los arreglos 265
1 // Fig. 7.3: InicArreglo.java
2 // Inicialización de los elementos de un arreglo con un inicializador de arreglo.
3
4 public class InicArreglo
5 {
6 public static void main( String args[] )
7 {
8 // la lista inicializadora especifica el valor para cada elemento
9 int arreglo[] = { 32, 27, 64, 18, 95, 14, 90, 70, 60, 37 };
10
11 System.out.printf( "%s%8sn", "Indice", "Valor" ); // encabezados de columnas
12
13 // imprime el valor del elemento de cada arreglo
14 for ( int contador = 0; contador < arreglo.length; contador++ )
15 System.out.printf( "%5d%8dn", contador, arreglo[ contador ] );
16 } // fin de main
17 } // fin de la clase InicArreglo
Figura 7.3 | Inicialización de los elementos de un arreglo con un inicializador de arreglo. (Parte 1 de 2).
Indice Valor
0 32
1 27
2 64
3 18
4 95
5 14
6 90
266 Capítulo 7 Arreglos
1 // Fig. 7.4: InicArreglo.java
2 // Cálculo de los valores a colocar en los elementos de un arreglo.
3
4 public class InicArreglo
5 {
6 public static void main( String args[] )
7 {
8 final int LONGITUD_ARREGLO = 10; // declara la constante
9 int arreglo[] = new int[ LONGITUD_ARREGLO ]; // crea el arreglo
10
11 // calcula el valor para cada elemento del arreglo
12 for ( int contador = 0; contador < arreglo.length; contador++ )
13 arreglo[ contador ] = 2 + 2 * contador;
14
15 System.out.printf( "%s%8sn", "Indice", "Valor" ); // encabezados de columnas
16
17 // imprime el valor de cada elemento del arreglo
18 for ( int contador = 0; contador < arreglo.length; contador++ )
19 System.out.printf( "%5d%8dn", contador, arreglo[ contador ] );
20 } // fin de main
21 } // fin de la clase InicArreglo
Figura 7.4 | Cálculo de los valores a colocar en los elementos de un arreglo.
Figura 7.3 | Inicialización de los elementos de un arreglo con un inicializador de arreglo. (Parte 2 de 2).
Cálculo de los valores a guardar en un arreglo
La aplicación de la figura 7.4 crea un arreglo de 10 elementos y asigna a cada elemento uno de los enteros pares
del 2 al 20 (2, 4, 6, …, 20). Después, la aplicación muestra el arreglo en formato tabular. La instrucción for en
las líneas 12 y 13 calcula el valor de un elemento del arreglo, multiplicando el valor actual de la variable de control
contador por 2, y después le suma 2.
La línea 8 utiliza el modificador final para declarar la variable constante LONGITUD_ARREGLO con el valor
10. Las variables constantes (también conocidas como variables final) deben inicializarse antes de usarlas, y no
pueden modificarse de ahí en adelante. Si trata de modificar una variable final después de inicializarla en su
declaración (como en la línea 8), el compilador genera el siguiente mensaje de error:
cannot assign a value to final variable nombreVariable
Si tratamos de acceder al valor de una variable final antes de inicializarla, el compilador produce el siguiente
mensaje de error
variable nombreVariable might not have been initialized
Indice Valor
0 2
1 4
2 6
3 8
4 10
5 12
6 14
7 16
8 18
9 20
7 70
8 60
9 37
Buena práctica de programación 7.2
Las variables constantes también se conocen como constantes con nombre o variables de sólo lectura. Con fre-
cuencia, dichas variables mejoran la legibilidad de un programa, en comparación con los programas que utilizan
valores literales (por ejemplo, 10); una constante con nombre como LONGITUD_ARREGLO indica sin duda su propósi-
to, mientras que un valor literal podría tener distintos significados, con base en el contexto en el que se utiliza.
Error común de programación 7.4
Asignar un valor a una constante después de inicializarla es un error de compilación.
Error común de programación 7.5
Tratar de usar una constante antes de inicializarla es un error de compilación.
Suma de los elementos de un arreglo
A menudo, los elementos de un arreglo representan una serie de valores que se emplearán en un cálculo.
Por ejemplo, si los elementos del arreglo representan las calificaciones de un examen, tal vez el profesor desee
sumar el total de los elementos del arreglo y utilizar esa suma para calcular el promedio de la clase para el exa-
men. Los ejemplos que utilizan la clase LibroCalificaciones más adelante en este capítulo, figura 7.14 y 7.18,
utilizan esta técnica.
La aplicación de la figura 7.5 suma los valores contenidos en el arreglo entero de 10 elementos. El programa
declara, crea e inicializa el arreglo en la línea 8. La instrucción for realiza los cálculos. [Nota: los valores suminis-
trados como inicializadores de arreglos generalmente se introducen en un programa, en vez de especificarse en
una lista inicializadora. Por ejemplo, una aplicación podría recibir los valores del usuario o de un archivo en disco
(como veremos en el capítulo 14, Archivos y flujos). Al hacer que los datos se introduzcan como entrada en el
programa éste se hace más flexible, ya que puede utilizarse con distintos conjuntos de datos].
Uso de gráficos de barra para mostrar los datos de un arreglo en forma gráfica
Muchas aplicaciones presentan datos a los usuarios en forma gráfica. Por ejemplo, con frecuencia los valores numé-
ricos se muestran como barras en un gráfico de barras. En dicho gráfico, las barras más largas representan valores
numéricos más grandes en forma proporcional. Una manera sencilla de mostrar los datos numéricos en forma
gráfica es mediante un gráfico de barras que muestre cada valor numérico como una barra de asteriscos (*).
7.4 Ejemplos acerca del uso de los arreglos 267
1 // Fig. 7.5: SumaArreglo.java
2 // Cálculo de la suma de los elementos de un arreglo.
3
4 public class SumaArreglo
5 {
6 public static void main( String args[] )
7 {
8 int arreglo[] = { 87, 68, 94, 100, 83, 78, 85, 91, 76, 87 };
9 int total = 0;
10
11 // suma el valor de cada elemento al total
12 for ( int contador = 0; contador < arreglo.length; contador++ )
13 total += arreglo[ contador ];
14
15 System.out.printf( "Total de los elementos del arreglo: %dn", total );
16 } // fin de main
17 } // fin de la clase SumaArreglo
Figura 7.5 | Cálculo de la suma de los elementos de un arreglo.
Total de los elementos del arreglo: 849
268 Capítulo 7 Arreglos
A los profesores les gusta examinar a menudo la distribución de las calificaciones en un examen. Un profesor
podría graficar el número de calificaciones en cada una de varias categorías, para visualizar la distribución de
las calificaciones. Suponga que las calificaciones en un examen fueron 87, 68, 94, 100, 83, 78, 85, 91, 76 y 87.
Observe que hubo una calificación de 100, dos calificaciones en el rango de 90 a 99, cuatro calificaciones en el
rango de 80 a 89, dos en el rango de 70 a 79, una en el rango de 60 a 69 y ninguna por debajo de 60. Nuestra
siguiente aplicación (figura 7.6) almacena estos datos de distribución de las calificaciones en un arreglo de 11 ele-
mentos, cada uno de los cuales corresponde a una categoría de calificaciones. Por ejemplo, arreglo[ 0 ] indica
el número de calificaciones en el rango de 0 a 9, arreglo[ 7 ] indica el número de calificaciones en el rango de
70 a 79 y arreglo[ 10 ] indica el número de calificaciones de 100. Las dos versiones de la clase LibroCali-
ficaciones que veremos más adelante en este capítulo (figuras 7.14 y 7.18) contienen código para calcular estas
frecuencias de calificaciones, con base en un conjunto de calificaciones. Por ahora crearemos el arreglo en forma
manual, mediante un análisis del conjunto de calificaciones.
1 // Fig. 7.6: GraficoBarras.java
2 // Programa para imprimir gráficos de barras.
3
4 public class GraficoBarras
5 {
6 public static void main( String args[] )
7 {
8 int arreglo[] = { 0, 0, 0, 0, 0, 0, 1, 2, 4, 2, 1 };
9
10 System.out.println( "Distribucion de calificaciones:" );
11
12 // para cada elemento del arreglo, imprime una barra del gráfico
13 for ( int contador = 0; contador < arreglo.length; contador++ )
14 {
15 // imprime etiqueta de la barra ( "00-09: ", ..., "90-99: ", "100: " )
16 if ( contador == 10 )
17 System.out.printf( "%5d: ", 100 );
18 else
19 System.out.printf( "%02d-%02d: ",
20 contador * 10, contador * 10 + 9 );
21
22 // imprime barra de asteriscos
23 for ( int estrellas = 0; estrellas < arreglo[ contador ]; estrellas++ )
24 System.out.print( "*" );
25
26 System.out.println(); // inicia una nueva línea de salida
27 } // fin de for externo
28 } // fin de main
29 } // fin de la clase GraficoBarras
Figura 7.6 | Programa para imprimir gráficos de barras.
Distribucion de calificaciones:
00-09:
10-19:
20-29:
30-39:
40-49:
50-59:
60-69: *
70-79: **
80-89: ****
90-99: **
100: *
La aplicación lee los números del arreglo y grafica la información en forma de un gráfico de barras. El progra-
ma muestra cada rango de calificaciones seguido de una barra de asteriscos, que indican el número de calificacio-
nes en ese rango. Para etiquetar cada barra, las líneas 16 a 20 imprimen un rango de calificaciones (por ejemplo,
"70-79: ") con base en el valor actual de contador. Cuando contador es 10, la línea 17 imprime 100 con una
anchura de campo de 5, seguida de dos puntos y un espacio, para alinear la etiqueta "100: " con las otras etique-
tas de las barras. La instrucción for anidada (líneas 23 y 24) imprime las barras en pantalla. Observe la condición
de continuación de ciclo en la línea 23 (estrellas < arreglo[ contador ]). Cada vez que el programa llega
al for interno, el ciclo cuenta desde 0 hasta arreglo[ contador ], con lo cual utiliza un valor en arreglo para
determinar el número de asteriscos a mostrar en pantalla. En este ejemplo, los valores de arreglo[ 0 ] hasta
arreglo[ 5 ] son 0, ya que ningún estudiante recibió una calificación menor de 60. Por ende, el programa no
muestra asteriscos enseguida de los primeros seis rangos de calificaciones. Observe que la línea 19 utiliza el espe-
cificador de formato %02d para imprimir los números en un rango de calificaciones. Este especificador indica que
se debe dar formato a un valor int como un campo de dos dígitos. La bandera 0 en el especificador de formato
indica que los valores con menos dígitos que la anchura de campo (2) deben empezar con un 0 a la izquierda.
Uso de los elementos de un arreglo como contadores
En ocasiones, los programas utilizan variables tipo contador para sintetizar datos, como los resultados de una
encuesta. En la figura 6.8 utilizamos contadores separados en nuestro programa para tirar dados, para rastrear el
número de veces que aparecía cada una de las caras de un dado con seis lados, al tiempo que la aplicación tiraba
el dado 6000 veces. En la figura 7.7 se muestra una versión de la aplicación de la figura 6.8, esta vez usando un
arreglo.
7.4 Ejemplos acerca del uso de los arreglos 269
1 // Fig. 7.7: TirarDado.java
2 // Tira un dado de seis lados 6000 veces.
3 import java.util.Random;
4
5 public class TirarDado
6 {
7 public static void main( String args[] )
8 {
9 Random numerosAleatorios = new Random(); // generador de números aleatorios
10 int frecuencia[] = new int[ 7 ]; // arreglo de contadores de frecuencia
11
12 // tira el dado 6000 veces; usa el valor del dado como índice de frecuencia
13 for ( int tiro = 1; tiro <= 6000; tiro++ )
14 ++frecuencia[ 1 + numerosAleatorios.nextInt( 6 ) ];
15
16 System.out.printf( "%s%10sn", "Cara", "Frecuencia" );
17
18 // imprime el valor de cada elemento del arreglo
19 for ( int cara = 1; cara < frecuencia.length; cara++ )
20 System.out.printf( "%4d%10dn", cara, frecuencia[ cara ] );
21 } // fin de main
22 } // fin de la clase TirarDado
Figura 7.7 | Programa para tirar dados que utiliza arreglos en vez de switch.
Cara Frecuencia
1 1015
2 999
3 998
4 996
5 1044
6 948
270 Capítulo 7 Arreglos
La figura 7.7 utiliza el arreglo frecuencia (línea 10) para contar las ocurrencias de cada lado del dado.
La instrucción individual en la línea 14 de este programa sustituye las líneas 23 a 46 de la figura 6.8. La línea 14
utiliza el valor aleatorio para determinar qué elemento de frecuencia debe incrementar durante cada iteración
del ciclo. El cálculo en la línea 14 produce números aleatorios del 1 al 6, por lo que el arreglo frecuencia debe
ser lo bastante grande como para poder almacenar seis contadores. Sin embargo, utilizamos un arreglo de siete
elementos, en el cual ignoramos frecuencia[ 0 ]; es más lógico que el valor de cara 1 incremente a fre-
cuencia[ 1 ] que a frecuencia[ 0 ]. Por ende, cada valor de cara se utiliza como subíndice para el arreglo
frecuencia. También sustituimos las líneas 50 a 52 de la figura 6.8 por un ciclo a través del arreglo frecuencia
para imprimir los resultados en pantalla (líneas 19 a 20).
Uso de arreglos para analizar los resultados de una encuesta
Nuestro siguiente ejemplo utiliza arreglos para sintetizar los resultados de los datos recolectados en una encuesta:
Se pidió a cuarenta estudiantes que calificaran la calidad de la comida en la cafetería estudiantil, en
una escala del 1 al 10 (en donde 1 significa pésimo y 10 significa excelente). Coloque las 40 respuestas
en un arreglo entero y sintetice los resultados de la encuesta.
Ésta es una típica aplicación de procesamiento de arreglos (vea la figura 7.8). Deseamos resumir el número de
respuestas de cada tipo (es decir, del 1 al 10). El arreglo respuestas (líneas 9 a 11) es un arreglo entero de 40
elementos, y contiene las respuestas de los estudiantes a la encuesta. Utilizamos un arreglo de 11 elementos lla-
mado frecuencia (línea 12) para contar el número de ocurrencias de cada respuesta. Cada elemento del arreglo
se utiliza como un contador para una de las respuestas de la encuesta, y se inicializa con cero de manera predeter-
minada. Al igual que en la figura 7.7, ignoramos frecuencia[ 0 ].
El ciclo for en las líneas 16 y 17 recibe las respuestas del arreglo respuestas una a la vez, e incrementa uno
de los 10 contadores en el arreglo frecuencia (de frecuencia[ 1 ] a frecuencia[ 10 ]). La instrucción
clave en el ciclo es la línea 17, la cual incrementa el contador de frecuencia apropiado, dependiendo del valor
de respuestas[ respuesta ].
Consideraremos varias iteraciones del ciclo for. Cuando la variable de control respuesta es 0, el valor de
respuestas[ respuesta ] es el valor de respuestas[ 0 ] (es decir, 1), por lo que el programa interpreta a
++frecuencia[ respuestas[ respuesta ] ] como
++frecuencia[ 1 ]
con lo cual se incrementa el valor en el elemento 1 del arreglo. Para evaluar la expresión, empiece con el valor en
el conjunto más interno de corchetes (respuesta). Una vez que conozca el valor de respuesta (que es el valor de
la variable de control de ciclo en la línea 16), insértelo en la expresión y evalúe el siguiente conjunto más externo
de corchetes (respuestas[ respuesta ], que es un valor seleccionado del arreglo respuestas en las líneas 9
a 11). Después utilice el valor resultante como subíndice del arreglo frecuencia, para especificar cuál contador
se incrementará.
Cuando respuesta es 1, respuestas[ respuesta ] es el valor de respuestas[ 1 ] (2), por lo que el
programa interpreta a ++frecuencia[ respuestas[ respuesta ] ] como
++frecuencia[ 2 ]
con lo cual se incrementa el elemento 2 del arreglo.
Cuando respuesta es 2, respuestas[ respuesta ] es el valor de respuestas[ 2 ] (6), por lo que el
programa interpreta a ++frecuencia[ respuestas [ respuesta ] ] como
++frecuencia[ 6 ]
con lo cual se incrementa el elemento 6 del arreglo, y así en lo sucesivo. Sin importar el número de respuestas
procesadas en la encuesta, el programa sólo requiere un arreglo de 11 elementos (en el cual se ignora el elemento
cero) para resumir los resultados, ya que todos los valores de las respuestas se encuentran entre 1 y 10, y los valores
de subíndice para un arreglo de 11 elementos son del 0 al 10.
Si los datos en el arreglo respuestas tuvieran valores inválidos como 13, el programa trataría de sumar
1 a frecuencia[ 13 ], lo cual se encuentra fuera de los límites del arreglo. Java no permite esto. Cuando se
ejecuta un programa en Java, la JVM comprueba los subíndices del arreglo para asegurarse que sean válidos (es
decir, deben ser mayores o iguales a 0 y menores que la longitud del arreglo). Si un programa utiliza un subíndice
inválido, Java genera una excepción para indicar que se produjo un error en el programa, en tiempo de ejecución.
Puede utilizarse una instrucción de control para evitar que ocurra un error tipo “fuera de los límites”. Por ejemplo,
la condición en una instrucción de control podría determinar si un subíndice es válido antes de permitir que se
utilice en una expresión de acceso a un arreglo.
Tip para prevenir errores 7.1
Una excepción indica que ocurrió un error en un programa. A menudo el programador puede escribir código para
recuperarse de una excepción y continuar con la ejecución del programa, en vez de terminarlo en forma anormal.
Cuando un programa trata de acceder a un elemento fuera de los límites del arreglo, se produce una excepción Array-
IndexOutOfBoundsException. En el capítulo 13 hablaremos sobre el manejo de excepciones.
Tip para prevenir errores 7.2
Al escribir código para iterar a través de un arreglo, hay que asegurar que el subíndice del arreglo siempre sea mayor o
igual a 0 y menor que la longitud del arreglo. La condición de continuación de ciclo debe evitar el acceso a elementos
fuera de este rango.
7.4 Ejemplos acerca del uso de los arreglos 271
1 // Fig. 7.8: EncuestaEstudiantes.java
2 // Programa de análisis de una encuesta.
3
4 public class EncuestaEstudiantes
5 {
6 public static void main( String args[] )
7 {
8 // arreglo de respuestas a una encuesta
9 int respuestas[] = { 1, 2, 6, 4, 8, 5, 9, 7, 8, 10, 1, 6, 3, 8, 6,
10 10, 3, 8, 2, 7, 6, 5, 7, 6, 8, 6, 7, 5, 6, 6, 5, 6, 7, 5, 6,
11 4, 8, 6, 8, 10 };
12 int frecuencia[] = new int[ 11 ]; // arreglo de contadores de frecuencia
13
14 // para cada respuesta, selecciona el elemento de respuestas y usa ese valor
15 // como índice de frecuencia para determinar el elemento a incrementar
16 for ( int respuesta = 0; respuesta < respuestas.length; respuesta++ )
17 ++frecuencia[ respuestas[ respuesta ] ];
18
19 System.out.printf( "%s%10sn", "Calificacion", "Frecuencia" );
20
21 // imprime el valor de cada elemento del arreglo
22 for ( int calificacion = 1; calificacion < frecuencia.length; calificacion++ )
23 System.out.printf( "%6d%10dn", calificacion, frecuencia[ calificacion ] );
24 } // fin de main
25 } // fin de la clase EncuestaEstudiantes
Figura 7.8 | Programa de análisis de una encuesta.
Calificacion Frecuencia
1 2
2 2
3 2
4 2
5 5
6 11
7 5
8 7
9 1
10 3
272 Capítulo 7 Arreglos
7.5 Ejemplo práctico: simulación para barajar y repartir cartas
Hasta ahora, en los ejemplos en este capítulo hemos utilizado arreglos que contienen elementos de tipos pri-
mitivos. En la sección 7.2 vimos que los elementos de un arreglo pueden ser de tipos primitivos o de tipos por
referencia. En esta sección utilizaremos la generación de números aleatorios y un arreglo de elementos de tipo
por referencia (a saber, objetos que representan cartas de juego) para desarrollar una clase que simule los procesos
de barajar y repartir cartas. Después podremos utilizar esta clase para implementar aplicaciones que ejecuten jue-
gos específicos de cartas. Los ejercicios al final del capítulo utilizan las clases que desarrollaremos aquí para crear
una aplicación simple de póquer.
Primero desarrollaremos la clase Carta (figura 7.9), la cual representa una carta de juego que tiene una cara
("As", "Dos", "Tres", …, "Joto", "Qüina", "Rey") y un palo ("Corazones", "Diamantes", "Tréboles",
"Espadas"). Después desarrollaremos la clase PaqueteDeCartas (figura 7.10), la cual crea un paquete de 52
cartas en las que cada elemento es un objeto Carta. Luego construiremos una aplicación de prueba (figura 7.11)
para demostrar las capacidades de barajar y repartir cartas de la clase PaqueteDeCartas.
La clase Carta
La clase Carta (figura 7.9) contiene dos variables de instancia String (cara y palo) que se utilizan para almace-
nar referencias al valor de la cara y al valor del palo para una Carta específica. El constructor de la clase (líneas 10
a 14) recibe dos objetos String que utiliza para inicializar cara y palo. El método toString (líneas 17 a 20) crea
un objeto String que consiste en la cara de la carta, el objeto String "de" y el palo de la carta. En el capítulo 6
vimos que el operador + puede utilizarse para concatenar (es decir, combinar) varios objetos String para formar
un objeto String más grande. El método toString de Carta puede invocarse en forma explícita para obtener
la representación de cadena de un objeto Carta (por ejemplo, "As de Espadas"). El método toString de un
objeto se llama en forma implícita cuando el objeto se utiliza en donde se espera un objeto String (por ejemplo,
cuando printf imprime en pantalla el objeto como un String, usando el especificador de formato %s, o cuando
el objeto se concatena con un objeto String mediante el operador +). Para que ocurra este comportamiento,
toString debe declararse con el encabezado que se muestra en la figura 7.9.
La clase PaqueteDeCartas
La clase PaqueteDeCartas (figura 7.10) declara un arreglo de variables de instancia llamado paquete, el cual
contiene objetos Carta (línea 7). Al igual que las declaraciones de arreglos de tipos primitivos, la declaración de
un arreglo de objetos incluye el tipo de los elementos en el arreglo, seguido del nombre de la variable del arreglo y
1 // Fig. 7.9: Carta.java
2 // La clase Carta representa una carta de juego.
3
4 public class Carta
5 {
6 private String cara; // cara de la carta ("As", "Dos", ...)
7 private String palo; // palo de la carta ("Corazones", "Diamantes", ...)
8
9 // el constructor de dos argumentos inicializa la cara y el palo de la carta
10 public Carta( String caraCarta, String paloCarta )
11 {
12 cara = caraCarta; // inicializa la cara de la carta
13 palo = paloCarta; // inicializa el palo de la carta
14 } // fin del constructor de Carta con dos argumentos
15
16 // devuelve representación String de Carta
17 public String toString()
18 {
19 return cara + " de " + palo;
20 } // fin del método toString
21 } // fin de la clase Carta
Figura 7.9 | La clase Carta representa una carta de juego.
7.5 Ejemplo práctico: simulación para barajar y repartir cartas 273
1 // Fig. 7.10: PaqueteDeCartas.java
2 // La clase PaqueteDeCartas representa un paquete de cartas de juego.
3 import java.util.Random;
4
5 public class PaqueteDeCartas
6 {
7 private Carta paquete[]; // arreglo de objetos Carta
8 private int cartaActual; // subíndice de la siguiente Carta a repartir
9 private final int NUMERO_DE_CARTAS = 52; // número constante de Cartas
10 private Random numerosAleatorios; // generador de números aleatorios
11
12 // el constructor llena el paquete de Cartas
13 public PaqueteDeCartas()
14 {
15 String caras[] = { "As", "Dos", "Tres", "Cuatro", "Cinco", "Seis",
16 "Siete", "Ocho", "Nueve", "Diez", "Joto", "Quina", "Rey" };
17 String palos[] = { "Corazones", "Diamantes", "Treboles", "Espadas" };
18
19 paquete = new Carta[ NUMERO_DE_CARTAS ]; // crea arreglo de objetos Carta
20 cartaActual = 0; // establece cartaActual para que la primera Carta repartida sea
paquete[ 0 ]
21 numerosAleatorios = new Random(); // crea generador de números aleatorios
22
23 // llena el paquete con objetos Carta
24 for ( int cuenta = 0; cuenta < paquete.length; cuenta++ )
25 paquete[ cuenta ] =
26 new Carta( caras[ cuenta % 13 ], palos[ cuenta / 13 ] );
27 } // fin del constructor de PaqueteDeCartas
28
29 // baraja el paquete de Cartas con algoritmo de una pasada
30 public void barajar()
31 {
32 // después de barajar, la repartición debe empezar en paquete[ 0 ] otra vez
33 cartaActual = 0; // reinicializa cartaActual
34
35 // para cada Carta, selecciona otra Carta aleatoria y las intercambia
36 for ( int primera = 0; primera < paquete.length; primera++ )
37 {
38 // selecciona un número aleatorio entre 0 y 51
39 int segunda = numerosAleatorios.nextInt( NUMERO_DE_CARTAS );
40
41 // intercambia Carta actual con la Carta seleccionada al azar
42 Carta temp = paquete[ primera ];
43 paquete[ primera ] = paquete[ segunda ];
44 paquete[ segunda ] = temp;
45 } // fin de for
46 } // fin de método barajar
47
48 // reparte una Carta
49 public Carta repartirCarta()
50 {
51 // determina si quedan Cartas por repartir
52 if ( cartaActual < paquete.length )
53 return paquete[ cartaActual++ ]; // devuelve la Carta actual en el arreglo
54 else
55 return null; // devuelve null para indicar que se repartieron todas las Cartas
56 } // fin del método repartirCarta
57 } // fin de la clase PaqueteDeCartas
Figura 7.10 | La clase PaqueteDeCartas representa un paquete de cartas de juego, que pueden barajarse y repartirse,
una a la vez.
274 Capítulo 7 Arreglos
de corchetes (por ejemplo Carta paquete[ ]). La clase PaqueteDeCartas también declara la variable de instan-
cia entera llamada cartaActual (línea 8), que representa la siguiente Carta a repartir del arreglo paquete, y la
constante con nombre NUMERO_DE_CARTAS (línea 9), que indica el número de objetos Carta en el paquete (52).
El constructor de la clase crea una instancia del arreglo paquete (línea 19) con un tamaño igual a NUME-
RO_DE_CARTAS. Cuando se crea por primera vez el arreglo paquete, sus elementos son null de manera prede-
terminada, por lo que el constructor utiliza una instrucción for (líneas 24 a 26) para llenar el arreglo paquetes
con objetos Carta. La instrucción for inicializa la variable de control cuenta con 0 e itera mientras cuenta sea
menor que paquete.length, lo cual hace que cuenta tome el valor de cada entero del 0 al 51 (los subíndices del
arreglo paquete). Cada objeto Carta se instancia y se inicializa con dos objetos String: uno del arreglo caras
(que contiene los objetos String del "As" hasta el "Rey") y uno del arreglo palos (que contiene los objetos
String "Corazones", "Diamantes", "Treboles" y "Espadas"). El cálculo cuenta % 13 siempre produce
un valor de 0 a 12 (los 13 subíndices del arreglo caras en las líneas 15 y 16), y el cálculo cuenta / 13 siempre
produce un valor de 0 a 3 (los cuatro subíndices del arreglo palos en la línea 17). Cuando se inicializa el arreglo
paquete, contiene los objetos Carta con las caras del "As" al "Rey" en orden para cada palo ("Corazones",
"Diamantes", "Treboles", "Espadas").
El método barajar (líneas 30 a 46) baraja los objetos Carta en el paquete. El método itera a través de los 52
objetos Carta (subíndices 0 a 51 del arreglo). Para cada objeto Carta se elige al azar un número entre 0 y 51 para
elegir otro objeto Carta. A continuación, el objeto Carta actual y el objeto Carta seleccionado al azar se inter-
cambian en el arreglo. Este intercambio se realiza mediante las tres asignaciones en las líneas 42 a 44. La variable
extra temp almacena en forma temporal uno de los dos objetos Carta que se van a intercambiar. El intercambio
no se puede realizar sólo con las dos instrucciones
paquete[ primera ] = paquete[ segunda ];
paquete[ segunda ] = paquete[ primera ];
Si paquete[ primera ] es el "As" de "Espadas" y paquete[ segunda ] es la "Quina" de "Corazones",
entonces después de la primera asignación, ambos elementos del arreglo contienen la "Quina" de "Corazones"
y se pierde el "As" de "Espadas"; es por ello que se necesita la variable extra temp. Una vez que termina el ciclo
for, los objetos Carta se ordenan al azar. Sólo se realizan 52 intercambios en una sola pasada del arreglo comple-
to, ¡y el arreglo de objetos Carta se baraja!
El método repartirCarta (líneas 49 a 56) reparte un objeto Carta en el arreglo. Recuerde que cartaAc-
tual indica el subíndice del siguiente objeto Carta que se repartirá (es decir, la Carta en la parte superior del
paquete). Por ende, la línea 52 compara cartaActual con la longitud del arreglo paquete. Si el paquete no está
vacío (es decir, si cartaActual es menor a 52), la línea 53 regresa el objeto Carta “superior” y postincrementa
cartaActual para prepararse para la siguiente llamada a repartirCarta; en caso contrario, se devuelve null.
En el capítulo 3 vimos que null representa una “referencia a nada”.
Barajar y repartir cartas
La aplicación de la figura 7.11 demuestra las capacidades de barajar y repartir cartas de la clase PaqueteDeCar-
tas (figura 7.10). La línea 9 crea un objeto PaqueteDeCartas llamado miPaqueteDeCartas. Recuerde que el
constructor PaqueteDeCartas crea el paquete con los 52 objetos Carta, en orden por palo y por cara. La línea
10 invoca el método barajar de miPaqueteDeCartas para reordenar los objetos Carta. La instrucción for en
las líneas 13 a 19 reparte los 52 objetos Carta en el paquete y los imprime en cuatro columnas, cada una con 13
objetos Carta. Las líneas 16 a 18 reparten e imprimen en pantalla cuatro objetos Carta, cada uno de los cuales
se obtiene mediante la invocación al método repartirCarta de miPaqueteDeCartas. Cuando printf imprime
en pantalla un objeto Carta con el especificador de formato %-20s, el método toString de Carta (declarado en
las líneas 17 a 20 de la figura 7.9) se invoca en forma implícita, y el resultado se imprime justificado a la izquierda,
en un campo con una anchura de 20.
7.6 Instrucción for mejorada
En ejemplos anteriores demostramos cómo utilizar las instrucciones for controladas por un contador para iterar
a través de los elementos en un arreglo. En esta sección presentaremos la instrucción for mejorada, la cual
itera a través de los elementos de un arreglo o colección sin utilizar un contador (con lo cual, evita la posibilidad
de “salirse” del arreglo). Esta sección habla acerca de cómo utilizar la instrucción for mejorada para iterar a tra-
vés de un arreglo. En el capítulo 19, Colecciones, veremos cómo utilizar la instrucción for mejorada con colec-
ciones. La sintaxis de una instrucción for mejorada es:
for ( parámetro : nombreArreglo )
instrucción
en donde parámetro tiene dos partes: un tipo y un identificador (por ejemplo, int numero), y nombreArreglo es el
arreglo a través del cual se iterará. El tipo del parámetro debe concordar con el tipo de los elementos en el arreglo.
Como se muestra en el siguiente ejemplo, el identificador representa valores sucesivos en el arreglo, en iteraciones
sucesivas de la instrucción for mejorada.
La figura 7.12 utiliza la instrucción for mejorada (líneas 12 y 13) para calcular la suma de los enteros en un
arreglo de calificaciones de estudiantes. El tipo especificado en el parámetro para el for mejorado es int, ya que
arreglo contiene valores int; el ciclo selecciona un valor int del arreglo durante cada iteración. La instrucción
for mejorada itera a través de valores sucesivos en el arreglo, uno por uno. El encabezado del for mejorado se
puede leer como “para cada iteración, asignar el siguiente elemento de arreglo a la variable int numero, después
ejecutar la siguiente instrucción”. Por lo tanto, para cada iteración, el identificador numero representa un valor
int en arreglo. Las líneas 12 y 13 son equivalentes a la siguiente repetición controlada por un contador que se
utiliza en las líneas 12 y 13 de la figura 7.5, para totalizar los enteros en el arreglo:
for ( int contador = 0; contador < arreglo.length; contador++ )
total += arreglo[ contador ];
7.6 Instruccion for mejorada 275
1 // Fig. 7.11: PruebaPaqueteDeCartas.java
2 // Aplicación para barajar y repartir cartas.
3
4 public class PruebaPaqueteDeCartas
5 {
6 // ejecuta la aplicación
7 public static void main( String args[] )
8 {
9 PaqueteDeCartas miPaqueteDeCartas = new PaqueteDeCartas();
10 miPaqueteDeCartas.barajar(); // coloca las Cartas en orden aleatorio
11
12 // imprime las 52 Cartas en el orden en el que se reparten
13 for ( int i = 0; i < 13; i++ )
14 {
15 // reparte e imprime 4 Cartas
16 System.out.printf( "%-20s%-20s%-20s%-20sn",
17 miPaqueteDeCartas.repartirCarta(), miPaqueteDeCartas.repartirCarta(),
18 miPaqueteDeCartas.repartirCarta(), miPaqueteDeCartas.repartirCarta() );
19 } // fin de for
20 } // fin de main
21 } // fin de la clase PruebaPaqueteDeCartas
Figura 7.11 | Aplicación para barajar y repartir cartas.
Nueve de Espadas Joto de Corazones Quina de Treboles Siete de Treboles
Seis de Corazones Seis de Treboles Joto de Diamantes Tres de Diamantes
Diez de Diamantes Cinco de Diamantes As de Treboles Rey de Diamantes
Siete de Corazones Cuatro de Corazones Cuatro de Espadas Nueve de Corazones
Dos de Espadas Quina de Diamantes Dos de Corazones Quina de Corazones
As de Corazones Cuatro de Treboles Cinco de Espadas Joto de Treboles
Cinco de Treboles Siete de Diamantes As de Espadas Ocho de Espadas
Nueve de Treboles Cuatro de Diamantes Siete de Espadas Rey de Corazones
Quina de Espadas Dos de Diamantes Rey de Treboles Diez de Corazones
Cinco de Corazones As de Diamantes Rey de Espadas Joto de Espadas
Ocho de Diamantes Tres de Espadas Ocho de Treboles Seis de Diamantes
Nueve de Diamantes Tres de Treboles Diez de Treboles Dos de Treboles
Tres de Corazones Ocho de Corazones Diez de Espadas Seis de Espadas
276 Capítulo 7 Arreglos
La instrucción for mejorada simplifica el código para iterar a través de un arreglo. No obstante, observe
que la instrucción for mejorada sólo puede utilizarse para obtener elementos del arreglo; no puede utilizarse
para modificar los elementos. Si su programa necesita modificar elementos, use la instrucción for tradicional,
controlada por contador.
La instrucción for mejorada se puede utilizar en lugar de la instrucción for controlada por contador, cuan-
do el código que itera a través de un arreglo no requiere acceso al contador que indica el subíndice del elemento
actual del arreglo. Por ejemplo, para totalizar los enteros en un arreglo se requiere acceso sólo a los valores de los
elementos; el subíndice de cada elemento es irrelevante. No obstante, si un programa debe utilizar un contador
por alguna razón que no sea tan sólo iterar a través de un arreglo (por ejemplo, imprimir un número de subíndice
al lado del valor de cada elemento del arreglo, como en los primeros ejemplos en este capítulo), use la instrucción
for controlada por contador.
1 // Fig. 7.12: PruebaForMejorado.java
2 // Uso de la instrucción for mejorada para sumar el total de enteros en un arreglo.
3
4 public class PruebaForMejorado
5 {
6 public static void main( String args[] )
7 {
8 int arreglo[] = { 87, 68, 94, 100, 83, 78, 85, 91, 76, 87 };
9 int total = 0;
10
11 // suma el valor de cada elemento al total
12 for ( int numero : arreglo )
13 total += numero;
14
15 System.out.printf( "Total de elementos del arreglo: %dn", total );
16 } // fin de main
17 } // fin de la clase PruebaForMejorado
Figura 7.12 | Uso de la instrucción for mejorada para sumar el total de los enteros en un arreglo.
Total de elementos del arreglo: 849
7.7 Paso de arreglos a los métodos
Esta sección demuestra cómo pasar arreglos y elementos individuales de un arreglo como argumentos para los
métodos. Al final de la sección, hablaremos acerca de cómo se pasan todos los tipos de argumentos a los métodos.
Para pasar un argumento tipo arreglo a un método, se especifica el nombre del arreglo sin corchetes. Por ejemplo,
si el arreglo temperaturasPorHora se declara como
double temperaturasPorHora[ ] = new double[ 24 ];
entonces la llamada al método
modificarArreglo( temperaturasPorHora );
pasa la referencia del arreglo temperaturasPorHora al método modificarArreglo. Todo objeto arreglo “conoce”
su propia longitud (a través de su campo length). Por ende, cuando pasamos a un método la referencia a un
objeto arreglo, no necesitamos pasar la longitud del arreglo como un argumento adicional.
Para que un método reciba una referencia a un arreglo a través de una llamada a un método, la lista de
parámetros del método debe especificar un parámetro tipo arreglo. Por ejemplo, el encabezado para el método
modificarArreglo podría escribirse así:
void modificarArreglo( int b[] )
lo cual indica que modificarArreglo recibe la referencia de un arreglo de enteros en el parámetro b. La llamada a
este método pasa la referencia al arreglo temperaturaPorHoras, de manera que cuando el método llamado utili-
za la variable b tipo arreglo, hace referencia al mismo objeto arreglo como temperaturasPorHora en el método
que hizo la llamada.
Cuando un argumento para un método es todo un arreglo, o un elemento individual de un arreglo de un
tipo por referencia, el método llamado recibe una copia de la referencia. Sin embargo, cuando un argumento
para un método es un elemento individual de un arreglo de un tipo primitivo, el método llamado recibe una
copia del valor del elemento. Dichos valores primitivos se conocen como escalares o cantidades escalares. Para
pasar un elemento individual de un arreglo a un método, use el nombre indexado del elemento del arreglo como
argumento en la llamada al método.
La figura 7.13 demuestra la diferencia entre pasar a un método todo un arreglo y pasar un elemento de
un arreglo de tipo primitivo. La instrucción for mejorada en las líneas 16 y 17 imprime en pantalla los cinco
elementos de arreglo (un arreglo de valores int). La línea 19 invoca al método modificarArreglo y le pa-
sa arreglo como argumento. El método modificarArreglo (líneas 36 a 40) recibe una copia de la referencia
a arreglo y utiliza esta referencia para multiplicar cada uno de los elementos de arreglo por 2. Para demostrar
que se modificaron los elementos de arreglo, la instrucción for en las líneas 23 y 24 imprime en pantalla los
cinco elementos de arreglo otra vez. Como se muestra en la salida, el método modificarArreglo duplicó el valor
de cada elemento. Observe que no pudimos usar la instrucción for mejorada en las líneas 38 y 39, ya que estamos
modificando los elementos del arreglo.
7.7 Paso de arreglos a los métodos 277
1 // Fig. 7.13: PasoArreglo.java
2 // Paso de arreglos y elementos individuales de un arreglo a los métodos.
3
4 public class PasoArreglo
5 {
6 // main crea el arreglo y llama a modificarArreglo y a modificarElemento
7 public static void main( String args[] )
8 {
9 int arreglo[] = { 1, 2, 3, 4, 5 };
10
11 System.out.println(
12 "Efectos de pasar una referencia a un arreglo completo:n" +
13 "Los valores del arreglo original son:" );
14
15 // imprime los elementos originales del arreglo
16 for ( int valor : arreglo )
17 System.out.printf( " %d", valor );
18
19 modificarArreglo( arreglo ); // pasa la referencia al arreglo
20 System.out.println( "nnLos valores del arreglo modificado son:" );
21
22 // imprime los elementos modificados del arreglo
23 for ( int valor : arreglo )
24 System.out.printf( " %d", valor );
25
26 System.out.printf(
27 "nnEfectos de pasar el valor de un elemento del arreglo:n" +
28 "arreglo[3] antes de modificarElemento: %dn”, arreglo[ 3 ] );
29
30 modificarElemento( arreglo[ 3 ] ); // intento por modificar arreglo[ 3 ]
31 System.out.printf(
32 "arreglo[3] despues de modificarElemento: %dn", arreglo[ 3 ] );
33 } // fin de main
34
35 // multiplica cada elemento de un arreglo por 2
Figura 7.13 | Paso de arreglos y elementos individuales de un arreglo a los métodos. (Parte 1 de 2).
278 Capítulo 7 Arreglos
Figura 7.13 | Paso de arreglos y elementos individuales de un arreglo a los métodos. (Parte 2 de 2).
Efectos de pasar una referencia a un arreglo completo:
Los valores del arreglo original son:
1 2 3 4 5
Los valores del arreglo modificado son:
2 4 6 8 10
Efectos de pasar el valor de un elemento del arreglo:
arreglo[3] antes de modificarElemento: 8
Valor del elemento en modificarElemento: 16
arreglo[3] despues de modificarElemento: 8
36 public static void modificarArreglo( int arreglo2[] )
37 {
38 for ( int contador = 0; contador < arreglo2.length; contador++ )
39 arreglo2[ contador ] *= 2;
40 } // fin del método modificarArreglo
41
42 // multiplica el argumento por 2
43 public static void modificarElemento( int elemento )
44 {
45 elemento *= 2;
46 System.out.printf(
47 "Valor del elemento en modificarElemento: %dn", elemento );
48 } // fin del método modificarElemento
49 } // fin de la clase PasoArreglo
La figura 7.13 demuestra a continuación que, cuando se pasa una copia de un elemento individual de un
arreglo de tipo primitivo a un método, si se modifica la copia en el método que se llamó, el valor original de ese
elemento no se ve afectado en el arreglo del método que hizo la llamada. Las líneas 26 a 28 imprimen en panta-
lla el valor de arreglo[ 3 ] (8) antes de invocar al método modificarElemento. La línea 30 llama al método
modificarElemento y le pasa arreglo[ 3 ] como argumento. Recuerde que arreglo[ 3 ] es en realidad
un valor int (8) en arreglo. Por lo tanto, el programa pasa una copia del valor de arreglo[ 3 ]. El método
modificarElemento (líneas 43 a 48) multiplica el valor recibido como argumento por 2, almacena el resultado en
su parámetro elemento y después imprime en pantalla el valor de elemento (16). Como los parámetros de los
métodos, al igual que las variables locales, dejan de existir cuando el método en el que se declaran termina su
ejecución, el parámetro elemento del método se destruye cuando modificarElemento termina. Por lo tanto,
cuando el programa devuelve el control a main, las líneas 31 y 32 imprimen en pantalla el valor de arreglo[ 3 ]
que no se modificó (es decir, 8).
Notas acerca del paso de argumentos a los métodos
El ejemplo anterior demostró las distintas maneras en las que se pasan los arreglos y los elementos de arreglos
de tipos primitivos a los métodos. Ahora veremos con más detalle la forma en que se pasan los argumentos a
los métodos en general. En muchos lenguajes de programación, dos formas de pasar argumentos en las llamadas
a métodos son el paso por valor y el paso por referencia (también conocidas como llamada por valor y llama-
da por referencia). Cuando se pasa un argumento por valor, se pasa una copia del valor del argumento al método
que se llamó. Este método trabaja exclusivamente con la copia. Las modificaciones a la copia del método que se
llamó no afectan el valor de la variable original en el método que hizo la llamada.
Cuando se pasa un argumento por referencia, el método que se llamó puede acceder al valor del argumento
en el método que hizo la llamada directamente, y puede modificar esos datos si es necesario. El paso por referencia
mejora el rendimiento, al eliminar la necesidad de copiar cantidades de datos posiblemente extensas.
A diferencia de otros lenguajes, Java no permite a los programadores elegir el paso por valor o el paso por
referencia; todos los argumentos se pasan por valor. Una llamada a un método puede pasar dos tipos de valores:
copias de valores primitivos (como valores de tipo int y double) y copias de referencias a objetos (incluyendo las
referencias a arreglos). Los objetos en sí no pueden pasarse a los métodos. Cuando un método modifica un pará-
metro de tipo primitivo, las modificaciones a ese parámetro no tienen efecto en el valor original del argumento en
el método que hizo la llamada. Por ejemplo, cuando la línea 30 en main de la figura 7.13 pasa arreglo[ 3 ] al
método modificarElemento, la instrucción en la línea 45 que duplica el valor del parámetro elemento no tiene
efecto sobre el valor de arreglo[ 3 ] en main. Esto también se aplica para los parámetros de tipo por referen-
cia. Si usted modifica un parámetro de tipo por referencia al asignarle la referencia de otro objeto, el parámetro
hace referencia al nuevo objeto, pero la referencia almacenada en la variable del método que hizo la llamada sigue
haciendo referencia al objeto original.
Aunque la referencia a un objeto se pasa por valor, un método puede de todas formas interactuar con el obje-
to al que se hace referencia, llamando a sus métodos public mediante el uso de la copia de la referencia al objeto.
Como la referencia almacenada en el parámetro es una copia de la referencia que se pasó como argumento, el
parámetro en el método que se llamó y el argumento en el método que hizo la llamada hacen referencia al mismo
objeto en la memoria. Por ejemplo, en la figura 7.13, tanto el parámetro arreglo2 en el método modificarArre
glo como la variable arreglo en main hacen referencia al mismo objeto en la memoria. Cualquier modificación
que se realice usando el parámetro arreglo2 se lleva a cabo en el mismo objeto al que hace referencia la varia-
ble que se pasó como argumento en el método que hizo la llamada. En la figura 7.13, las modificaciones realizadas
en modificarArreglo en las que se utiliza arreglo2, afectan al contenido del objeto arreglo al que hace referencia
arreglo en main. De esta forma, con una referencia a un objeto, el método que se llamó puede manipular el
objeto del método que hizo la llamada directamente.
Tip de rendimiento 7.1
Pasar arreglos por referencia tiene sentido por cuestiones de rendimiento. Si los arreglos se pasaran por valor, se
pasaría una copia de cada elemento. En los arreglos grandes que se pasan con frecuencia, esto desperdiciaría tiempo
y consumiría una cantidad considerable de almacenamiento para las copias de los arreglos.
7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un
arreglo para almacenar las calificaciones
En esta sección desarrollaremos aún más la clase LibroCalificaciones, que presentamos en el capítulo 3 y
expandimos en los capítulos 4 y 5. Recuerde que esta clase representa un libro de calificaciones utilizado por
un instructor para almacenar y analizar un conjunto de calificaciones de estudiantes. Las versiones anteriores de
esta clase procesan un conjunto de calificaciones introducidas por el usuario, pero no mantienen los valores de las
calificaciones individuales en variables de instancia de la clase. Por ende, los cálculos repetidos requieren que el
usuario vuelva a introducir las mismas calificaciones. Una manera de resolver este problema sería almacenar cada
calificación introducida por el usuario en una instancia individual de la clase. Por ejemplo, podríamos crear las
variables de instancia calificacion1, calificacion2, …, calificacion10 en la clase LibroCalificaciones para
almacenar 10 calificaciones de estudiantes. No obstante, el código para totalizar las calificaciones y determinar
el promedio de la clase sería voluminoso, y la clase no podría procesar más de 10 calificaciones a la vez. En esta
sección resolvemos este problema, almacenando las calificaciones en un arreglo.
Almacenar las calificaciones de los estudiantes en un arreglo en la clase LibroCalificaciones
La versión de la clase LibroCalificaciones (figura 7.14) que presentamos aquí utiliza un arreglo de enteros para
almacenar las calificaciones de varios estudiantes en un solo examen. Esto elimina la necesidad de introducir varias
veces el mismo conjunto de calificaciones. El arreglo calificaciones se declara como una variable de instancia
en la línea 7; por lo tanto, cada objeto LibroCalificaciones mantiene su propio conjunto de calificaciones. El
constructor de la clase (líneas 10 a 14) tiene dos parámetros: el nombre del curso y un arreglo de calificaciones.
Cuando una aplicación (por ejemplo, la clase PruebaLibroCalificaciones en la figura 7.15) crea un objeto
LibroCalificaciones, la aplicación pasa un arreglo int existente al constructor, el cual asigna la referencia del
arreglo a la variable de instancia calificaciones (línea 13). El tamaño del arreglo calificaciones se determina
en base a la clase que pasa el arreglo al constructor. Por ende, un objeto LibroCalificaciones puede procesar un
número de calificaciones variable. Los valores de las calificaciones en el arreglo que se pasa podría introducirlos
un usuario desde el teclado, o podrían leerse desde un archivo en el disco (como veremos en el capítulo 14). En
7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones 279
280 Capítulo 7 Arreglos
nuestra aplicación de prueba, simplemente inicializamos un arreglo con un conjunto de valores de calificaciones
(figura 7.15, línea 10). Una vez que las calificaciones se almacenan en una variable de instancia llamada cali-
ficaciones de la clase LibroCalificaciones, todos los métodos de la clase pueden acceder a los elementos de
calificaciones según sea necesario, para realizar varios cálculos.
1 // Fig. 7.14: LibroCalificaciones.java
2 // Libro de calificaciones que utiliza un arreglo para almacenar las calificaciones de
una prueba.
3
4 public class LibroCalificaciones
5 {
6 private String nombreDelCurso; // nombre del curso que representa este
LibroCalificaciones
7 private int calificaciones[]; // arreglo de calificaciones de estudiantes
8
9 // el constructor de dos argumentos inicializa nombreDelCurso y el arreglo
calificaciones
10 public LibroCalificaciones( String nombre, int arregloCalif[] )
11 {
12 nombreDelCurso = nombre; // inicializa nombreDelCurso
13 calificaciones = arregloCalif; // almacena las calificaciones
14 } // fin del constructor de LibroCalificaciones con dos argumentos
15
16 // método para establecer el nombre del curso
17 public void establecerNombreDelCurso( String nombre )
18 {
19 nombreDelCurso = nombre; // almacena el nombre del curso
20 } // fin del método establecerNombreDelCurso
21
22 // método para obtener el nombre del curso
23 public String obtenerNombreDelCurso()
24 {
25 return nombreDelCurso;
26 } // fin del método obtenerNombreDelCurso
27
28 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones
29 public void mostrarMensaje()
30 {
31 // obtenerNombreDelCurso obtiene el nombre del curso
32 System.out.printf( "Bienvenido al libro de calificaciones paran%s!nn",
33 obtenerNombreDelCurso() );
34 } // fin del método mostrarMensaje
35
36 // realiza varias operaciones sobre los datos
37 public void procesarCalificaciones()
38 {
39 // imprime el arreglo de calificaciones
40 imprimirCalificaciones();
41
42 // llama al método obtenerPromedio para calcular la calificación promedio
43 System.out.printf( "nEl promedio de la clase es %.2fn", obtenerPromedio() );
44
45 // llama a los métodos obtenerMinima y obtenerMaxima
46 System.out.printf( "La calificacion mas baja es %dnLa calificacion mas alta es
%dnn",
47 obtenerMinima(), obtenerMaxima() );
Figura 7.14 | La clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones de una prueba.
(Parte 1 de 3).
7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones 281
48
49 // llama a imprimirGraficoBarras para imprimir el gráfico de distribución de
calificaciones
50 imprimirGraficoBarras();
51 } // fin del método procesarCalificaciones
52
53 // busca la calificación más baja
54 public int obtenerMinima()
55 {
56 int califBaja = calificaciones[ 0 ]; // asume que calificaciones[ 0 ] es la más
baja
57
58 // itera a través del arreglo de calificaciones
59 for ( int calificacion : calificaciones )
60 {
61 // si calificación es menor que califBaja, se asigna a califBaja
62 if ( calificacion < califBaja )
63 califBaja = calificacion; // nueva calificación más baja
64 } // fin de for
65
66 return califBaja; // devuelve la calificación más baja
67 } // fin del método obtenerMinima
68
69 // busca la calificación más alta
70 public int obtenerMaxima()
71 {
72 int califAlta = calificaciones[ 0 ]; // asume que calificaciones[ 0 ] es la más
alta
73
74 // itera a través del arreglo de calificaciones
75 for ( int calificacion : calificaciones )
76 {
77 // si calificacion es mayor que califAlta, se asigna a califAlta
78 if ( calificacion > califAlta )
79 califAlta = calificacion; // nueva calificación más alta
80 } // fin de for
81
82 return califAlta; // devuelve la calificación más alta
83 } // fin del método obtenerMaxima
84
85 // determina la calificación promedio de la prueba
86 public double obtenerPromedio()
87 {
88 int total = 0; // inicializa el total
89
90 // suma las calificaciones para un estudiante
91 for ( int calificacion : calificaciones )
92 total += calificacion;
93
94 // devuelve el promedio de las calificaciones
95 return (double) total / calificaciones.length;
96 } // fin del método obtenerPromedio
97
98 // imprime gráfico de barras que muestra la distribución de las calificaciones
99 public void imprimirGraficoBarras()
100 {
101 System.out.println( "Distribucion de calificaciones:" );
102
Figura 7.14 | La clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones de una prueba.
(Parte 2 de 3).
282 Capítulo 7 Arreglos
El método procesarCalificaciones (líneas 37 a 51) contiene una serie de llamadas a métodos que produce
un reporte en el que se resumen las calificaciones. La línea 40 llama al método imprimirCalificaciones para
imprimir el contenido del arreglo calificaciones. Las líneas 134 a 136 en el método imprimirCalificaciones
utilizan una instrucción for para imprimir las calificaciones de los estudiantes. En este caso se debe utilizar una
instrucción for controlada por contador, ya que las líneas 135 y 136 utilizan el valor de la variable contador
estudiante para imprimir cada calificación enseguida de un número de estudiante específico (vea la figura 7.15).
Aunque los subíndices de los arreglos empiezan en 0, lo común es que el profesor enumere a los estudiantes empe-
zando desde 1. Por ende, las líneas 135 y 136 imprimen estudiante + 1 como el número de estudiante para
producir las etiquetas "Estudiante 1: ", "Estudiante 2: ", y así en lo sucesivo.
A continuación, el método procesarCalificaciones llama al método obtenerPromedio (línea 43) para
obtener el promedio de las calificaciones en el arreglo. El método obtenerPromedio (líneas 86 a 96) utiliza una
instrucción for mejorada para totalizar los valores en el arreglo calificaciones antes de calcular el promedio.
El parámetro en el encabezado de la instrucción for mejorada (por ejemplo, int calificacion) indica que para
cada iteración, la variable int calificacion recibe un valor en el arreglo calificaciones. Observe que el cálculo
del promedio en la línea 95 utiliza calificaciones.length para determinar el número de calificaciones que se
van a promediar.
103 // almacena la frecuencia de las calificaciones en cada rango de 10 calificaciones
104 int frecuencia[] = new int[ 11 ];
105
106 // para cada calificación, incrementa la frecuencia apropiada
107 for ( int calificacion : calificaciones )
108 ++frecuencia[ calificacion / 10 ];
109
110 // para cada frecuencia de calificación, imprime una barra en el gráfico
111 for ( int cuenta = 0; cuenta < frecuencia.length; cuenta++ )
112 {
113 // imprime etiquetas de las barras ( "00-09: ", ..., "90-99: ", "100: " )
114 if ( cuenta == 10 )
115 System.out.printf( "%5d: ", 100 );
116 else
117 System.out.printf( "%02d-%02d: ",
118 cuenta * 10, cuenta * 10 + 9 );
119
120 // imprime barra de asteriscos
121 for ( int estrellas = 0; estrellas < frecuencia[ cuenta ]; estrellas++ )
122 System.out.print( "*" );
123
124 System.out.println(); // inicia una nueva línea de salida
125 } // fin de for externo
126 } // fin del método imprimirGraficoBarras
127
128 // imprime el contenido del arreglo de calificaciones
129 public void imprimirCalificaciones()
130 {
131 System.out.println( "Las calificaciones son:n" );
132
133 // imprime la calificación de cada estudiante
134 for ( int estudiante = 0; estudiante < calificaciones.length; estudiante++ )
135 System.out.printf( "Estudiante %2d: %3dn",
136 estudiante + 1, calificaciones[ estudiante ] );
137 } // fin del método imprimirCalificaciones
138 } // fin de la clase LibroCalificaciones
Figura 7.14 | La clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones de una prueba.
(Parte 3 de 3).
7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones 283
Las líneas 46 y 47 en el método procesarCalificaciones llaman a los métodos obtenerMinima y obte-
nerMaxima para determinar las calificaciones más baja y más alta de cualquier estudiante en el examen, en forma
respectiva. Cada uno de estos métodos utiliza una instrucción for mejorada para iterar a través del arreglo cali-
ficaciones. Las líneas 59 a 64 en el método obtenerMinima iteran a través del arreglo. Las líneas 62 y 63 com-
paran cada calificación con califBaja; si una calificación es menor que califBaja, a califBaja se le asigna esa
calificación. Cuando la línea 66 se ejecuta, califBaja contiene la calificación más baja en el arreglo. El método
obtenerMaxima (líneas 70 a 83) funciona de manera similar al método obtenerMinima.
Por último, la línea 50 en el método procesarCalificaciones llama al método imprimirGraficoBarras
para imprimir un gráfico de distribución de los datos de las calificaciones, mediante el uso de una técnica similar
a la de la figura 7.6. En ese ejemplo, calculamos en forma manual el número de calificaciones en cada categoría (es
decir, de 0 a 9, de 10 a 19, …, de 90 a 99 y 100) con sólo analizar un conjunto de calificaciones. En este ejemplo,
las líneas 107 y 108 utilizan una técnica similar a la de las figuras 7.7 y 7.8 para calcular la frecuencia de las califi-
caciones en cada categoría. La línea 104 declara y crea el arreglo frecuencia de 11 valores int para almacenar la
frecuencia de las calificaciones en cada categoría de éstas. Para cada calificacion en el arreglo calificaciones,
las líneas 107 y 108 incrementan el elemento apropiado del arreglo frecuencia. Para determinar qué elemento
se debe incrementar, la línea 108 divide la calificacion actual entre 10, mediante la división entera. Por ejemplo,
si calificacion es 85, la línea 108 incrementa frecuencia[ 8 ] para actualizar la cuenta de calificaciones en
el rango 80-89. Las líneas 111 a 125 imprimen a continuación el gráfico de barras (vea la figura 7.15), con base
en los valores en el arreglo frecuencia. Al igual que las líneas 23 y 24 de la figura 7.6, las líneas 121 y 122 de
la figura 7.14 utilizan un valor en el arreglo frecuencia para determinar el número de asteriscos a imprimir en
cada barra.
La clase PruebaLibroCalificaciones para demostrar la clase LibroCalificaciones
La aplicación de la figura 7.15 crea un objeto de la clase LibroCalificaciones (figura 7.14) mediante el uso del
arreglo int arregloCalif (que se declara y se inicializa en la línea 10). Las líneas 12 y 13 pasan el nombre de un
curso y arregloCalif al constructor de LibroCalificaciones. La línea 14 imprime un mensaje de bienvenida,
y la línea 15 invoca el método procesarCalificaciones del objeto LibroCalificaciones. La salida muestra el
resumen de las 10 calificaciones en miLibroCalificaciones.
Figura 7.15 | PruebaLibroCalificaciones crea un objeto LibroCalificaciones usando un arreglo de
calificaciones, y después invoca al método procesarCalificaciones para analizarlas. (Parte 1 de 2).
1 // Fig. 7.15: PruebaLibroCalificaciones.java
2 // Crea objeto LibroCalificaciones, usando un arreglo de calificaciones.
3
4 public class PruebaLibroCalificaciones
5 {
6 // el método main comienza la ejecución del programa
7 public static void main( String args[] )
8 {
9 // arreglo unidimensional de calificaciones de estudiantes
10 int arregloCalif[] = { 87, 68, 94, 100, 83, 78, 85, 91, 76, 87 };
11
12 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones(
13 “CS101 Introduccion a la programacion en Java”, arregloCalif );
14 miLibroCalificaciones.mostrarMensaje();
15 miLibroCalificaciones.procesarCalificaciones();
16 } // fin de main
17 } // fin de la clase PruebaLibroCalificaciones
Bienvenido al libro de calificaciones para
CS101 Introduccion a la programacion en Java!
284 Capítulo 7 Arreglos
Las calificaciones son:
Estudiante 1: 87
Estudiante 2: 68
Estudiante 3: 94
Estudiante 4: 100
Estudiante 5: 83
Estudiante 6: 78
Estudiante 7: 85
Estudiante 8: 91
Estudiante 9: 76
Estudiante 10: 87
El promedio de la clase es 84.90
La calificacion mas baja es 68
La calificacion mas alta es 100
Distribucion de calificaciones:
00-09:
10-19:
20-29:
30-39:
40-49:
50-59:
60-69: *
70-79: **
80-89: ****
90-99: **
100: *
Figura 7.15 | PruebaLibroCalificaciones crea un objeto LibroCalificaciones usando un arreglo de
calificaciones, y después invoca al método procesarCalificaciones para analizarlas. (Parte 2 de 2).
Observación de ingeniería de software 7.1
Un arnés de prueba (o aplicación de prueba) es responsable de crear un objeto de la clase que se probará y de propor-
cionarle datos. Estos datos podrían provenir de cualquiera de varias fuentes. Los datos de prueba pueden colocarse
directamente en un arreglo con un inicializador de arreglos, pueden provenir del usuario mediante el teclado, de un
archivo (como veremos en el capítulo 14) o pueden provenir de una red (como veremos en el capítulo 24). Después de
pasar estos datos al constructor de la clase para instanciar el objeto, este arnés de prueba debe llamar al objeto para
probar sus métodos y manipular sus datos. La recopilación de datos en el arnés de prueba de esta forma permite a la
clase manipular datos de varias fuentes.
7.9 Arreglos multidimensionales
Los arreglos multidimensionales de dos dimensiones se utilizan con frecuencia para representar tablas de valores,
las cuales consisten en información ordenada en filas y columnas. Para identificar un elemento específico de una
tabla, debemos especificar dos subíndices. Por convención, el primero identifica la fila del elemento y el segundo
su columna. Los arreglos que requieren dos subíndices para identificar un elemento específico se llaman arre-
glos bidimensionales (los arreglos multidimensionales pueden tener más de dos dimensiones). Java no soporta
los arreglos multidimensionales directamente, pero permite al programador especificar arreglos unidimensionales,
cuyos elementos sean también arreglos unidimensionales, con lo cual se obtiene el mismo efecto. La figura 7.16
ilustra un arreglo bidimensional a, que contiene tres filas y cuatro columnas (es decir, un arreglo de tres por cua-
tro). En general, a un arreglo con m filas y n columnas se le llama arreglo de m por n.
Cada elemento en el arreglo a se identifica en la figura 7.16 mediante una expresión de acceso a un arreglo de
la forma a [ fila ][ columna ]; a es el nombre del arreglo, fila y columna son los subíndices que identifican
en forma única a cada elemento en el arreglo a por número de fila y columna. Observe que los nombres de los
elementos en la fila 0 tienen todos un primer subíndice de 0, y los nombres de los elementos en la columna 3
tienen un segundo subíndice de 3.
Arreglos de arreglos unidimensionales
Al igual que los arreglos unidimensionales, los arreglos multidimensionales pueden inicializarse mediante iniciali-
zadores de arreglos en las declaraciones. Un arreglo bidimensional b con dos filas y dos columnas podría declararse
e inicializarse con inicializadores de arreglos anidados, como se muestra a continuación:
int b[ ] [ ] = { { 1, 2 }, {3, 4} };
Los valores del inicializador se agrupan por fila entre llaves. Así, 1 y 2 inicializan a b[ 0 ][ 0 ] y b[ 0 ][ 1 ],
respectivamente; 3 y 4 inicializan a b[ 1 ][ 0 ] y b[ 1 ][ 1 ], respectivamente. El compilador cuenta el núme-
ro de inicializadores de arreglos anidados (representados por conjuntos de llaves dentro de las llaves externas) en
la declaración del arreglo, para determinar el número de filas en el arreglo b. El compilador cuenta los valores
inicializadores en el inicializador de arreglos anidado de una fila, para determinar el número de columnas en esa
fila. Como veremos en unos momentos, esto significa que las filas pueden tener distintas longitudes.
Los arreglos multidimensionales se mantienen como arreglos de arreglos unidimensionales. Por lo tanto, el
arreglo b en la declaración anterior está realmente compuesto de dos arreglos unidimensionales separados: uno
que contiene los valores en la primera lista inicializadora anidada { 1, 2 } y uno que contiene los valores en la
segunda lista inicializadora anidada { 3, 4 }. Así, el arreglo b en sí es un arreglo de dos elementos, cada uno de
los cuales es un arreglo unidimensional de valores int.
Arreglos bidimensionales con filas de distintas longitudes
La forma en que se representan los arreglos multidimensionales los hace bastante flexibles. De hecho, las longitu-
des de las filas en el arreglo b no tienen que ser iguales. Por ejemplo,
int b[ ][ ] = { { 1, 2 }, { 3, 4, 5 } };
crea el arreglo entero b con dos elementos (los cuales se determinan según el número de inicializadores de arreglos
anidados) que representan las filas del arreglo bidimensional. Cada elemento de b es una referencia a un arre-
glo unidimensional de variables int. El arreglo int de la fila 0 es un arreglo unidimensional con dos elementos
(1 y 2), y el arreglo int de la fila 1 es un arreglo unidimensional con tres elementos (3, 4 y 5).
Creación de arreglos bidimensionales mediante expresiones de creación de arreglos
Un arreglo multidimensional con el mismo número de columnas en cada fila puede crearse mediante una expre-
sión de creación de arreglos. Por ejemplo, en las siguientes líneas se declara el arreglo b y se le asigna una referencia
a un arreglo de tres por cuatro:
int b[ ][ ] = new int[ 3 ][ 4 ];
7.9 Arreglos multidimensionales 285
Fila 0
Fila 1
Fila 2
Subíndice de columna
Subíndice de fila
Nombre del arreglo
a[ 0 ][ 0 ]
a[ 1 ][ 0 ]
a[ 2 ][ 0 ]
a[ 0 ][ 1 ]
a[ 1 ][ 1 ]
a[ 2 ][ 1 ]
a[ 0 ][ 2 ]
a[ 1 ][ 2 ]
a[ 2 ][ 2 ]
a[ 0 ][ 3 ]
Columna 0 Columna 1 Columna 2 Columna 3
a[ 1 ][ 3 ]
a[ 2 ][ 3 ]
Figura 7.16 | Arreglo bidimensional con tres filas y cuatro columnas.
286 Capítulo 7 Arreglos
En este caso, utilizamos los valores literales 3 y 4 para especificar el número de filas y columnas, respectivamente,
pero esto no es obligatorio. Los programas también pueden utilizar variables para especificar las dimensiones
de los arreglos, ya que new crea arreglos en tiempo de ejecución, no en tiempo de compilación. Al igual que
con los arreglos unidimensionales, los elementos de un arreglo multidimensional se inicializan cuando se crea el
objeto arreglo.
Un arreglo multidimensional, en el que cada fila tiene un número distinto de columnas, puede crearse de la
siguiente manera:
int b[][] = new int[ 2 ][ ]; // crea 2 filas
b[ 0 ] = new int[ 5 ]; // crea 5 columnas para la fila 0
b[ 1 ] = new int[ 3 ]; // crea 3 columnas para la fila 1
Estas instrucciones crean un arreglo bidimensional con dos filas. La fila 0 tiene cinco columnas y la fila 1 tiene 3.
Ejemplo de arreglos bidimensionales: cómo mostrar los valores de los elementos
La figura 7.17 demuestra cómo inicializar arreglos bidimensionales con inicializadores de arreglos, y cómo utilizar
ciclos for anidados para recorrer los arreglos (es decir, manipular cada uno de los elementos de cada arreglo).
El método main de la clase InicArreglo declara dos arreglos. En la declaración de arreglo1 (línea 9) se
utilizan inicializadores de arreglos anidados para inicializar la primera fila del arreglo con los valores 1, 2 y 3, y la
segunda fila con los valores 4, 5 y 6. En la declaración de arreglo2 (línea 10) se utilizan inicializadores anidados
de distintas longitudes. En este caso, la primera fila se inicializa para tener dos elementos con los valores 1 y 2,
respectivamente. La segunda fila se inicializa para tener un elemento con el valor 3. La tercera fila se inicializa para
tener tres elementos con los valores 4, 5 y 6, respectivamente.
1 // Fig. 7.17: InicArreglo.java
2 // Inicialización de arreglos bidimensionales.
3
4 public class InicArreglo
5 {
6 // crea e imprime arreglos bidimensionales
7 public static void main( String args[] )
8 {
9 int arreglo1[][] = { { 1, 2, 3 }, { 4, 5, 6 } };
10 int arreglo2[][] = { { 1, 2 }, { 3 }, { 4, 5, 6 } };
11
12 System.out.println( "Los valores en arreglo1 por filas son" );
13 imprimirArreglo( arreglo1 ); // muestra arreglo1 por filas
14
15 System.out.println( "nLos valores en arreglo2 por filas son" );
16 imprimirArreglo( arreglo2 ); // muestra arreglo2 por filas
17 } // fin de main
18
19 // imprime filas y columnas de un arreglo bidimensional
20 public static void imprimirArreglo( int arreglo[][] )
21 {
22 // itera a través de las filas del arreglo
23 for ( int fila = 0; fila < arreglo.length; fila++ )
24 {
25 // itera a través de las columnas de la fila actual
26 for ( int columna = 0; columna < arreglo[ fila ].length; columna++ )
27 System.out.printf( "%d ", arreglo[ fila ][ columna ] );
28
29 System.out.println(); // inicia nueva línea de salida
30 } // fin de for externo
31 } // fin del método imprimirArreglo
32 } // fin de la clase InicArreglo
Figura 7.17 | Inicialización de arreglos bidimensionales. (Parte 1 de 2).
Las líneas 13 y 16 llaman al método imprimirArreglo (líneas 20 a 31) para imprimir los elementos de
arreglo1 y arreglo2, respectivamente. El método imprimirArreglo especifica el parámetro tipo arreglo como
int arreglo[][] para indicar que el método recibe un arreglo bidimensional. La instrucción for (líneas 23
a 30) imprime las filas de un arreglo bidimensional. En la condición de continuación de ciclo de la instrucción
for exterior, la expresión arreglo.length determina el número de filas en el arreglo. En la expresión for inte-
rior, la expresión arreglo[ fila ].length determina el número de columnas en la fila actual del arreglo. Esta
condición permite al ciclo determinar el número exacto de columnas en cada fila.
Manipulaciones comunes en arreglos multidimensionales, realizadas mediante instrucciones for
En muchas manipulaciones comunes en arreglos se utilizan instrucciones for. Como ejemplo, la siguiente ins-
trucción for asigna a todos los elementos en la fila 2 del arreglo a, en la figura 7.16, el valor de cero:
for ( int columna = 0; columna < a[ 2 ].length; columna++ )
a[ 2 ][ columna ] = 0;
Especificamos la fila 2; por lo tanto, sabemos que el primer índice siempre será 2 (0 es la primera fila y 1 es la
segunda). Este ciclo for varía solamente el segundo índice (es decir, el índice de la columna). Si la fila 2 del arreglo
a contiene cuatro elementos, entonces la instrucción for anterior es equivalente a las siguientes instrucciones de
asignación:
a[ 2 ][ 0 ] = 0;
a[ 2 ][ 1 ] = 0;
a[ 2 ][ 2 ] = 0;
a[ 2 ][ 3 ] = 0;
La siguiente instrucción for anidada suma el total de los valores de todos los elementos del arreglo a:
int total = 0;
for ( int fila = 0; fila < a.length; fila++ )
{
for ( int columna = 0; columna < a[ fila ].length; columna++ )
total += a[ fila ][ columna ];
} // fin de for exterior
Estas instrucciones for anidadas suman el total de los elementos del arreglo, una fila a la vez. La instrucción for
exterior empieza asignando 0 al índice fila, de manera que los elementos de la primera fila puedan totalizarse
mediante la instrucción for interior. Después, la instrucción for exterior incrementa fila a 1, de manera que la
segunda fila pueda totalizarse. Luego, la instrucción for exterior incrementa fila a 2, para que la tercera fila pueda
totalizarse. La variable total puede mostrarse al terminar la instrucción for exterior. En el siguiente ejemplo le
mostraremos cómo procesar un arreglo bidimensional de una manera similar, usando instrucciones for mejora-
das anidadas.
7.9 Arreglos multidimensionales 287
Los valores en arreglo1 por filas son
1 2 3
4 5 6
Los valores en arreglo2 por filas son
1 2
3
4 5 6
Figura 7.17 | Inicialización de arreglos bidimensionales. (Parte 2 de 2).
288 Capítulo 7 Arreglos
7.10 Ejemplo práctico: la clase LibroCalificaciones que usa
un arreglo bidimensional
En la sección 7.8 presentamos la clase LibroCalificaciones (figura 7.14), la cual utilizó un arreglo unidimen-
sional para almacenar las calificaciones de los estudiantes en un solo examen. En la mayoría de los cursos, los
estudiantes presentan varios exámenes. Es probable que los profesores quieran analizar las calificaciones a lo largo
de todo el curso, tanto para un solo estudiante como para la clase en general.
Cómo almacenar las calificaciones de los estudiantes en un arreglo bidimensional en la clase
LibroCalificaciones
La figura 7.18 contie
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf
Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf

Más contenido relacionado

PDF
17 arreglos bidimensionales java
PDF
Introducción a Xamarin Forms con XAML
PPTX
Una base de datos relacional
PPTX
Fundamentos de BD - Unidad 2 Modelo Entidad Relacion
PDF
Paralelos y meridianos 2
PPTX
Aguas superficiales
PPT
Programación por Capas en PHP
17 arreglos bidimensionales java
Introducción a Xamarin Forms con XAML
Una base de datos relacional
Fundamentos de BD - Unidad 2 Modelo Entidad Relacion
Paralelos y meridianos 2
Aguas superficiales
Programación por Capas en PHP

La actualidad más candente (20)

PPTX
Apache cassandra
PPTX
Hilos cindy
PPS
Tectonica De Placas (resumen)
DOCX
Operaciones Básicas con el Sistema Binario
DOCX
Manual de instalacion
PPTX
Modelo Relacional (Base de Datos)
PDF
7. Mantenimiento de Software
PDF
Arreglos Bidimensionales - Java - NetBeans
DOCX
Informe de programacion
PPTX
Dbms orientados a objetos
ODP
Active Directory
PPTX
1.4 software numerico
PPT
Diagramas de Secuencia.
DOCX
Informe v2.1 Base de Datos II - Proyecto TodoAutos : venta de carros del año
PPTX
Sistemas de gestión de base de datos
PPTX
10 sistemas gestores de base de datos
PPTX
Presentacion de Listas, Pilas y Colas
DOCX
Informe técnico Unidad 4 Estructuras no lineales (Rubí Verónica)
DOCX
Explicacion metodologia 3 capas y base de datos, proyecto de ejemplo jsp
PDF
Informe Final Del Proyecto Poo
 
Apache cassandra
Hilos cindy
Tectonica De Placas (resumen)
Operaciones Básicas con el Sistema Binario
Manual de instalacion
Modelo Relacional (Base de Datos)
7. Mantenimiento de Software
Arreglos Bidimensionales - Java - NetBeans
Informe de programacion
Dbms orientados a objetos
Active Directory
1.4 software numerico
Diagramas de Secuencia.
Informe v2.1 Base de Datos II - Proyecto TodoAutos : venta de carros del año
Sistemas de gestión de base de datos
10 sistemas gestores de base de datos
Presentacion de Listas, Pilas y Colas
Informe técnico Unidad 4 Estructuras no lineales (Rubí Verónica)
Explicacion metodologia 3 capas y base de datos, proyecto de ejemplo jsp
Informe Final Del Proyecto Poo
 
Publicidad

Similar a Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf (20)

DOCX
Como programar en java
PDF
Como programar java, 9na edicion deitel
PDF
Como programar java, 9na edicion deitel
PDF
Como programar en java 9na completo
PDF
Prentice hall piensa en java (bruce eckel) - segunda edicion
PDF
Apuntes de introduccion a la programación
PDF
Cómo programar C++, 9na Edición - Paul Deitel.pdf
PDF
Java jedi pre
PDF
Java jedi prev
PDF
Java angel estevan pre
PDF
Java angel estevan pre
PDF
Mini curso de java
PDF
2280954 java
PDF
Aprender java
PDF
Lenguaje java
PDF
Lenguaje de programacion java pre
PDF
Lenguaje de programacion java prev
PPT
Java Ago Dic07
PDF
Aprendiendo java
PDF
Libro java
Como programar en java
Como programar java, 9na edicion deitel
Como programar java, 9na edicion deitel
Como programar en java 9na completo
Prentice hall piensa en java (bruce eckel) - segunda edicion
Apuntes de introduccion a la programación
Cómo programar C++, 9na Edición - Paul Deitel.pdf
Java jedi pre
Java jedi prev
Java angel estevan pre
Java angel estevan pre
Mini curso de java
2280954 java
Aprender java
Lenguaje java
Lenguaje de programacion java pre
Lenguaje de programacion java prev
Java Ago Dic07
Aprendiendo java
Libro java
Publicidad

Más de AnaLpez275 (18)

PDF
PROYECTO INTEGRADOR ASTINI-BELLIDO VFINAL.pdf
PDF
PROYECTO INTEGRADOR ASTINI-BELLIDO VFINAL (1).pdf
PDF
UPS-CT008640.pdf
DOCX
ESTRUCTURA DE ANTEPROYECTO_ (2).docx
DOCX
LIBRO Y SOPA LETRAS.docx
PDF
Como programar en Java - 7ma Edicion - P. J. Deitel.pdf
PDF
AprendiendoJava.pdf
DOCX
codigomenu-1.docx
DOCX
codigomenu-1 (1).docx
PDF
Como programar en Java - 7ma Edicion - P. J. Deitel.pdf
DOC
Ejercicios_Basicos_de_netbeans.doc
PDF
Desarrollo_de_Software_con_NetBeans_7_1.pdf
PDF
INSTITUTO_TECNOLOGICO_DE_MORELIA_PROGRAM.pdf
DOC
Ejercicios_Basicos_de_netbeans (2).doc
PDF
Manual_java.pdf
DOC
Ejercicios_Basicos_de_netbeans (3).doc
DOC
Ejercicios_Basicos_de_netbeans (1).doc
PDF
Guis_en_java_6pp.pdf
PROYECTO INTEGRADOR ASTINI-BELLIDO VFINAL.pdf
PROYECTO INTEGRADOR ASTINI-BELLIDO VFINAL (1).pdf
UPS-CT008640.pdf
ESTRUCTURA DE ANTEPROYECTO_ (2).docx
LIBRO Y SOPA LETRAS.docx
Como programar en Java - 7ma Edicion - P. J. Deitel.pdf
AprendiendoJava.pdf
codigomenu-1.docx
codigomenu-1 (1).docx
Como programar en Java - 7ma Edicion - P. J. Deitel.pdf
Ejercicios_Basicos_de_netbeans.doc
Desarrollo_de_Software_con_NetBeans_7_1.pdf
INSTITUTO_TECNOLOGICO_DE_MORELIA_PROGRAM.pdf
Ejercicios_Basicos_de_netbeans (2).doc
Manual_java.pdf
Ejercicios_Basicos_de_netbeans (3).doc
Ejercicios_Basicos_de_netbeans (1).doc
Guis_en_java_6pp.pdf

Último (20)

PPTX
lareformaprevisional-091013175510-phpapp01.pptx
PPTX
Presentacion Capacitacion RC y RG (5).pptx
PPTX
Tutoria 3. Unidad 2 PRUEBAS BIOLÓGICAS.pptx
PPTX
EXPOSICIÓN 2021.pptxhgdfshdghsdgshdghsds
PDF
Registro de Limpieza y Desinfección.pdf1
PDF
Riesgos en Negociaciones_comercio exterior.pdf
PPTX
Introducción al analisis de datos con Power bi
PPTX
Características de jean Piaget y su fase cognitivo
PDF
lavado de manos_20250805_212935_0000.pdf
DOCX
LAS DROGAS, SU CONSUMO Y LAS ADICCIONES.docx
PPTX
Precio optimo de venta para un emprendimiento familiar
PDF
MORFOLOGIA (ASPECTO FORMACION DE PALABRAS).pdf
PPTX
Abdomen HosAESREBBweubeehkrhkqhrkhehrjktil.pptx
PDF
INFORME ESPECIAL BICENTENARIO DE BOLIVIA.pdf
PDF
docsity-diapositivas-de-la-salud-mental.pdf
PDF
Unidad Nº 1 Introduccion a Estadísticas
PDF
REPORTE DE VICTIMAS POR HOMICIDIO DOLOSO IRAPUATO JULIO 2025
PDF
2425_s9_1_Bitacora_para_la_reflexion.pdf
PPTX
ESTRADA ORDEN INTERNO 111111111111111111
PPTX
CANALES DE DISTRIBUCION en comercio internacional
lareformaprevisional-091013175510-phpapp01.pptx
Presentacion Capacitacion RC y RG (5).pptx
Tutoria 3. Unidad 2 PRUEBAS BIOLÓGICAS.pptx
EXPOSICIÓN 2021.pptxhgdfshdghsdgshdghsds
Registro de Limpieza y Desinfección.pdf1
Riesgos en Negociaciones_comercio exterior.pdf
Introducción al analisis de datos con Power bi
Características de jean Piaget y su fase cognitivo
lavado de manos_20250805_212935_0000.pdf
LAS DROGAS, SU CONSUMO Y LAS ADICCIONES.docx
Precio optimo de venta para un emprendimiento familiar
MORFOLOGIA (ASPECTO FORMACION DE PALABRAS).pdf
Abdomen HosAESREBBweubeehkrhkqhrkhehrjktil.pptx
INFORME ESPECIAL BICENTENARIO DE BOLIVIA.pdf
docsity-diapositivas-de-la-salud-mental.pdf
Unidad Nº 1 Introduccion a Estadísticas
REPORTE DE VICTIMAS POR HOMICIDIO DOLOSO IRAPUATO JULIO 2025
2425_s9_1_Bitacora_para_la_reflexion.pdf
ESTRADA ORDEN INTERNO 111111111111111111
CANALES DE DISTRIBUCION en comercio internacional

Como programar en Java - 7ma Edicion - P. J. Deitel (1).pdf

  • 1. ® I N C LUYE CD-R O M H o ssFly ™ ™ ™ Visítenos en: www.pearsoneducacion.net ISBN 978-970-26-1190-5 ® Una introducción completa y autorizada del código activo de DEITEL® a la pro- gramación orientada a objetos, con la nueva edición Java™ Standard Edition 6, JDBC™ 4, JavaServer Faces y Servicios Web ¡Java™ es el lenguaje de programación orientada a objetos más popular, con cinco millones de desarrolladores! Esta nueva edición del libro de texto sobre Java más utilizado en el mundo emplea un método anticipado para las clases y objetos. Incluye también una cobertura completa de la programación orientada a objetos en Java, para lo cual presenta varios ejemplos prácticos integrados: la clase Tiempo, la clase Empleado, la clase LibroCalificaciones, un ejemplo práctico opcional de DOO/UML™ 2 con el ATM (capítulos 1 a 8 y 10), el ejemplo práctico opcional de GUI y gráficos (capítulos 3 a 10), un libro de direc- ciones controlado por base de datos (capítulo 25) y dos aplicaciones Web multinivel controladas por bases de datos: una libreta de direcciones que utiliza controles JSF habilitados para AJAX para mostrar un nombre y una dirección en un Mapa de Google™ (capítulo 27), y un sistema de reservaciones de una aerolínea que utiliza ser- vicios Web (capítulo 28). Los recursos para los usuarios de este libro incluyen los sitios Web (www.deitel.com y www.pearsoeducacion.net/deitel) con los ejemplos de código del libro e información para profesores, estudiantes y profesionales. El CD de este libro incluye material adicional en español y códigos de los ejemplos del libro. Para mayor información visite: www.pearsoneducacion.net/deitel Deitel Java.qxp 4/29/08 9:47 AM Page 1
  • 3.
  • 5. P. J. Deitel Deitel & Associates, Inc. H. M. Deitel Deitel & Associates, Inc. TRADUCCIÓN Alfonso Vidal Romero Elizondo Ingeniero en Sistemas Electrónicos Instituto Tecnológico y de Estudios Superiores de Monterrey Campus Monterrey REVISIÓN TÉCNICA Gabriela Azucena Campos García Roberto Martínez Román Departamento de Computación Instituto Tecnológico y de Estudios Superiores de Monterrey Campus Estado de México Jorge Armando Aparicio Lemus Coordinador del Área de Software Universidad Tecnológica de El Salvador ™
  • 6. Authorized translation from the English language edition entitled Java™ How to Program, 7th Edition, by Deitel & Associates (Harvey & Paul), published by Pearson Education, Inc., publishing as Prentice Hall, Inc., Copyright © 2007. All rights reserved. ISBN 0-13-222220-5 Traducción autorizada de la edición en idioma inglés titulada Java™ How to Program, 7a Edición, por Deitel & Associates (Harvey & Paul), publicada por Pearson Education, Inc., publicada como Prentice Hall, Inc., Copyright © 2007. Todos los derechos reservados. Esta edición en español es la única autorizada. Edición en español Editor: Luis Miguel Cruz Castillo e-mail: luis.cruzpearsoned.com Editor de desarrollo: Bernardino Gutiérrez Hernández Supervisor de producción: Enrique Trejo Hernández Edición en inglés DEITEL, PAUL J. Y HARVEY M. DEITEL CÓMO PROGRAMAR EN JAVA. Séptima edición PEARSON EDUCACIÓN, México 2008 ISBN: 978-970-26-1190-5 Área: Computación Formato: 20 × 25.5 cm Páginas: 1152 Vice President and Editorial Director, ECS: Marcia J. Horton Associate Editor: Jennifer Cappello Assistant Editor: Carole Snyder Executive Managing Editor: Vince O’Brien Managing Editor: Bob Engelhardt Production Editors: Donna M. Crilly, Marta Samsel Director of Creative Services: Paul Belfanti A/V Production Editor: Xiaohong Zhu Art Studio: Artworks, York, PA Creative Director: Juan López Art Director: Kristine Carney Cover Design: Abbey S. Deitel, Harvey M. Deitel, Francesco Santalucia, Kristine Carney Interior Design: Harvey M. Deitel, Kristine Carney Manufacturing Manager: Alexis Heydt-Long Manufacturing Buyer: Lisa McDowell Executive Marketing Manager: Robin O’Brien SÉPTIMA EDICIÓN, 2008 D.R. © 2008 por Pearson Educación de México, S.A. de C.V. Atlacomulco 500-5o. piso Col. Industrial Atoto 53519, Naucalpan de Juárez, Estado de México Cámara Nacional de la Industria Editorial Mexicana. Reg. Núm. 1031. Prentice Hall es una marca registrada de Pearson Educación de México, S.A. de C.V. Reservados todos los derechos. Ni la totalidad ni parte de esta publicación pueden reproducirse, registrarse o transmitirse, por un sistema de recuperación de información, en ninguna forma ni por ningún medio, sea electrónico, mecánico, fotoquímico, magnético o electroóptico, por fotocopia, grabación o cualquier otro, sin permiso previo por escrito del editor. El préstamo, alquiler o cualquier otra forma de cesión de uso de este ejemplar requerirá también la autorización del editor o de sus represen- tantes. ISBN 10: 970-26-1190-3 ISBN 13: 978-970-26-1190-5 Impreso en México. Printed in Mexico. 1 2 3 4 5 6 7 8 9 0 - 11 10 09 08 ®
  • 7. A Vince O’Brien, Director de Administración de Proyectos, Prentice Hall. Es un privilegio para nosotros trabajar con un profesional consumado. Nuestros mejores deseos para tu éxito continuo. Paul y Harvey
  • 8. Marcas registradas DEITEL, el insecto con dos pulgares hacia arriba y DIVE INTO son marcas registradas de Deitel and Associates, Inc. Java y todas las marcas basadas en Java son marcas registradas de Sun Microsystems, Inc., en los Estados Unidos y otros países. Pearson Education es independiente de Sun Microsystems, Inc. Microsoft, Internet Explorer y el logotipo de Windows son marcas registradas de Microsoft Corporation en los Estados Uni- dos y/o en otros países UNIX es una marca registrada de The Open Group.
  • 9. Contenido Prefacio xix Antes de empezar xxx 1 Introducción a las computadoras, Internet y Web 1 1.1 Introducción 2 1.2 ¿Qué es una computadora? 4 1.3 Organización de una computadora 4 1.4 Los primeros sistemas operativos 5 1.5 Computación personal, distribuida y cliente/servidor 5 1.6 Internet y World Wide Web 6 1.7 Lenguajes máquina, ensambladores y de alto nivel 6 1.8 Historia de C y C++ 7 1.9 Historia de Java 8 1.10 Bibliotecas de clases de Java 8 1.11 FORTRAN, COBOL, Pascal y Ada 9 1.12 BASIC, Visual Basic, Visual C++, C# y .NET 10 1.13 Entorno de desarrollo típico en Java 10 1.14 Generalidades acerca de Java y este libro 13 1.15 Prueba de una aplicación en Java 14 1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML 19 1.17 Web 2.0 23 1.18 Tecnologías de software 24 1.19 Conclusión 25 1.20 Recursos Web 25 2 Introducción a las aplicaciones en Java 34 2.1 Introducción 35 2.2 Su primer programa en Java: imprimir una línea de texto 35 2.3 Modificación de nuestro primer programa en Java 41 2.4 Cómo mostrar texto con printf 43 2.5 Otra aplicación en Java: suma de enteros 44 2.6 Conceptos acerca de la memoria 48 2.7 Aritmética 49 2.8 Toma de decisiones: operadores de igualdad y relacionales 52 2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de requerimientos de un problema 56 2.10 Conclusión 65 3 Introducción a las clases y los objetos 75 3.1 Introducción 76 3.2 Clases, objetos, métodos y variables de instancia 76
  • 10. viii Contenido 3.3 Declaración de una clase con un método e instanciamiento de un objeto de una clase 77 3.4 Declaración de un método con un parámetro 81 3.5 Variables de instancia, métodos establecer y métodos obtener 84 3.6 Comparación entre tipos primitivos y tipos por referencia 88 3.7 Inicialización de objetos mediante constructores 89 3.8 Números de punto flotante y el tipo double 91 3.9 (Opcional) Ejemplo práctico de GUI y gráficos: uso de cuadros de diálogo 95 3.10 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un documento de requerimientos 98 3.11 Conclusión 105 4 Instrucciones de control: parte 1 112 4.1 Introducción 113 4.2 Algoritmos 113 4.3 Seudocódigo 114 4.4 Estructuras de control 114 4.5 Instrucción de selección simple if 116 4.6 Instrucción de selección doble if...else 117 4.7 Instrucción de repetición while 121 4.8 Cómo formular algoritmos: repetición controlada por un contador 123 4.9 Cómo formular algoritmos: repetición controlada por un centinela 127 4.10 Cómo formular algoritmos: instrucciones de control anidadas 134 4.11 Operadores de asignación compuestos 138 4.12 Operadores de incremento y decremento 139 4.13 Tipos primitivos 142 4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples 142 4.15 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de los atributos de las clases 146 4.16 Conclusión 150 5 Instrucciones de control: parte 2 164 5.1 Introducción 165 5.2 Fundamentos de la repetición controlada por contador 165 5.3 Instrucción de repetición for 167 5.4 Ejemplos sobre el uso de la instrucción for 171 5.5 Instrucción de repetición do...while 174 5.6 Instrucción de selección múltiple switch 176 5.7 Instrucciones break y continue 183 5.8 Operadores lógicos 185 5.9 Resumen sobre programación estructurada 190 5.10 (Opcional) Ejemplo práctico de GUI y gráficos: dibujo de rectángulos y óvalos 194 5.11 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo identificar los estados y actividades de los objetos 197 5.12 Conclusión 200 6 Métodos: un análisis más detallado 211 6.1 Introducción 212 6.2 Módulos de programas en Java 212 6.3 Métodos static, campos static y la clase Math 214 6.4 Declaración de métodos con múltiples parámetros 216 6.5 Notas acerca de cómo declarar y utilizar los métodos 219 6.6 Pila de llamadas a los métodos y registros de activación 221 6.7 Promoción y conversión de argumentos 221
  • 11. Contenido ix 6.8 Paquetes de la API de Java 222 6.9 Ejemplo práctico: generación de números aleatorios 224 6.9.1 Escalamiento y desplazamiento generalizados de números aleatorios 227 6.9.2 Repetitividad de números aleatorios para prueba y depuración 228 6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones) 228 6.11 Alcance de las declaraciones 232 6.12 Sobrecarga de métodos 235 6.13 (Opcional) Ejemplo práctico de GUI y gráficos: colores y figuras rellenas 238 6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones de las clases 241 6.15 Conclusión 246 7 Arreglos 260 7.1 Introducción 261 7.2 Arreglos 261 7.3 Declaración y creación de arreglos 262 7.4 Ejemplos acerca del uso de los arreglos 264 7.5 Ejemplo práctico: simulación para barajar y repartir cartas 272 7.6 Instrucción for mejorada 274 7.7 Paso de arreglos a los métodos 276 7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones 279 7.9 Arreglos multidimensionales 284 7.10 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo bidimensional 288 7.11 Listas de argumentos de longitud variable 293 7.12 Uso de argumentos de línea de comandos 294 7.13 (Opcional) Ejemplo práctico de GUI y gráficos: cómo dibujar arcos 296 7.14 (Opcional) Ejemplo práctico de Ingeniería de Software: colaboración entre los objetos 299 7.15 Conclusión 305 8 Clases y objetos: un análisis más detallado 325 8.1 Introducción 326 8.2 Ejemplo práctico de la clase Tiempo 327 8.3 Control del acceso a los miembros 330 8.4 Referencias a los miembros del objeto actual mediante this 331 8.5 Ejemplo práctico de la clase Tiempo: constructores sobrecargados 333 8.6 Constructores predeterminados y sin argumentos 338 8.7 Observaciones acerca de los métodos Establecer y Obtener 338 8.8 Composición 340 8.9 Enumeraciones 342 8.10 Recolección de basura y el método finalize 345 8.11 Miembros de clase static 345 8.12 Declaración static import 350 8.13 Variables de instancia final 351 8.14 Reutilización de software 353 8.15 Abstracción de datos y encapsulamiento 354 8.16 Ejemplo práctico de la clase Tiempo: creación de paquetes 355 8.17 Acceso a paquetes 360 8.18 (Opcional) Ejemplo práctico de GUI y gráficos: uso de objetos con gráficos 361 8.19 (Opcional) Ejemplo práctico de Ingeniería de Software: inicio de la programación de las clases del sistema ATM 364 8.20 Conclusión 369
  • 12. x Contenido 9 Programación orientada a objetos: herencia 378 9.1 Introducción 379 9.2 Superclases y subclases 380 9.3 Miembros protected 382 9.4 Relación entre las superclases y las subclases 382 9.4.1 Creación y uso de una clase EmpleadoPorComision 383 9.4.2 Creación de una clase EmpleadoBaseMasComision sin usar la herencia 387 9.4.3 Creación de una jerarquía de herencia EmpleadoPorComision- EmpleadoBaseMasComision 391 9.4.4 La jerarquía de herencia EmpleadoPorComision-EmpleadoBaseMasComision mediante el uso de variables de instancia protected 394 9.4.5 La jerarquía de herencia EmpleadoPorComision-EmpleadoBaseMasComision mediante el uso de variables de instancia private 399 9.5 Los constructores en las subclases 404 9.6 Ingeniería de software mediante la herencia 409 9.7 La clase object 410 9.8 (Opcional) Ejemplo práctico de GUI y gráficos: mostar texto e imágenes usando etiquetas 411 9.9 Conclusión 413 10 Programación orientada a objetos: polimorfismo 417 10.1 Introducción 418 10.2 Ejemplos del polimorfismo 419 10.3 Demostración del comportamiento polimórfico 420 10.4 Clases y métodos abstractos 423 10.5 Ejemplo práctico: sistema de nómina utilizando polimorfismo 425 10.5.1 Creación de la superclase abstracta Empleado 426 10.5.2 Creación de la subclase concreta EmpleadoAsalariado 426 10.5.3 Creación de la subclase concreta EmpleadoPorHoras 429 10.5.4 Creación de la subclase concreta EmpleadoPorComision 431 10.5.5 Creación de la subclase concreta indirecta EmpleadoBaseMasComision 432 10.5.6 Demostración del procesamiento polimórfico, el operador instanceof y la conversión descendente 433 10.5.7 Resumen de las asignaciones permitidas entre variables de la superclase y de la subclase 437 10.6 Métodos y clases final 438 10.7 Ejemplo práctico: creación y uso de interfaces 439 10.7.1 Desarrollo de una jerarquía PorPagar 440 10.7.2 Declaración de la interfaz PorPagar 441 10.7.3 Creación de la clase Factura 441 10.7.4 Modificación de la clase Empleado para implementar la interfaz PorPagar 443 10.7.5 Modificación de la clase EmpleadoAsalariado para usarla en la jerarquía PorPagar 445 10.7.6 Uso de la interfaz PorPagar para procesar objetos Factura y Empleado mediante el polimorfismo 446 10.7.7 Declaración de constantes con interfaces 448 10.7.8 Interfaces comunes de la API de Java 448 10.8 (Opcional) Ejemplo práctico de GUI y gráficos: realizar dibujos mediante el polimorfismo 449 10.9 (Opcional) Ejemplo práctico de Ingeniería de Software: incorporación de la herencia en el sistema ATM 451 10.10 Conclusión 457
  • 13. Contenido xi 11 Componentes de la GUI: parte 1 462 11.1 Introducción 463 11.2 Entrada/salida simple basada en GUI con JOptionPane 464 11.3 Generalidades de los componentes de Swing 467 11.4 Mostrar texto e imágenes en una ventana 469 11.5 Campos de texto y una introducción al manejo de eventos con clases anidadas 474 11.6 Tipos de eventos comunes de la GUI e interfaces de escucha 479 11.7 Cómo funciona el manejo de eventos 481 11.8 JButton 483 11.9 Botones que mantienen el estado 486 11.9.1 JCheckBox 486 11.9.2 JRadioButton 489 11.10 JComboBox y el uso de una clase interna anónima para el manejo de eventos 492 11.11 JList 495 11.12 Listas de selección múltiple 497 11.13 Manejo de eventos de ratón 500 11.14 Clases adaptadoras 504 11.15 Subclase de JPanel para dibujar con el ratón 507 11.16 Manejo de eventos de teclas 510 11.17 Administradores de esquemas 513 11.17.1 FlowLayout 514 11.17.2 BorderLayout 517 11.17.3 GridLayout 520 11.18 Uso de paneles para administrar esquemas más complejos 522 11.19 JTextArea 523 11.20 Conclusión 526 12 Gráficos y Java 2D™ 539 12.1 Introducción 540 12.2 Contextos y objetos de gráficos 542 12.3 Control de colores 542 12.4 Control de tipos de letra 548 12.5 Dibujo de líneas, rectángulos y óvalos 554 12.6 Dibujo de arcos 558 12.7 Dibujo de polígonos y polilíneas 560 12.8 La API Java 2D 563 12.9 Conclusión 569 13 Manejo de excepciones 578 13.1 Introducción 579 13.2 Generalidades acerca del manejo de excepciones 580 13.3 Ejemplo: división entre cero sin manejo de excepciones 580 13.4 Ejemplo: manejo de excepciones tipo ArithmeticException e InputMismatchException 582 13.5 Cuándo utilizar el manejo de excepciones 587 13.6 Jerarquía de excepciones en Java 587 13.7 Bloque finally 590 13.8 Limpieza de la pila 594 13.9 printStackTrace, getStackTrace y getMessage 595 13.10 Excepciones encadenadas 597 13.11 Declaración de nuevos tipos de excepciones 599 13.12 Precondiciones y poscondiciones 600
  • 14. xii Contenido 13.13 Aserciones 601 13.14 Conclusión 602 14 Archivos y flujos 608 14.1 Introducción 609 14.2 Jerarquía de datos 610 14.3 Archivos y flujos 611 14.4 La clase File 613 14.5 Archivos de texto de acceso secuencial 617 14.5.1 Creación de un archivo de texto de acceso secuencial 617 14.5.2 Cómo leer datos de un archivo de texto de acceso secuencial 623 14.5.3 Ejemplo práctico: un programa de solicitud de crédito 625 14.5.4 Actualización de archivos de acceso secuencial 630 14.6 Serialización de objetos 630 14.6.1 Creación de un archivo de acceso secuencial mediante el uso de la serialización de objetos 631 14.6.2 Lectura y deserialización de datos de un archivo de acceso secuencial 636 14.7 Clases adicionales de java.io 638 14.8 Abrir archivos con JFileChooser 640 14.9 Conclusión 643 15 Recursividad 653 15.1 Introducción 654 15.2 Conceptos de recursividad 655 15.3 Ejemplo de uso de recursividad: factoriales 655 15.4 Ejemplo de uso de recursividad: serie de Fibonacci 658 15.5 La recursividad y la pila de llamadas a métodos 661 15.6 Comparación entre recursividad e iteración 662 15.7 Las torres de Hanoi 664 15.8 Fractales 666 15.9 “Vuelta atrás” recursiva (backtracking) 676 15.10 Conclusión 676 15.11 Recursos en Internet y Web 676 16 Búsqueda y ordenamiento 685 16.1 Introducción 686 16.2 Algoritmos de búsqueda 687 16.2.1 Búsqueda lineal 687 16.2.2 Búsqueda binaria 690 16.3 Algoritmos de ordenamiento 695 16.3.1 Ordenamiento por selección 695 16.3.2 Ordenamiento por inserción 699 16.3.3 Ordenamiento por combinación 702 16.4 Invariantes 708 16.5 Conclusión 709 17 Estructuras de datos 714 17.1 Introducción 715 17.2 Clases de envoltura de tipos para los tipos primitivos 716 17.3 Autoboxing y autounboxing 716
  • 15. Contenido xiii 17.4 Clases autorreferenciadas 717 17.5 Asignación dinámica de memoria 717 17.6 Listas enlazadas 718 17.7 Pilas 726 17.8 Colas 730 17.9 Árboles 733 17.10 Conclusión 739 18 Genéricos 761 18.1 Introducción 762 18.2 Motivación para los métodos genéricos 762 18.3 Métodos genéricos: implementación y traducción en tiempo de compilación 764 18.4 Cuestiones adicionales sobre la traducción en tiempo de compilación: métodos que utilizan un parámetro de tipo como tipo de valor de retorno 767 18.5 Sobrecarga de métodos genéricos 770 18.6 Clases genéricas 770 18.7 Tipos crudos (raw) 779 18.8 Comodines en métodos que aceptan parámetros de tipo 783 18.9 Genéricos y herencia: observaciones 787 18.10 Conclusión 787 18.11 Recursos en Internet y Web 787 19 Colecciones 792 19.1 Introducción 793 19.2 Generalidades acerca de las colecciones 794 19.3 La clase Arrays 794 19.4 La interfaz Collection y la clase Collections 797 19.5 Listas 798 19.5.1 ArrayList e Iterator 799 19.5.2 LinkedList 800 19.5.3 Vector 805 19.6 Algoritmos de las colecciones 808 19.6.1 El algoritmo sort 809 19.6.2 El algoritmo shuffle 812 19.6.3 Los algoritmos reverse, fill, copy, max y min 815 19.6.4 El algoritmo binarySearch 816 19.6.5 Los algoritmos addAll, frequency y disjoint 818 19.7 La clase Stack del paquete java.util 820 19.8 La clase PriorityQueue y la interfaz Queue 822 19.9 Conjuntos 823 19.10 Mapas 826 19.11 La clase Properties 829 19.12 Colecciones sincronizadas 832 19.13 Colecciones no modificables 833 19.14 Implementaciones abstractas 834 19.15 Conclusión 834 20 Introducción a los applets de Java 841 20.1 Introducción 842 20.2 Applets de muestra incluidos en el JDK 842 20.3 Applet simple en Java: cómo dibujar una cadena 846
  • 16. xiv Contenido 20.3.1 Cómo ejecutar un applet en el appletviewer 848 20.3.2 Ejecución de un applet en un explorador Web 850 20.4 Métodos del ciclo de vida de los applets 850 20.5 Cómo inicializar una variable de instancia con el método int 851 20.6 Modelo de seguridad “caja de arena” 853 20.7 Recursos en Internet y Web 853 20.8 Conclusión 854 21 Multimedia: applets y aplicaciones 858 21.1 Introducción 859 21.2 Cómo cargar, mostrar y escalar imágenes 860 21.3 Animación de una serie de imágenes 862 21.4 Mapas de imágenes 867 21.5 Carga y reproducción de clips de audio 869 21.6 Reproducción de video y otros medios con el Marco de trabajo de medios de Java 872 21.7 Conclusión 876 21.8 Recursos Web 876 22 Componentes de la GUI: parte 2 883 22.1 Introducción 884 22.2 JSlider 884 22.3 Ventanas: observaciones adicionales 888 22.4 Uso de menús con marcos 889 22.5 JPopupMenu 896 22.6 Apariencia visual adaptable 899 22.7 JDesktopPane y JInternalFrame 903 22.8 JTabbedPane 906 22.9 Administradores de esquemas: BoxLayout y GridBagLayout 908 22.10 Conclusión 920 23 Subprocesamiento múltiple 925 23.1 Introducción 926 23.2 Estados de los subprocesos: ciclo de vida de un subproceso 927 23.3 Prioridades y programación de subprocesos 929 23.4 Creación y ejecución de subprocesos 931 23.4.1 Objetos Runnable y la clase Thread 931 23.4.2 Administración de subprocesos con el marco de trabajo Executor 934 23.5 Sincronización de subprocesos 935 23.5.1 Cómo compartir datos sin sincronización 936 23.5.2 Cómo compartir datos con sincronización: hacer las operaciones atómicas 940 23.6 Relación productor/consumidor sin sincronización 943 23.7 Relación productor/consumidor: ArrayBlockingQueue 949 23.8 Relación productor/consumidor con sincronización 952 23.9 Relación productor/consumidor: búferes delimitados 957 23.10 Relación productor/consumidor: las interfaces Lock y Condition 964 23.11 Subprocesamiento múltiple con GUIs 970 23.11.1 Realización de cálculos en un subproceso trabajador 970 23.11.2 Procesamiento de resultados inmediatos con SwingWorker 976 23.12 Otras clases e interfaces en java.util.concurrent 982 23.13 Conclusión 983
  • 17. Contenido xv 24 Redes 992 24.1 Introducción 993 24.2 Manipulación de URLs 994 24.3 Cómo leer un archivo en un servidor Web 998 24.4 Cómo establecer un servidor simple utilizando sockets de flujo 1001 24.5 Cómo establecer un cliente simple utilizando sockets de flujo 1003 24.6 Interacción entre cliente/servidor mediante conexiones de socket de flujo 1004 24.7 Interacción entre cliente/servidor sin conexión mediante datagramas 1014 24.8 Juego de Tres en raya (Gato) tipo cliente/servidor, utilizando un servidor con subprocesamiento múltiple 1021 24.9 La seguridad y la red 1034 24.10 [Bono Web] Ejemplo práctico: servidor y cliente DeitelMessenger 1034 24.11 Conclusión 1035 25 Acceso a bases de datos con JDBC 1041 25.1 Introducción 1042 25.2 Bases de datos relacionales 1043 25.3 Generalidades acerca de las bases de datos relacionales: la base de datos libros 1044 25.4 SQL 1047 25.4.1 Consulta básica SELECT 1047 25.4.2 La cláusula WHERE 1048 25.4.3 La cláusula ORDER BY 1050 25.4.4 Cómo fusionar datos de varias tablas: INNER JOIN 1051 25.4.5 La instrucción INSERT 1053 25.4.6 La instrucción UPDATE 1053 25.4.7 La instrucción DELETE 1054 25.5 Instrucciones para instalar MySQL y MySQL Connector/J 1055 25.6 Instrucciones para establecer una cuenta de usuario de MySQL 1056 25.7 Creación de la base de datos libros en MySQL 1057 25.8 Manipulación de bases de datos con JDBC 1057 25.8.1 Cómo conectarse y realizar consultas en una base de datos 1057 25.8.2 Consultas en la base de datos libros 1062 25.9 La interfaz RowSet 1073 25.10 Java DB/Apache Derby 1075 25.11 Objetos PreparedStatement 1076 25.12 Procedimientos almacenados 1090 25.13 Procesamiento de transacciones 1091 25.14 Conclusión 1091 25.15 Recursos Web y lecturas recomendadas 1092 Los capítulos 26 a 30 así como los apéndices, los encontrará en el CD que acompaña este libro. 26 Aplicaciones Web: parte 1 1101 26.1 Introducción 1102 26.2 Transacciones HTTP simples 1103 26.3 Arquitectura de aplicaciones multinivel 1105 26.4 Tecnologías Web de Java 1106 26.4.1 Servlets 1106 26.4.2 JavaServer Pages 1106 26.4.3 JavaServer Faces 1107 26.4.4 Tecnologías Web en Java Studio Creator 2 1108
  • 18. xvi Contenido 26.5 Creación y ejecución de una aplicación simple en Java Studio Creator 2 1108 26.5.1 Análisis de un archivo JSP 1109 26.5.2 Análisis de un archivo de bean de página 1111 26.5.3 Ciclo de vida del procesamiento de eventos 1115 26.5.4 Relación entre la JSP y los archivos de bean de página 1115 26.5.5 Análisis del XHTML generado por una aplicación Web de Java 1115 26.5.6 Creación de una aplicación Web en Java Studio Creator 2 1117 26.6 Componentes JSF 1123 26.6.1 Componentes de texto y gráficos 1123 26.6.2 Validación mediante los componentes de validación y los validadores personalizados 1128 26.7 Rastreo de sesiones 1137 26.7.1 Cookies 1138 26.7.2 Rastreo de sesiones con el objeto SessionBean 1150 26.8 Conclusión 1162 26.9 Recursos Web 1163 27 Aplicaciones Web: parte 2 1173 27.1 Introducción 1174 27.2 Acceso a bases de datos en las aplicaciones Web 1174 27.2.1 Creación de una aplicación Web que muestra datos de una base de datos 1175 27.2.2 Modificación del archivo de bean de página para la aplicación LibretaDirecciones 1183 27.3 Componentes JSF habilitados para Ajax 1185 27.3.1 Biblioteca de componentes Java BluePrints 1186 27.4 Autocomplete Text Field y formularios virtuales 1187 27.4.1 Configuración de los formularios virtuales 1187 27.4.2 Archivo JSP con formularios virtuales y un AutoComplete Text Field 1189 27.4.3 Cómo proporcionar sugerencias para un AutoComplete Text Field 1192 27.5 Componente Map Viewer de Google Maps 1196 27.5.1 Cómo obtener una clave de la API Google Maps 1196 27.5.2 Cómo agregar un componente y un Map Viewer a una página 1196 27.5.3 Archivo JSP con un componente Map Viewer 1197 27.5.4 Bean de página que muestra un mapa en el componente Map Viewer 1201 27.6 Conclusión 1206 27.7 Recursos Web 1206 28 Servicios Web JAX-WS, Web 2.0 y Mash-ups 1212 28.1 Introducción 1213 28.1.1 Descarga, instalación y configuración de Netbeans 5.5 y Sun Java System Application Server 1214 28.1.2 Centro de recursos de servicios Web y Centros de recursos sobre Java en www.deitel.com 1215 28.2 Fundamentos de los servicios Web de Java 1215 28.3 Creación, publicación, prueba y descripción de un servicio Web 1216 28.3.1 Creación de un proyecto de aplicación Web y cómo agregar una clase de servicio Web en Netbeans 1216 28.3.2 Definición del servicio Web EnteroEnorme en Netbeans 1217 28.3.3 Publicación del servicio Web EnteroEnorme desde Netbeans 1221 28.3.4 Prueba del servicio Web EnteroEnorme con la página Web Tester de Sun Java System Application Server 1222 28.3.5 Descripción de un servicio Web con el Lenguaje de descripción de servicios Web (WSDL) 1224
  • 19. Contenido xvii 28.4 Cómo consumir un servicio Web 1224 28.4.1 Creación de un cliente para consumir el servicio Web EnteroEnorme 1225 28.4.2 Cómo consumir el servicio Web EnteroEnorme 1227 28.5 SOAP 1234 28.6 Rastreo de sesiones en los servicios Web 1234 28.6.1 Creación de un servicio Web Blackjack 1235 28.6.2 Cómo consumir el servicio Web Blackjack 1239 28.7 Cómo consumir un servicio Web controlado por base de datos desde una aplicación Web 1249 28.7.1 Configuración de Java DB en Netbeans y creación de la base de datos Reservacion 1249 28.7.2 Creación de una aplicación Web para interactuar con el servicio Web Reservacion 1253 28.8 Cómo pasar un objeto de un tipo definido por el usuario a un servicio Web 1258 28.9 Conclusión 1266 28.10 Recursos Web 1267 29 Salida con formato 1275 29.1 Introducción 1276 29.2 Flujos 1276 29.3 Aplicación de formato a la salida con printf 1276 29.4 Impresión de enteros 1277 29.5 Impresión de números de punto flotante 1278 29.6 Impresión de cadenas y caracteres 1279 29.7 Impresión de fechas y horas 1280 29.8 Otros caracteres de conversión 1283 29.9 Impresión con anchuras de campo y precisiones 1284 29.10 Uso de banderas en la cadena de formato de printf 1285 29.11 Impresión con índices como argumentos 1289 29.12 Impresión de literales y secuencias de escape 1290 29.13 Aplicación de formato a la salida con la clase Formatter 1290 29.14 Conclusión 1291 30 Cadenas, caracteres y expresiones regulares 1297 30.1 Introducción 1298 30.2 Fundamentos de los caracteres y las cadenas 1298 30.3 La clase String 1299 30.3.1 Constructores de String 1299 30.3.2 Métodos length, charAt y getChars de String 1300 30.3.3 Comparación entre cadenas 1301 30.3.4 Localización de caracteres y subcadenas en las cadenas 1305 30.3.5 Extracción de subcadenas de las cadenas 1307 30.3.6 Concatenación de cadenas 1308 30.3.7 Métodos varios de String 1308 30.3.8 Método valueOf de String 1309 30.4 La clase StringBuilder 1311 30.4.1 Constructores de StringBuilder 1311 30.4.2 Métodos length, capacity, setLength y ensureCapacity de StringBuilder 1312 30.4.3 Métodos charAt, setCharAt, getChars y reverse de StringBuilder 1313 30.4.4 Métodos append de StringBuilder 1314 30.4.5 Métodos de inserción y eliminación de StringBuilder 1316 30.5 La clase Character 1317 30.6 La clase StringTokenizer 1321 30.7 Expresiones regulares, la clase Pattern y la clase Matcher 1322 30.8 Conclusión 1330
  • 20. xviii Contenido A Tabla de precedencia de los operadores 1340 B Conjunto de caracteres ASCII 1342 C Palabras clave y palabras reservadas 1343 D Tipos primitivos 1344 E Sistemas numéricos 1345 E.1 Introducción 1346 E.2 Abreviatura de los números binarios como números octales y hexadecimales 1348 E.3 Conversión de números octales y hexadecimales a binarios 1349 E.4 Conversión de un número binario, octal o hexadecimal a decimal 1350 E.5 Conversión de un número decimal a binario, octal o hexadecimal 1351 E.6 Números binarios negativos: notación de complemento a dos 1352 F GroupLayout 1357 F.1 Introducción 1357 F.2 Fundamentos de GroupLayout 1357 F.3 Creación de un objeto SelectorColores 1358 F.4 Recursos Web sobre GroupLayout 1367 G Componentes de integración Java Desktop (JDIC) 1368 G.1 Introducción 1368 G.2 Pantallas de inicio 1368 G.3 La clase Desktop 1370 G.4 Iconos de la bandeja 1371 G.5 Proyectos JDIC Incubator 1373 G.6 Demos de JDIC 1373 H Mashups 1374 Índice 1381
  • 21. “No vivas más en fragmentos, sólo conéctate”. —Edgar Morgan Foster ¡Bienvenido a Java y Cómo programar en Java, 7ª edición! En Deitel & Associates escribimos para Prentice Hall libros de texto sobre lenguajes de programación y libros de nivel profesional, impartimos capacitación a empresas en todo el mundo y desarrollamos negocios en Internet. Fue un placer escribir esta edición ya que refleja cam- bios importantes en el lenguaje Java y en las formas de impartir y aprender programación. Se han realizado ajustes considerables en todos los capítulos. Características nuevas y mejoradas He aquí una lista de las actualizaciones que hemos realizado a la 6ª y 7ª ediciones: Actualizamos todo el libro a la nueva plataforma Java Standard Edition 6 (“Mustang”) y lo revisamos cuidadosamente, en base a la Especificación del lenguaje Java. Revisamos la presentación conforme a las recomendaciones del currículum de ACM/IEEE. Reforzamos nuestra pedagogía anticipada sobre las clases y los objetos, poniendo especial atención a la orientación de los profesores universitarios en nuestros equipos de revisión, para asegurarnos de obtener el nivel conceptual correcto. Todo el libro está orientado a objetos, y las explicaciones sobre la POO son claras y accesibles. En el capítulo 1 presentamos los conceptos básicos y la terminología de la tecnología de objetos. Los estudiantes desarrollan sus primeras clases y objetos personalizados en el capítulo 3. Al presentar los objetos y las clases en los primeros capítulos, hacemos que los estudiantes “piensen acerca de objetos” de inmediato, y que dominen estos conceptos con más profundidad. La primera presentación de clases y objetos incluye los ejemplos prácticos de las clases Tiempo, Empleado y LibroCalificaciones, los cuales van haciendo su propio camino a través de varias secciones y capítu- los, presentando conceptos de OO cada vez más profundos. Los profesores que imparten cursos introductorios tienen una amplia opción en cuanto a la cantidad de GUI y gráficos a cubrir; desde cero, a una secuencia introductoria de diez secciones breves, hasta un tratamiento detallado en los capítulos 11, 12 y 22, y en el apéndice F. Adaptamos nuestra presentación orientada a objetos para utilizar la versión más reciente de UML™ (Lenguaje Unificado de Modelado™): UML™ 2, el lenguaje gráfico estándar en la industria para modelar sistemas orientados a objetos. En los capítulos 1-8 y 10 presentamos y adaptamos el ejemplo práctico opcional del cajero automático (ATM) de DOO/UML 2. Incluimos un apéndice Web adicional, con la implementación completa del código. Dé un vistazo a los testimonios que se incluyen en la parte posterior del libro. Agregamos varios ejemplos prácticos sustanciales sobre programación Web orientada a objetos. Actualizamos el capítulo 25, Acceso a bases de datos con JDBC, para incluir JDBC 4 y utilizar el nuevo sistema de administración de bases de datos Java DB/Apache Derby, además de MySQL. Este capítulo incluye un ejemplo práctico OO sobre el desarrollo de una libreta de direcciones controlada por una base de datos, la cual demuestra las instrucciones preparadas y el descubrimiento automático de contro- ladores de JDBC 4. Agregamos los capítulos 26 y 27, Aplicaciones Web: partes 1 y 2, que introducen la tecnología Java- Server Faces (JSF) y la utilizan con Sun Java Studio Creador 2 para construir aplicaciones Web de una manera rápida y sencilla. El capítulo 26 incluye ejemplos sobre la creación de GUIs de aplicaciones Web, • • • • • • • • • • Prefacio
  • 22. el manejo de eventos, la validación de formularios y el rastreo de sesiones. El material de JSF sustituye los capítulos anteriores sobre servlets y JavaServer Pages (JSP). Agregamos el capítulo 27, Aplicaciones Web: parte 2, que habla acerca del desarrollo de aplicaciones Web habilitadas para Ajax, usando las tecnologías JavaServer Faces y Java BluePrints. Este capítulo incluye una aplicación de libreta de direcciones Web multiniveles, controlada por una base de datos, que permite a los usuarios agregar y buscar contactos, y mostrar las direcciones de los contactos en mapas de Google™ Maps. Esta aplicación habilitada para Ajax le proporciona una sensación real del desarrollo Web 2.0. La aplicación utiliza Componentes JSF habilitados para Ajax para sugerir los nombres de los contactos, mientras el usuario escribe un nombre para localizar y mostrar una dirección localizada en un mapa de Google Maps. Agregamos el capítulo 28, Servicios Web JAX-WS, Web 2.0 y Mash-ups que utiliza un método basado en herramientas para crear y consumir servicios Web, una capacidad típica de Web 2.0. Los ejemplos prácticos incluyen el desarrollo de los servicios Web del juego de blackjack y un sistema de reservaciones de una aerolínea. Utilizamos el nuevo método basado en herramientas para desarrollar aplicaciones Web con rapidez; todas las herramientas pueden descargarse sin costo. Fundamos la Iniciativa Deitel de Negocios por Internet (Deitel Internet Business Initiative) con 60 nue- vos centros de recursos para apoyar a nuestros lectores académicos y profesionales. Dé un vistazo a nues- tros nuevos centros de recursos (www.deitel.com/resourcecenters.html), incluyendo: Java SE 6 (Mustang), Java, Evaluación y Certificación de Java, Patrones de Diseño de Java, Java EE 5, Motores de Búsqueda de Código y Sitios de Código, Programación de Juegos, Proyectos de Programación y muchos más. Regístrese en el boletín de correo electrónico gratuito Deitel® Buzz Online (www.deitel. com/newsletter/subscribe.html); cada semana anunciamos nuestro(s) centro(s) de recurso(s) más reciente(s); además incluimos otros temas de interés para nuestros lectores. Hablamos sobre los conceptos clave de la comunidad de ingeniería de software, como Web 2.0, Ajax, SOA, servicios Web, software de código fuente abierto, patrones de diseño, mashups, refabricación, programación extrema, desarrollo ágil de software, prototipos rápidos y mucho más. Rediseñamos por completo el capítulo 23, Subprocesamiento múltiple [nuestro agradecimiento especial a Brian Goetz y Joseph Bowbeer, coautores de Java Concurrency in Practice, Addison-Wesley, 2006]. Hablamos sobre la nueva clase SwingWorker para desarrollar interfaces de usuario con subprocesamien- to múltiple. Hablamos sobre los nuevos Componentes de Integración de Escritorio de Java (JDIC), como las panta- llas de inicio (splash screens) y las interacciones con la bandeja del sistema. Hablamos sobre el nuevo administrador de esquemas GroupLayout en el contexto de la herramienta de diseño de GUI NetBeans 5.5 Matisse para crear GUIs portables que se adhieran a los lineamientos de diseño de GUI de la plataforma subyacente. Presentamos las nuevas características de ordenamiento y filtrado de JTable, que permiten al usuario reordenar los datos en un objeto JTable y filtrarlos mediante expresiones regulares. Presentamos un tratamiento detallado de los genéricos y las colecciones de genéricos. Introducimos los mashups, aplicaciones que, por lo general, se crean mediante llamadas a servicios Web (y/o usando fuentes RSS) de dos o más sitios; otra característica típica de Web 2.0. Hablamos sobre la nueva clase StringBuilder, que tiene un mejor desempeño que StringBuffer en aplicaciones sin subprocesamiento. Presentamos las anotaciones, que reducen en gran parte la cantidad de código necesario para crear apli- caciones. Las características que se presentan en Cómo programar en Java, 7a edición, incluyen: Cómo obtener entrada con formato mediante la clase Scanner. Mostrar salida con formato mediante el método printf del objeto System.out. • • • • • • • • • • • • • • • • xx Prefacio
  • 23. Instrucciones for mejoradas para procesar elementos de arreglos y colecciones. Declaración de métodos con listas de argumentos de longitud variable (“varargs”). Uso de clases enum que declaran conjuntos de constantes. Importación de los miembros static de una clase para usarlos en otra. Conversión de valores de tipo primitivo a objetos de envolturas de tipo y viceversa, usando autoboxing y auto-unboxing, respectivamente. Uso de genéricos para crear modelos generales de métodos y clases que pueden declararse una vez, pero usarse con muchos tipos de datos distintos. Uso de las estructuras de datos mejoradas para genéricos de la API Collections. Uso de la API Concurrency para implementar aplicaciones con subprocesamiento múltiple. Uso de objetos RowSet de JDBC para acceder a los datos en una base de datos. Todo esto ha sido revisado cuidadosamente por distinguidos profesores y desarrolladores de la industria, que trabajaron con nosotros en Cómo programar en Java 6ª y 7ª ediciones. Creemos que este libro y sus materiales de apoyo proporcionarán a los estudiantes y profesionales una expe- riencia informativa, interesante, retadora y placentera. El libro incluye una extensa suite de materiales comple- mentarios para ayudar a los profesores a maximizar la experiencia de aprendizaje de sus estudiantes. Cómo programar en Java 7ª edición presenta cientos de programas completos y funcionales, y describe sus entradas y salidas. Éste es nuestro característico método de “código activo” (“live code”); presentamos la mayoría de los conceptos de programación de Java en el contexto de programas funcionales completos. Si surge alguna duda o pregunta a medida que lee este libro, envíe un correo electrónico a deitel@deitel. com; le responderemos a la brevedad. Para obtener actualizaciones sobre este libro y el estado de todo el software de soporte de Java, además de las noticias más recientes acerca de todas las publicaciones y servicios de Deitel, visite www.deitel.com. Regístrese en www.deitel.com/newsletter/subscribe.html para obtener el boletín de correo electrónico Deitel® Buzz Online y visite la página www.deitel.com/resourcecenters.html para tener acceso a nuestra lista creciente de centros de recursos. Uso de UML 2 para desarrollar un diseño orientado a objetos de un ATM. UML 2 se ha convertido en el lenguaje de modelado gráfico preferido para diseñar sistemas orientados a objetos. Todos los diagramas de UML en el libro cumplen con la especificación UML 2. Utilizamos los diagramas de actividad de UML para demostrar el flujo de control en cada una de las instrucciones de control de Java, y usamos los diagramas de clases de UML para representar las clases y sus relaciones de herencia en forma visual. Incluimos un ejemplo práctico opcional (pero altamente recomendado) acerca del diseño orientado a objetos mediante el uso de UML. La revisión del ejemplo práctico estuvo a cargo de un distinguido equipo de profesores y profesionales de la industria relacionados con DOO/UML, incluyendo líderes en el campo de Rational (los creadores de UML) y el Grupo de administración de objetos (responsable de la evolución de UML). En el ejem- plo práctico, diseñamos e implementamos por completo el software para un cajero automático (ATM) simple. Las secciones Ejemplo práctico de Ingeniería de Software al final de los capítulos 1 a 8 y 10 presentan una introducción cuidadosamente planeada al diseño orientado a objetos mediante el uso de UML. Presentamos un subconjunto conciso y simplificado de UML 2, y después lo guiamos a través de su primera experiencia de dise- ño, ideada para los principiantes. El ejemplo práctico no es un ejercicio, sino una experiencia de aprendizaje de principio a fin, que concluye con un recorrido detallado a través del código completo en Java. Las secciones del Ejemplo Práctico de Ingeniería de Software ayudan a los estudiantes a desarrollar un diseño orientado a objetos para complementar los conceptos de programación orientada a objetos que empiezan a aprender en el capítulo 1, y que implementan en el capítulo 3. En la primera de estas secciones, al final del capítulo 1, introducimos los conceptos básicos y la terminología del DOO. En las secciones opcionales Ejemplo Práctico de Ingeniería de Software al final de los capítulos 2 a 5, consideramos cuestiones más sustanciales al emprender la tarea de resolver un problema retador con las técnicas del DOO. Analizamos un documento de requerimientos típico que especi- fica un sistema a construir, determina los objetos necesarios para implementar ese sistema, establece los atributos que deben tener estos objetos, fija los comportamientos que deben exhibir estos objetos y especifica la forma en que deben interactuar los objetos entre sí para cumplir con los requerimientos del sistema. En un apéndice • • • • • • • • • Prefacio xxi
  • 24. Web adicional presentamos el código completo de una implementación en Java del sistema orientado a objetos que diseñamos en los primeros capítulos. El ejemplo práctico ayuda a preparar a los estudiantes para los tipos de proyectos sustanciales que encontrarán en la industria. Empleamos un proceso de diseño orientado a objetos cuidadosamente desarrollado e incremental para producir un modelo en UML 2 para nuestro sistema ATM. A partir de este diseño, producimos una implementación sustancial funcional en Java, usando las nociones clave de la programación orientada a objetos, incluyendo clases, objetos, encapsulamiento, visibilidad, composición, herencia y polimorfismo. Gráfico de dependencias En el gráfico de la siguiente página se muestra las dependencias entre los capítulos, para ayudar a los profesores a pla- near su programa de estudios. Cómo programar en Java 7ª edición es un libro extenso, apropiado para una variedad de cursos de programación en distintos niveles. Los capítulos 1-14 forman una secuencia de programación elemental accesible, con una sólida introducción a la programación orientada a objetos. Los capítulos 11, 12, 20, 21 y 22 for- man una secuencia sustancial de GUI, gráficos y multimedia. Los capítulos 15 a 19 forman una excelente secuencia de estructuras de datos. Los capítulos 24 a 28 forman una clara secuencia de desarrollo Web con uso intensivo de bases de datos. Método de enseñanza Cómo programar en Java 7ª edición contiene una extensa colección de ejemplos. El libro se concentra en los principios de la buena ingeniería de software, haciendo hincapié en la claridad de los programas. Enseñamos mediante ejemplos. Somos educadores que impartimos temas de vanguardia en salones de clases de la industria alrededor del mundo. El Dr. Harvey M. Deitel tiene 20 años de experiencia en la enseñanza universitaria y 17, en la enseñanza en la industria. Paul Deitel tiene 15 años de experiencia en la enseñanza en la industria. Juntos han impartido cursos, en todos los niveles, a clientes gubernamentales, industriales, militares y académicos de Deitel & Associates. Método del código activo. Cómo programar en Java 7ª edición está lleno de ejemplos de “código activo”; esto significa que cada nuevo concepto se presenta en el contexto de una aplicación en Java completa y funcional, que es seguido inmediatamente por una o más ejecuciones actuales, que muestran las entradas y salidas del programa. Este estilo ejemplifica la manera en que enseñamos y escribimos acerca de la programación; a éste le llamamos el método del “código activo”. Resaltado de código. Colocamos rectángulos de color gris alrededor de los segmentos de código clave en cada programa. Uso de fuentes para dar énfasis. Colocamos los términos clave y la referencia a la página del índice para cada ocurrencia de definición en texto en negritas para facilitar su referencia. Enfatizamos los componentes en pantalla en la fuente Helvética en negritas (por ejemplo, el menú Archivo) y enfatizamos el texto del programa en la fuente Lucida (por ejemplo, int x = 5). Acceso Web. Todos los ejemplos de código fuente para Cómo programar en Java 7ª edición (y para nuestras otras publicaciones) se pueden descargar en: www.deitel.com/books/jhtp7 www.pearsoneducacion.net/deitel El registro en el sitio es un proceso fácil y rápido. Descargue todos los ejemplos y, a medida que lea las correspon- dientes discusiones en el libro de texto, después ejecute cada programa. Realizar modificaciones a los ejemplos y ver los efectos de esos cambios es una excelente manera de mejorar su experiencia de aprendizaje en Java. Objetivos. Cada capítulo comienza con una declaración de objetivos. Esto le permite saber qué es lo que debe esperar y le brinda la oportunidad, después de leer el capítulo, de determinar si ha cumplido con ellos. Frases. Después de los objetivos de aprendizaje aparecen una o más frases. Algunas son graciosas, otras filosóficas y las demás ofrecen ideas interesantes. Esperamos que disfrute relacionando las frases con el material del capítulo. xxii Prefacio
  • 25. Plan general. El plan general de cada capítulo le permite abordar el material de manera ordenada, para poder anticiparse a lo que está por venir y establecer un ritmo cómodo y efectivo de aprendizaje. Ilustraciones/Figuras. Incluimos una gran cantidad de gráficas, tablas, dibujos lineales, programas y salidas de programa. Modelamos el flujo de control en las instrucciones de control mediante diagramas de actividad en 1 Introducción a las computadoras, Internet y Web 2 Introducción a las aplicaciones en Java 3 Introducción a las clases y los objetos 4 Instrucciones de control: parte 1 5 Instrucciones de control: parte 2 6 Métodos: Un análisis más detallado 10 Programación orientada a objetos: polimorfismo 8 Clases y objetos: un análisis más detallado 9 Programación orientada a objetos: herencia 11 Componentes de la GUI: parte 1 12 Gráficos y Java2D™ 25 Acceso a base de datos con JDBC1 13 Manejo de excepciones1 15 Recursividad3 18 Genéricos 24 Redes2 14 Archivos y flujos 23 Subprocesamiento múltiple4 7 Arreglos 16 Búsqueda y ordenamiento 21 Multimedia: applets y aplicaciones 22 Componentes de la GUI: parte 2 26 Aplicaciones Web: parte 1 27 Aplicaciones Web: parte 2 19 Colecciones 20 Introducción a los applets de Java 29 Salida con formato (la mayor parte) 30 Cadenas, caracteres y expresiones regulares 3.9 Uso de cuadros de diálogo (Opcional) Ruta de GUI y gráficos 4.14 Creación de dibujos simples 5.10 Dibujo de rectángulos y óvalos 6.13 Colores y figuras rellenas 7.13 Cómo dibujar arcos 8.18 Uso de objetos con gráficos 9.8 Mostrar texto e imágenes usando etiquetas 10.8 Realizar dibujos mediante el polimorfismo 17 Estructuras de datos 28 Servicios Web JAX-WS, Web 2.0 y Mash-ups 1. Los capítulos 13 y 25 dependen del capítulo 11 para la GUI que se utiliza en un ejemplo. 2. El capítulo 24 depende del capítulo 20 para un ejemplo que utiliza un applet. El ejemplo práctico extenso al final de este capítulo depende del capítulo 22 para la GUI y del capítulo 23 para el subprocesamiento múltiple. 3. El capítulo 15 depende de los capítulos 11 y 12 para la GUI y los gráficos que se utilizan en un ejemplo. 4. El capítulo 23 depende del capítulo 11 para la GUI que se utiliza en un ejemplo, y de los capítulos 18-19 para un ejemplo. Prefacio xxiii
  • 26. UML. Los diagramas de clases de UML modelan los campos, constructores y métodos de las clases. En el ejemplo práctico opcional del ATM de DOO/UML 2 hacemos uso extensivo de seis tipos principales de diagramas en UML. Tips de programación. Incluimos tips de programación para ayudarle a enfocarse en los aspectos importantes del desarrollo de programas. Estos tips y prácticas representan lo mejor que hemos podido recabar a lo largo de seis décadas combinadas de experiencia en la programación y la enseñanza. Una de nuestras alumnas, estudiante de matemáticas, recientemente nos comentó que siente que este método es similar al de resaltar axiomas, teoremas y corolarios en los libros de matemáticas, ya que proporciona una base sólida sobre la cual se puede construir buen software. Buena práctica de programación Las buenas prácticas de programación llaman la atención hacia técnicas que le ayudarán a producir programas más claros, comprensibles y fáciles de mantener. Error común de programación Con frecuencia, los estudiantes tienden a cometer ciertos tipos de errores; al poner atención en estos Errores comunes de programación se reduce la probabilidad de que usted pueda cometerlos. Tip para prevenir errores Estos tips contienen sugerencias para exponer los errores y eliminarlos de sus programas; muchos de ellos describen aspectos de Java que evitan que los errores entren a los programas. Tip de rendimiento A los estudiantes les gusta “turbo cargar” sus programas. Estos tips resaltan las oportunidades para hacer que sus programas se ejecuten más rápido, o para minimizar la cantidad de memoria que ocupan. Tip de portabilidad Incluimos Tips de portabilidad para ayudarle a escribir el código que pueda ejecutarse en una variedad de plata- formas, y que expliquen cómo es que Java logra su alto grado de portabilidad. Observación de ingeniería de software Las Observaciones de ingeniería de software resaltan los asuntos de arquitectura y diseño, lo cual afecta la cons- trucción de los sistemas de software, especialmente los de gran escala. Archivo Nuevo Abrir... Cerrar Observaciones de apariencia visual Le ofrecemos Observaciones de apariencia visual para resaltar las convenciones de la interfaz gráfica de usuario. Estas observaciones le ayudan a diseñar interfaces gráficas de usuario atractivas y amigables para el usuario, en con- formidad con las normas de la industria. Sección de conclusión. Cada uno de los capítulos termina con una sección breve de “conclusión”, que recapitula el contenido del capítulo y la transición al siguiente capítulo. Viñetas de resumen. Cada capítulo termina con estrategias pedagógicas adicionales. Presentamos un resumen detallado del capítulo, estilo lista con viñetas, sección por sección. Terminología. Incluimos una lista alfabetizada de los términos importantes definidos en cada capítulo. Ejercicios de autoevaluación y respuestas. Se incluyen diversos ejercicios de autoevaluación con sus respuestas, para que los estudiantes practiquen por su cuenta. xxiv Prefacio
  • 27. Ejercicios. Cada capítulo concluye con un diverso conjunto de ejercicios, incluyendo recordatorios simples de terminología y conceptos importantes; identificar los errores en muestras de código, escribir instrucciones in- dividuales de programas; escribir pequeñas porciones de métodos y clases en Java; escribir métodos, clases y programas completos; y crear proyectos finales importantes. El extenso número de ejercicios permite a los instructores adaptar sus cursos a las necesidades únicas de sus estudiantes, y variar las asignaciones de los cur- sos cada semestre. Los profesores pueden usar estos ejercicios para formar tareas, exámenes cortos, exámenes regulares y proyectos finales. [NOTA: No nos escriba para solicitarnos acceso al Centro de Recursos para Instructores. El acceso está limitado estrictamente a profesores universitarios que impartan clases en base al libro. Los profesores sólo pueden obtener acceso a través de los representantes de Pearson Educación]. Asegúrese de revisar nuestro centro de recursos de proyectos de programación (http://www.deitel.com/ ProgrammingProjects/) para obtener muchos ejercicios adicionales y posibilidades de proyectos. Miles de entradas en el índice. Hemos incluido un extenso índice, que es útil, en especial, cuando se utiliza el libro como referencia. “Doble indexado” de ejemplos de código activo de Java. Para cada programa de código fuente en el libro, indexamos la leyenda de la figura en forma alfabética y como subíndice, bajo “Ejemplos”. Esto facilita encontrar los ejemplos usando las características especiales. Recursos para el estudiante incluidos en Cómo programar en Java 7ª edición Hay, disponibles a la venta, una variedad de herramientas de desarrollo, pero ninguna de ellas es necesaria para comenzar a trabajar con Java. Escribimos Cómo programar en Java 7ª edición utilizando sólo el nuevo Kit de Desarrollo de Java Standard Edition (JDK), versión 6.0. Puede descargar la versión actual del JDK del sitio Web de Java de Sun: java.sun.com/javase/downloads/index.jsp. Este sitio también contiene las descargas de la documentación del JDK. El CD que se incluye con este libro contienen el Entorno de Desarrollo Integrado (IDE) NetBeans™ 5.5 para desarrollar todo tipo de aplicaciones en Java, y el software Sun Java™ Studio Creator 2 Update 1 para el desarrollo de aplicaciones Web. Se proporciona también una versión en Windows de MySQL® 5.0 Community Edition 5.0.27 y MySQL Connector/J 5.0.4, para el procesamiento de datos que se lleva a cabo en los capítulos 25 a 28. El CD también contiene los ejemplos del libro y una página Web con vínculos al sitio Web de Deitel & Associates, Inc. Puede cargar esta página Web en un explorador Web para obtener un rápido acceso a todos los recursos. Encontrará recursos adicionales y descargas de software en nuestro centro de recursos de Java SE 6 (Mus- tang), ubicado en: www.deitel.com/JavaSE6Mustang/ Java Multimedia Cyber Classroom 7ª edición Cómo programar en Java 7ª edición incluye multimedia interactiva con mucho audio y basada en Web, comple- mentaria para el libro Java Multimedia Cyber Classroom, 7ª edición, disponible en inglés. Nuestro Ciber salón de clases (Cyber Classroom) basado en Web incluye recorridos con audio de los ejemplos de código de los capítulos 1 a 14, soluciones a casi la mitad de los ejercicios del libro, un manual de laboratorio y mucho más. Para obtener más información acerca del Cyber Classroom basado en Web, visite: www.prenhall.com/deitel/cyberclassroom/ A los estudiantes que utilizan nuestros Ciber salones de clases les gusta su interactividad y capacidades de refe- rencia. Los profesores nos dicen que sus estudiantes disfrutan al utilizar el Ciber salón de clases y, en consecuencia, invierten más tiempo en los cursos, dominando un porcentaje mayor del material que en los cursos que sólo utilizan libros de texto. Prefacio xxv
  • 28. Recursos para el instructor de Cómo programar en Java 7ª edición Cómo programar en Java 7ª edición tiene una gran cantidad de recursos para los profesores. El Centro de Recursos para Instructores de Prentice Hall contiene el Manual de soluciones, con respuestas para la mayoría de los ejercicios al final de cada capítulo, un Archivo de elementos de prueba de preguntas de opción múltiple (aproximadamente dos por cada sección del libro) y diapositivas en PowerPoint® que contienen todo el código y las figuras del texto, además de los elementos en viñetas que sintetizan los puntos clave del libro. Los profesores pueden personalizar las diapositivas. Si usted todavía no es un miembro académico registrado, póngase en contacto con su represen- tante de Pearson Educación. Cabe mencionar que todos estos recursos se encuentran en inglés. Boletín de correo electrónico gratuito Deitel® Buzz Online Cada semana, el boletín de correo electrónico Deitel® Buzz Online anuncia nuestro(s) centro(s) de recursos más reciente(s) e incluye comentarios acerca de las tendencias y desarrollos en la industria, vínculos a artículos y recur- sos gratuitos de nuestros libros publicados y de las próximas publicaciones, itinerarios de lanzamiento de produc- tos, fe de erratas, retos, anécdotas, información sobre nuestros cursos de capacitación corporativa impartidos por instructores y mucho más. También es una buena forma para que usted se mantenga actualizado acerca de todo lo relacionado con Cómo programar en Java 7ª edición. Para suscribirse, visite la página Web: www.deitel.com/newsletter/subscribe.html Novedades en Deitel Centros de recursos y la iniciativa de negocios por Internet de Deitel. Hemos creado muchos centros de recursos en línea (en www.deitel.com/resourcecenters.html) para mejorar su experiencia de aprendizaje en Java. Anunciamos nuevos centros de recursos en cada edición del boletín de correo electrónico Deitel® Buzz Online. Aquellos de especial interés para los lectores de este libro incluyen: Java, Certificación en Java, Patrones de Diseño en Java, Java EE 5, Java SE 6, AJAX, Apache, Motores de Búsqueda de Código y Sitios de Código, Eclipse, Programación de Juegos, Mashups, MySQL, Código Abierto, Proyectos de Programación, Web2.0, Web 3.0, Servicios Web y XML. Los centros de recursos de Deitel adicionales incluyen: Programas Afiliados, Servicios de Alerta, ASP.NET, Economía de Atención, Creación de Comunidades Web, C, C++, C#, Juegos de Computadora, DotNetNuke, FireFox, Gadgets, Google AdSense, Google Analytics, Google Base, Google Services, Google Video, Google Web Toolkit, IE7, Iniciativa de Negocios por Internet, Publicidad por Internet, Internet Video, Linux, Microformatos, .NET, Ning, OpenGL, Perl, PHP, Podcasting, Python, Recommender Systems, RSS, Ruby, Motores de Búsqueda, Optimización de Motores de Búsqueda, Skype, Sudoku, Mundos Virtuales, Visual Basic, Wikis, Windows Vista, WinFX y muchos más por venir. Iniciativa de contenido libre. Nos complace ofrecerle artículos de invitados y tutoriales gratuitos, seleccio- nados de nuestras publicaciones actuales y futuras como parte de nuestra iniciativa de contenido libre. En cada tema del boletín de correo electrónico Deitel® Buzz Online, anunciamos las adiciones más recientes a nuestra biblioteca de contenido libre. Reconocimientos Uno de los mayores placeres al escribir un libro de texto es el de reconocer el esfuerzo de mucha gente, cuyos nom- bres quizá no aparezcan en la portada, pero cuyo arduo trabajo, cooperación, amistad y comprensión fue crucial para la elaboración de este libro. Mucha gente en Deitel & Associates, Inc. dedicó largas horas a este proyecto; queremos agradecer en especial a Abbey Deitel y Barbara Deitel. También nos gustaría agradecer a dos participantes de nuestro programa de Pasantía con Honores, que con- tribuyeron a esta publicación: Megan Shuster, con especialidad en ciencias computacionales en el Swarthmore College, y Henry Klementowicz, con especialidad en ciencias computacionales en la Universidad de Columbia. Nos gustaría mencionar nuevamente a nuestros colegas que realizaron contribuciones importantes a Cómo programar en Java 6ª edición: Andrew B. Goldberg, Jeff Listfield, Su Zhang, Cheryl Yaeger, Jing Hu, Sin Han Lo, John Paul Casiello y Christi Kelsey. Somos afortunados al haber trabajado en este proyecto con un talentoso y dedicado equipo de editores pro- fesionales en Prentice Hall. Apreciamos el extraordinario esfuerzo de Marcia Horton, Directora Editorial de la División de Ingeniería y Ciencias Computacionales de Prentice Hall. Jennifer Cappello y Dolores Mars hicieron xxvi Prefacio
  • 29. un excelente trabajo al reclutar el equipo de revisión del libro y administrar el proceso de revisión. Francesco San- talucia (un artista independiente) y Kristine Carney de Prentice Hall hicieron un maravilloso trabajo al diseñar la portada del libro; nosotros proporcionamos el concepto y ellos lo hicieron realidad. Vince O’Brien, Bob Engel- hardt, Donna Crilly y Marta Samsel hicieron un extraordinario trabajo al administrar la producción del libro. Deseamos reconocer el esfuerzo de nuestros revisores. Al adherirse a un estrecho itinerario, escrutinizaron el texto y los programas, proporcionando innumerables sugerencias para mejorar la precisión e integridad de la presentación. Apreciamos con sinceridad los esfuerzos de nuestros revisores de post-publicación de la 6ª edición, y nuestros revisores de la 7ª edición: Revisores de Cómo programar en Java 7ª edición (incluyendo los revisores de la post-publicación de la 6ª edición) Revisores de Sun Microsystems: Lance Andersen (Líder de especificaciones de JDBC/Rowset, Java SE Engi- neering), Ed Burns, Ludovic Champenois (Servidor de Aplicaciones de Sun para programadores de Java EE con Sun Application Server y herramientas: NetBeans, Studio Enterprise y Studio Creador), James Davidson, Vadiraj Deshpande (Grupo de Integración de Sistemas de Java Enterprise, Sun Microsystems India), Sanjay Dhamankar (Grupo Core Developer Platform), Jesse Glick (Grupo NetBeans), Brian Goetz (autor de Java Con- currency in Practice, Addison-Wesley, 2006), Doug Kohlert (Grupo Web Technologies and Standards), Sandeep Konchady (Organización de Ingeniería de Software de Java), John Morrison (Grupo Portal Server Product de Sun Java System), Winston Prakash, Brandon Taylor (grupo SysNet dentro de la División de Software) y Jayashri Visvanathan (Equipo de Java Studio Creador de Sun Microsystems). Revisores académicos y de la industria: Akram Al-Rawi (Universidad King Faisal), Mark Biamonte (DataDiret), Ayad Boudiab (Escuela Internacional de Choueifat, Líbano), Joe Bowbeer (Mobile App Consulting), Harlan Brewer (Select Engineering Services), Marita Ellixson (Eglin AFB, Universidad Indiana Wesleyan, Facilitador en Jefe), John Goodson (DataDiret), Anne Hor- ton (Lockheed Martin), Terrell Regis Hull (Logicalis Integration Solutions), Clark Richey (RABA Technologies, LLC, Java Sun Champion), Manfred Riem (UTA Interactive, LLC, Java Sun Champion), Karen Tegtmeyer (Model Technologies, Inc.), David Wolf (Universidad Pacific Lutheran) y Hua Yan (Borough of Manhattan Community Collage, City University of New York). Revisores de la post-publicación de Cómo programar en Java 6ª edición: Anne Horton (Lockheed Martin), William Martz (Universidad de Colorado, en Colorado Springs), Bill O’Farrell (IBM), Jeffry Babb (Universidad Virginia Commonwealth), Jeffrey Six (Universidad de Delaware, Instalaciones Adjuntas), Jesse Glick (Sun Microsystems), Karen Tegtmeyer (Model Technologies, Inc.), Kyle Gabhart (L-3 Communications), Marita Ellixson (Eglin AFB, Universidad Indiana Wesleyan, Facilitador en Jefe) y Sean Santry (Consultor independiente). Revisores de Cómo programar en Java 6ª edición (incluyendo a los revisores de la post-publicación de la 5ª edición) Revisores académicos: Karen Arlien (Colegio Estatal de Bismarck), Ben Blake (Universidad Estatal de Cleve- land), Walt Bunch (Universidad Chapman), Marita Ellixson (Eglin AFB/Universidad de Arkansas), Ephrem Eyob (Universidad Estatal de Virginia), Bjorn Foss (Universidad Metropolitana de Florida), Bill Freitas (The Lawrenceville School), Joe Kasprzyk (Colegio Estatal de Salem), Brian Larson (Modesto Junior College), Roberto Lopez-Herrejon (Universidad de Texas en Austin), Dean Mellas (Cerritos College), David Messier (Eastern Uni- versity), Andy Novobilski (Universidad deTennessee, Chattanooga), Richard Ord (Universidad de California, San Diego), Gavin Osborne (Saskatchewan Institute of Applied Science & Technology), Donna Reese (Universidad Estatal de Mississippi), Craig Slinkman (Universidad de Texas en Arlington), Sreedhar Thota (Western Iowa Tech Community Collage), Mahendran Velauthapillai (Universidad de Georgetown), Loran Walter (Universidad Tecnológica de Lawrence) y Stephen Weiss (Universidad de Carolina del Norte en Chapel Hill). Revisores de la industria: Butch Anton (Wi-Tech Consulting), Jonathan Bruce (Sun Microsystems, Inc.; Líder de Especifica- ciones de JCP para JDBC), Gilad Bracha (Sun Microsystems, Inc.; Líder de Especificaciones de JCP para Gené- ricos), Michael Develle (Consultor independiente), Jonathan Gadzik (Consultor independiente), Brian Goetz (Quiotix Corporation (Miembro del Grupo de Expertos de Especificaciones de Herramientas de Concurrencia de JCP), Anne Horton (AT&T Bell Laboratories), James Huddleston (Consultor independiente), Peter Jones (Sun Microsystems, Inc.), Doug Kohlert (Sun Microsystems, Inc.), Earl LaBatt (Altaworks Corp./Universidad de New Hampshire), Paul Monday (Sun Microsystems, Inc.), Bill O’Farrell (IBM), Cameron Skinner (Embar- cadero Technologies, Inc.), Brandon Taylor (Sun Microsystems, Inc.) y Karen Tegtmeyer (Consultor indepen- Prefacio xxvii
  • 30. diente). Revisores del ejemplo práctico opcional de DOO/UML: Sinan Si Alhir (Consultor independiente), Gene Ames (Star HRG), Jan Bergandy (Universidad de Massachussetts en Dartmouth), Marita Ellixson (Eglin AFB/Universidad de Arkansas), Jonathan Gadzik (Consultor independiente), Thomas Harder (ITT ESI, Inc.), James Huddleston (Consultor independiente), Terrell Hull (Consultor independiente), Kenneth Hussey (IBM), Joe Kasprzyk (Colegio Estatal de Salem), Dan McCracken (City College of New York), Paul Monday (Sun Microsystems, Inc.), Davyd Norris (Rational Software), Cameron Skinner (Embarcadero Technologies, Inc.), Craig Slinkman (Universidad de Texas en Arlington) y Steve Tockey (Construx Software). Estos profesionales revisaron cada aspecto del libro y realizaron innumerables sugerencias para mejorar la precisión e integridad de la presentación. Bueno ¡ahí lo tiene! Java es un poderoso lenguaje de programación que le ayudará a escribir programas con rapidez y eficiencia. Escala sin problemas hacia el ámbito del desarrollo de sistemas empresariales, para ayudar a las organizaciones a crear sus sistemas de información críticos. A medida que lea el libro, apreciaremos con sinceri- dad sus comentarios, críticas, correcciones y sugerencias para mejorar el texto. Dirija toda su correspondencia a: deitel@deitel.com Le responderemos oportunamente y publicaremos las correcciones y aclaraciones en nuestro sitio Web, www.deitel.com/books/jHTP7/ ¡Esperamos que disfrute aprendiendo con este libro tanto como nosotros disfrutamos el escribirlo! Paul J. Deitel Dr. Harvey M. Deitel Maynard, Massachussets Diciembre del 2006 Acerca de los autores Paul J. Deitel, CEO y Director Técnico de Deitel & Associates, Inc., es egresado del Sloan School of Manage- ment del MIT (Massachussets Institute of Technology), en donde estudió Tecnología de la Información. Posee las certificaciones Programador Certificado en Java (Java Certified Programmer) y Desarrollador Certificado en Java (Java Certified Developer), y ha sido designado por Sun Microsystems como Java Champion. A través de Deitel & Associates, Inc., ha impartido cursos en Java, C, C++, C# y Visual Basic a clientes de la industria, incluyendo: IBM, Sun Microsystems, Dell, Lucent Technologies, Fidelity, NASA en el Centro Espacial Kennedy, el Natio- nal Severe Storm Laboratory, White Sands Missile Range, Rogue Wave Software, Boeing, Stratus, Cambridge Technology Partners, Open Environment Corporation, One Wave, Hyperion Software, Adra Systems, Entergy, CableData Systems, Nortel Networks, Puma, iRobot, Invensys y muchos más. También ha ofrecido conferencias de Java y C++ para la Boston Chapter of the Association for Computing Machinery. Él y su padre, el Dr. Harvey M. Deitel, son autores de los libros de programación más vendidos en el mundo. Dr. Harvey M. Deitel, es Presidente y Consejero de Estrategia de Deitel & Associates, Inc., tiene 45 años de experiencia en el campo de la computación; lo que incluye un amplio trabajo académico y en la industria. El Dr. Deitel tiene una licenciatura y una maestría por el MIT y un doctorado de la Universidad de Boston. Tiene 20 años de experiencia como profesor universitario, la cual incluye un puesto vitalicio y el haber sido presidente del departamento de Ciencias de la computación en el Boston College antes de fundar, con su hijo Paul J. Deitel, Deitel & Associates, Inc. Él y Paul son coautores de varias docenas de libros y paquetes multimedia, y piensan escribir muchos más. Los textos de los Deitel se han ganado el reconocimiento internacional y han sido tradu- cidos al japonés, alemán, ruso, español, chino tradicional, chino simplificado, coreano, francés, polaco, italiano, portugués, griego, urdú y turco. El Dr. Deitel ha impartido cientos de seminarios profesionales para grandes empresas, instituciones académicas, organizaciones gubernamentales y diversos sectores del ejército. Acerca de Deitel & Associates, Inc. Deitel & Associates, Inc. es una empresa reconocida a nivel mundial, dedicada al entrenamiento corporativo y la creación de contenido, con especialización en lenguajes de programación, tecnología de software para Inter- net/World Wide Web, educación de tecnología de objetos y desarrollo de negocios por Internet a través de su xxviii Prefacio
  • 31. Iniciativa de Negocios en Internet. La empresa proporciona cursos, que son impartidos por instructores, sobre la mayoría de los lenguajes y plataformas de programación, como Java, Java Avanzado, C, C++, C#, Visual C++, Visual Basic, XML, Perl, Python, tecnología de objetos y programación en Internet y World Wide Web. Los fun- dadores de Deitel & Associates, Inc. son el Dr. Harvey M. Deitel y Paul J. Deitel. Sus clientes incluyen muchas de las empresas más grandes del mundo, agencias gubernamentales, sectores del ejército e instituciones académicas. A lo largo de su sociedad editorial de 30 años con Prentice Hall, Deitel & Associates Inc. ha publicado libros de texto de vanguardia sobre programación, libros profesionales, multimedia interactiva en CD como los Cyber Classrooms, Cursos Completos de Capacitación, cursos de capacitación basados en Web y contenido electrónico para los populares sistemas de administración de cursos WebCT, Blackboard y CourseCompass de Pearson. Deitel & Associates, Inc. y los autores pueden ser contactados mediante correo electrónico en: deitel@deitel.com Para conocer más acerca de Deitel & Associates, Inc., sus publicaciones y su currículum mundial de la Serie de Capacitación Corporativa DIVE INTO®, visite: www.deitel.com y suscríbase al boletín gratuito de correo electrónico, Deitel® Buzz Online, en: www.deitel.com/newsletter/subscribe.html Puede verificar la lista creciente de Centros de Recursos Deitel en: www.deitel.com/resourcecenters.html Quienes deseen comprar publicaciones de Deitel pueden hacerlo en: www.deitel.com/books/index.html Las empresas, el gobierno, las instituciones militares y académicas que deseen realizar pedidos en masa deben hacerlo directamente con Prentice Hall. Para obtener más información, visite: www.prenhall.com/mischtm/support.html#order Prefacio xxix
  • 32. Antes de comenzar a utilizar este libro, debe seguir las instrucciones de esta sección para asegurarse que Java esté instalado de manera apropiada en su computadora. Convenciones de fuentes y nomenclatura Utilizamos varios tipos de letra para diferenciar los componentes en la pantalla (como los nombres de menús y los elementos de los mismos) y el código o los comandos en Java. Nuestra convención es hacer hincapié en los componentes en pantalla en una fuente Helvetica sans-serif en negritas (por ejemplo, el menú Archivo) y enfatizar el código y los comandos de Java en una fuente Lucida sans-serif (por ejemplo, System.out.println()). Kit de desarrollo de Java Standard Edition (JDK) 6 Los ejemplos en este libro se desarrollaron con el Kit de Desarrollo de Java Standard Edition (JDK) 6. Puede descargar la versión más reciente y su documentación en: java.sun.com/javase/6/download.jsp Si tiene preguntas, envíe un correo electrónico a deitel@deitel.com. Le responderemos en breve. Requerimientos de software y hardware del sistema Procesador Pentium III de 500 MHz (mínimo) o de mayor velocidad; Sun® Java™ Studio Creator 2 Update 1 requiere un procesador Intel Pentium 4 de 1 GHz (o equivalente). Microsoft Windows Server 2003, Windows XP (con Service Pack 2), Windows 2000 Professional (con Service Pack 4). Una de las siguientes distribuciones de Linux: Red Hat® Enterprise Linux 3, o Red Hat Fedora Core 3. Mínimo 512 MB de memoria en RAM; Sun Java Studio Creator 2 Update 1 requiere 1 GB de RAM. Mínimo 1.5 GB de espacio en disco duro. Unidad de CD-ROM. Conexión a Internet. Explorador Web, Adobe® Acrobat® Reader® y una herramienta para descomprimir archivos zip. Uso de los CD Los ejemplos para Cómo programar en Java, 7ª edición se encuentran en los CD (Windows y Linux) que se inclu- yen en este libro. Siga los pasos de la siguiente sección, Cómo copiar los ejemplos del libro del CD, para copiar el directorio de ejemplos apropiado del CD a su disco duro. Le sugerimos trabajar desde su disco duro en lugar de hacerlo desde su unidad de CD por dos razones: 1, los CD son de sólo lectura, por lo que no podrá guardar sus aplicaciones en ellos; 2 es posible acceder a los archivos con mayor rapidez desde un disco duro que de un CD. Los ejemplos del libro también están disponibles para descargarse de: www.deitel.com/books/jhtp7/ www.pearsoneducacion.net/deitel/ La interfaz para el contenido del CD de Microsoft® Windows® está diseñada para iniciarse de manera auto- mática, a través del archivo AUTORUN.EXE. Si no aparece una pantalla de inicio cuando inserte el CD en su • • • • • • • • Antes de empezar
  • 33. computadora, haga doble clic en el archivo welcome.htm para iniciar la interfaz del CD para el estudiante, o consulte el archivo readme.txt en el CD. Para iniciar la interfaz del CD para Linux, haga doble clic en el archivo welcome.html. Cómo copiar los ejemplos del libro del CD Las capturas de pantalla de esta sección pueden diferir un poco de lo que usted verá en su computadora, de acuer- do con el sistema operativo y el explorador Web de que disponga. Las instrucciones de los siguientes pasos asumen que está utilizando Microsoft Windows. 1. Insertar el CD. Inserte el CD que se incluye con este libro en la unidad de CD de su computadora. A continuación deberá aparecer de manera automática la página Web welcome.htm (figura 1) en Win- dows. También puede utilizar el Explorador de Windows para ver el contenido del CD y hacer doble clic en welcome.htm para mostrar esta página. 2. Abrir el directorio del CD-ROM. Haga clic en el vínculo Browse CD Contents (Explorar contenido del CD) (figura 1) para ver el contenido del CD. Figura 1 | Página de bienvenida para el CD de Cómo programar en Java. Haga clic en el vínculo Browse CD Contents para acceder al contenido del CD 3. Copiar el directorio ejemplos. Haga clic en el directorio ejemplos (figura 2), después seleccione Copiar. A continuación, use el Explorador de Windows para ver el contenido de su unidad C:. (Tal vez necesite hacer clic en un vínculo para mostrar el contenido de la unidad). Una vez que se muestre el con- tenido, haga clic en cualquier parte y seleccione la opción Pegar del menú Editar para copiar el direc- torio ejemplos del CD a su unidad C:. [Nota: guardamos los ejemplos directamente en la unidad C: y hacemos referencia a esta unidad a lo largo del texto. Puede optar por guardar sus archivos en una unidad distinta, con base en la configuración de su computadora, en el laboratorio de su escuela o sus preferencias personales. Si trabaja en un laboratorio de computadoras, consulte con su profesor para obtener más información para confirmar en dónde se deben guardar los ejemplos]. Modificación de la propiedad de sólo lectura de los archivos Los archivos de ejemplo que copió a su computadora desde el CD son de sólo lectura. A continuación eliminará la propiedad de sólo lectura, para poder modificar y ejecutar los ejemplos. 1. Abrir el cuadro de diálogo Propiedades. Haga clic con el botón derecho del ratón en el directorio ejemplos y seleccione Propiedades. A continuación aparecerá el cuadro de diálogo Propiedades de ejemplos (figura 3). Antes de empezar xxxi
  • 34. 2. Cambiar la propiedad de sólo lectura. En la sección Atributos de este cuadro de diálogo, haga clic en el botón Sólo lectura para eliminar la marca de verificación (figura 4). Haga clic en Aplicar para aplicar los cambios. Figura 3 | Cuadro de diálogo Propiedades de ejemplos. xxxii Antes de empezar Figura 2 | Copia del directorio ejemplos. Haga clic con el botón derecho del ratón en el directorio ejemplos Seleccione Copiar
  • 35. Figura 4 | Desactivar la casilla de verificación Sólo lectura. Figura 5 | Eliminar la propiedad de sólo lectura para todos los archivos en el directorio ejemplos. Antes de empezar xxxiii 3. Cambiar la propiedad para todos los archivos. Al hacer clic en Aplicar se mostrará la ventana Confir- mar cambios de atributos (figura 5). En esta ventana, haga clic en el botón de opción Aplicar cambios a esta carpeta y a todas las subcarpetas y archivos y haga clic en Aceptar para eliminar la propiedad de sólo lectura para todos los archivos y directorios en el directorio ejemplos. Desactive el atributo Sólo lectura Instalación del Kit de Desarrollo de Java Standard Edition (JDK) Antes de ejecutar las aplicaciones de este libro o de crear sus propias aplicaciones, debe instalar el Kit de Desarrollo de Java Standard Edition (JDK) 6 o una herramienta de desarrollo para Java que soporte a Java SE 6. Puede descargar el JDK 6 y su documentación de java.sun.com/javase/6/download.jsp. Haga clic en el botón » DOWNLOAD para JDK 6. Debe aceptar el acuerdo de licencia antes de descargar. Una vez que acepte el acuerdo, haga clic en el vínculo para el instalador de su plataforma. Guarde el instalador en su disco duro y no olvide en dónde lo guardó. Antes de instalar, lea con cuidado las instrucciones de instalación del JDK para su plataforma, que se encuentran en java.sun.com/javase/6/webnotes/install/index.html. Después de descargar el instalador del JDK, haga doble clic en el programa instalador para empezar a insta- larlo. Le recomendamos que acepte todas las opciones de instalación predeterminadas. Si modifica el directorio predeterminado, asegúrese de anotar el nombre y la ubicación exactos que eligió, ya que necesitará esta informa- ción más adelante en el proceso de instalación. En Windows, el JDK se coloca, de manera predeterminada, en el siguiente directorio: C:Archivos de programaJavajdk1.6.0 Haga clic en este botón de opción para eliminar la propiedad Sólo lectura para todos los archivos
  • 36. Establecer la variable de entorno PATH La variable de entorno PATH en su computadora indica qué directorios debe buscar la computadora cuando inten- te localizar aplicaciones, como aquellas que le permiten compilar y ejecutar sus aplicaciones en Java (conocidas como javac.exe y java.exe, respectivamente). Ahora aprenderá a establecer la variable de entorno PATH en su computadora para indicar en dónde están instaladas las herramientas del JDK. 1. Abrir el cuadro de diálogo Propiedades del sistema. Haga clic en Inicio > Panel de control > Sistema para mostrar el cuadro de diálogo Propiedades del sistema (figura 6). [Nota: su cuadro de diálogo Pro- piedades del sistema puede tener una apariencia distinta al que se muestra en la figura 6, dependiendo de la versión de Microsoft Windows. Este cuadro de diálogo específico es de una computadora que ejecuta Microsoft Windows XP. Sin embargo, el que aparece en su computadora podría incluir distinta información]. 2. Abrir el cuadro de diálogo Variables de entorno. Seleccione la ficha Opciones avanzadas de la parte superior del cuadro de diálogo Propiedades del sistema (figura 7). Haga clic en el botón Variables de entorno para desplegar el cuadro de diálogo Variables de entorno (figura 8). 3. Editar la variable PATH. Desplácese por el cuadro Variables del sistema para seleccionar la variable PATH. Haga clic en el botón Modificar. Esto hará que se despliegue el cuadro de diálogo Modificar la variable del sistema (figura 9). 4. Modificar la variable PATH. Coloque el cursor dentro del campo Valor de variable. Use la flecha izquierda para desplazar el cursor hasta el inicio de la lista. Al principio de la lista, escriba el nombre del directorio en el que colocó el JDK, seguido de bin; (figura 10). Agregue C:Archivos de progra- ma Javajdk1.6.0bin; a la variable PATH, si eligió el directorio de instalación predeterminado. No coloque espacios antes o después de lo que escriba. No se permiten espacios antes o después de cada valor en una variable de entorno. Haga clic en el botón Aceptar para aplicar sus cambios a la variable PATH. Si no establece la variable PATH de manera correcta, al utilizar las herramientas del JDK recibirá un mensaje como éste: ‘java’ no se reconoce como un comando interno o externo, programa o archivo por lotes ejecutable. En este caso, regrese al principio de esta sección y vuelva a comprobar sus pasos. Si ha descargado una versión más reciente del JDK, tal vez necesite modificar el nombre del directorio de instalación del JDK en la variable PATH. Figura 6 | Cuadro de diálogo Propiedades del sistema. xxxiv Antes de empezar
  • 37. Antes de empezar xxxv Figura 8 | Cuadro de diálogo Variables de entorno. Figura 7 | Ficha Opciones avanzadas del cuadro de diálogo Propiedades del sistema. Seleccione la ficha Opciones avanzadas Haga clic en el botón Variables de entorno Figura 9 | Cuadro de diálogo Modificar la variable del sistema. Figura 10 | Modificación de la variable PATH.
  • 38. Establecer la variable de entorno CLASSPATH Si trata de ejecutar un programa en Java y recibe un mensaje como: Exception in thread “main” java.lang.NoClassDefFoundError: SuClase entonces su sistema tiene una variable de entorno CLASSPATH que debe modificarse. Para corregir el error anterior, siga los pasos para establecer la variable de entorno PATH, localice la variable CLASSPATH y modifique su valor para que incluya lo siguiente: .; al principio de su valor (sin espacios antes o después de estos caracteres). Ahora está listo para empezar sus estudios de Java con el libro Cómo programar en Java, 7ª edición. ¡Espera- mos que lo disfrute! xxxvi Antes de empezar
  • 39. Introducción a las computadoras, Internet y Web OBJETIVOS En este capítulo aprenderá a: Comprender los conceptos básicos de hardware y software. Conocer los conceptos básicos de la tecnología de objetos, como las clases, objetos, atributos, comportamientos, encapsulamiento, herencia y polimorfismo. Familiarizarse con los distintos lenguajes de programación. Saber qué lenguajes de programación se utilizan más. Comprender un típico entorno de desarrollo en Java. Entender el papel de Java en el desarrollo de aplicaciones cliente/servidor distribuidas para Internet y Web. Conocer la historia de UML: el lenguaje de diseño orientado a objetos estándar en la industria. Conocer la historia de Internet y World Wide Web. Probar aplicaciones en Java. Q Q Q Q Q Q Q Q Q Nuestra vida se malgasta por los detalles… simplificar, simplificar. —Henry David Thoreau La principal cualidad del lenguaje es la claridad. —Galen Mi sublime objetivo deberé llevarlo a cabo a tiempo. —W. S. Gilbert Tenía un maravilloso talento para empacar estrechamente el pensamiento, haciéndolo portable. —Thomas B. Macaulay “¡Caray, creo que de los dos, el intérprete es el más difícil de entender!” —Richard Brinsley Sheridan El hombre sigue siendo la computadora más extraordinaria de todas. —John F. Kennedy 1
  • 40. 2 Capítulo 1 Introducción a las computadoras, Internet y Web 1.1 Introducción ¡Bienvenido a Java! Hemos trabajado duro para crear lo que pensamos será una experiencia de aprendizaje infor- mativa, divertida y retadora para usted. Java es un poderoso lenguaje de programación, divertido para los princi- piantes y apropiado para los programadores experimentados que desarrollan sistemas de información de tamaño considerable. Cómo programar en Java, 7ª edición es una herramienta efectiva de aprendizaje para cada una de estas audiencias. Pedagogía La parte central del libro se enfoca en la claridad de los programas, a través de las técnicas comprobadas de la pro- gramación orientada a objetos. Los principiantes aprenderán programación de manera correcta, desde el principio. La presentación es clara, simple y tiene muchas ilustraciones. Incluye cientos de programas completos y funcio- nales en Java, y muestra la salida que se obtiene al ejecutar estos programas en una computadora. Enseñamos las características de Java en un contexto de programas completos y funcionales; a esto le llamamos el método de código activo (Live-Code™). Los programas de ejemplo están disponibles en el CD que acompaña a este libro. También puede descargarlos de los sitios Web www.deitel.com/books/jhtp7/ o www.pearsoneducacion. net.com/deitel. Fundamentos Los primeros capítulos presentan los fundamentos de las computadoras, la programación de éstas y el lenguaje de programación Java, con lo cual se provee una base sólida para un análisis más detallado de Java en los capítulos posteriores. Los programadores experimentados tienden a leer los primeros capítulos rápidamente, y descubren que el análisis de Java en los capítulos posteriores es riguroso y retador. La mayoría de las personas están familiarizadas con las emocionantes tareas que realizan las computado- ras. Por medio de este libro, usted aprenderá a programar las computadoras para que realicen dichas tareas. El 1.1 Introducción 1.2 ¿Qué es una computadora? 1.3 Organización de una computadora 1.4 Los primeros sistemas operativos 1.5 Computación personal, distribuida y cliente/servidor 1.6 Internet y World Wide Web 1.7 Lenguajes máquina, ensambladores y de alto nivel 1.8 Historia de C y C++ 1.9 Historia de Java 1.10 Bibliotecas de clases de Java 1.11 FORTRAN, COBOL, Pascal y Ada 1.12 BASIC, Visual Basic, Visual C++, C# y .NET 1.13 Entorno de desarrollo típico en Java 1.14 Generalidades acerca de Java y este libro 1.15 Prueba de una aplicación en Java 1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML 1.17 Web 2.0 1.18 Tecnologías de software 1.19 Conclusión 1.20 Recursos Web Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios Pla n g e ne r a l
  • 41. software (las instrucciones que usted escribe para indicar a la computadora que realice acciones y tome decisio- nes) es quien controla a las computadoras (conocidas comúnmente como hardware). Java, desarrollado por Sun Microsystems, es uno de los lenguajes para desarrollo de software más populares en la actualidad. Java Standard Edition 6 (Java SE 6) y el Kit de Desarrollo de Java 6 (JDK 6) Este libro se basa en la plataforma Java Standard Edition 6 (Java SE 6) de Sun, también conocida como Mus- tang. Sun ofrece una implementación de Java SE 6, conocida como Kit de Desarrollo de Java (JDK), que incluye las herramientas necesarias para escribir software en Java. Nosotros utilizamos el JDK versión 6.0 para los programas en este libro. Por lo regular, Sun actualiza el JDK para corregir errores: para descargar la versión más reciente del JDK 6, visite java.sun.com/javase/6/download.jsp. Evolución de la computación y de la programación El uso de las computadoras se está incrementando en casi cualquier campo de trabajo; los costos de se han redu- cido en forma dramática, debido al rápido desarrollo en la tecnología de hardware y software. Las computadoras que ocupaban grandes habitaciones y que costaban millones de dólares, hace algunas décadas, ahora pueden colocarse en las superficies de chips de silicio más pequeños que una uña, y con un costo de quizá unos cuan- tos dólares cada uno. Por fortuna, el silicio es uno de los materiales más abundantes en el planeta (es uno de los ingredientes de la tierra). La tecnología de los chips de silicio ha vuelto tan económica a la tecnología de la computación que cientos de millones de computadoras de uso general se encuentran actualmente ayudando a la gente de todo el mundo en: empresas, la industria, el gobierno y en sus vidas. Dicho número podría dupli- carse fácilmente en unos cuantos años. A través de los años, muchos programadores aprendieron la metodología conocida como programación estructurada. Usted aprenderá tanto la programación estructurada como la novedosa y excitante metodología de la programación orientada a objetos. ¿Por qué enseñamos ambas? La programación orientada a objetos es la metodología clave utilizada hoy en día por los programadores. Usted creará y trabajará con muchos objetos de software en este libro. Sin embargo, descubrirá que la estructura interna de estos objetos se construye, a menudo, utilizando técnicas de programación estructurada. Además, la lógica requerida para manipular objetos se expresa algunas veces mediante la programación estructurada. El lenguaje de elección para las aplicaciones en red Java se ha convertido en el lenguaje de elección para implementar aplicaciones basadas en Internet, y software para dispositivos que se comunican a través de una red. Ahora, los estéreos y otros dispositivos en los hogares pueden conectarse entre sí mediante el uso de tecnología Java. ¡En la conferencia JavaOne en mayo del 2006, Sun anunció que había mil millones de teléfonos móviles y dispositivos portátiles habilitados para Java! Java ha evolucionado rápidamente en el ámbito de las aplicaciones de gran escala. Es el lenguaje preferido para satisfacer la mayoría de las necesidades de programación de muchas organizaciones. Java ha evolucionado tan rápidamente que publicamos esta séptima edición de Cómo programar en Java justamente 10 años después de publicar la primera edición. Java ha crecido tanto que cuenta con otras dos edi- ciones. La edición Java Enterprise Edition (Java EE) está orientada hacia el desarrollo de aplicaciones de red distribuidas, de gran escala, y aplicaciones basadas en Web. La plataforma Java Micro Edition (Java ME) está orientada hacia el desarrollo de aplicaciones para dispositivos pequeños, con memoria limitada, como los teléfo- nos celulares, radiolocalizadores y PDAs. Permanezca en contacto con nosotros Está a punto de comenzar una ruta de desafíos y recompensas. Mientras tanto, si desea comunicarse con nosotros, envíenos un correo a deitel@deitel.com o explore nuestro sitio Web en www.deitel.com. Le responderemos a la brevedad. Para mantenerse al tanto de los desarrollos con Java en Deitel & Associates, regístrese para recibir nuestro boletín de correo electrónico, Deitel® Buzz Online en www.deitel.com/newsletter/subscribe.html Para obtener material adicional sobre Java, visite nuestra creciente lista de centros de recursos en www.deitel. com/ResourceCenters.html. Esperamos que disfrute aprender con Cómo programar en Java, 7ª edición. 1.1 Introducción 3
  • 42. 4 Capítulo 1 Introducción a las computadoras, Internet y Web 1.2 ¿Qué es una computadora? Una computadora es un dispositivo capaz de realizar cálculos y tomar decisiones lógicas a velocidades de millones (incluso de miles de millones) de veces más rápidas que los humanos. Por ejemplo, muchas de las computadoras personales actuales pueden realizar varios miles de millones de cálculos en un segundo. Una persona con una calculadora podría requerir toda una vida para completar el mismo número de operaciones. (Puntos a considerar: ¿cómo sabría que la persona sumó los números de manera correcta?, ¿cómo sabría que la computadora sumó los números de manera correcta?) ¡Las supercomputadoras actuales más rápidas pueden realizar billones de sumas por segundo! Las computadoras procesan los datos bajo el control de conjuntos de instrucciones llamadas programas de cómputo. Estos programas guían a la computadora a través de conjuntos ordenados de acciones especificadas por gente conocida como programadores de computadoras. Una computadora está compuesta por diversos dispositivos (como teclado, monitor, ratón, discos, memoria, DVD, CD-ROM y unidades de procesamiento) conocidos como hardware. A los programas que se ejecutan en una computadora se les denomina software. Los costos de las piezas de hardware han disminuido de manera espectacular en años recientes, al punto en que las computadoras personales se han convertido en artículos domés- ticos. En este libro aprenderá métodos comprobados que pueden reducir los costos de desarrollo del software: programación orientada a objetos y (en nuestro Ejemplo práctico de Ingeniería de Software en los capítulos 2-8 y 10) diseño orientado a objetos. 1.3 Organización de una computadora Independientemente de las diferencias en su apariencia física, casi todas las computadoras pueden representarse mediante seis unidades lógicas o secciones: 1. Unidad de entrada. Esta sección “receptora” obtiene información (datos y programas de cómputo) desde diversos dispositivos de entrada y pone esta información a disposición de las otras unidades para que pueda procesarse. La mayoría de la información se introduce a través de los teclados y ratones; tam- bién puede introducirse de muchas otras formas, como hablar con su computadora, digitalizar imágenes y desde una red, como Internet. 2. Unidad de salida. Esta sección de “embarque” toma información que ya ha sido procesada por la computadora y la coloca en los diferentes dispositivos de salida, para que esté disponible fuera de la computadora. Hoy en día, la mayoría de la información de salida de las computadoras se despliega en el monitor, se imprime en papel o se utiliza para controlar otros dispositivos. Las computadoras también pueden dar salida a su información a través de redes como Internet. 3. Unidad de memoria. Esta sección de “almacén” de acceso rápido, pero con relativa baja capacidad, retiene la información que se introduce a través de la unidad de entrada, para que esté disponible de manera inmediata para procesarla cuando sea necesario. La unidad de memoria también retiene la infor- mación procesada hasta que ésta pueda colocarse en los dispositivos de salida por la unidad de salida. Por lo general, la información en la unidad de memoria se pierde cuando se apaga la computadora. Con frecuencia, a esta unidad de memoria se le llama memoria o memoria primaria. 4. Unidad aritmética y lógica (ALU). Esta sección de “manufactura” es la responsable de realizar cálcu- los como suma, resta, multiplicación y división. Contiene los mecanismos de decisión que permiten a la computadora hacer cosas como, por ejemplo, comparar dos elementos de la unidad de memoria para determinar si son iguales o no. 5. Unidad central de procesamiento (CPU). Esta sección “administrativa” coordina y supervisa la ope- ración de las demás secciones. La CPU le indica a la unidad de entrada cuándo debe grabarse la infor- mación dentro de la de memoria; a la ALU, cuándo debe utilizarse la información de la memoria para los cálculos; y a la unidad de salida, cuándo enviar la información desde la memoria hasta ciertos dispositivos de salida. Muchas de las computadoras actuales contienen múltiples CPUs y, por lo tanto, pueden realizar diversas operaciones de manera simultánea (a estas computadoras se les conoce como multiprocesadores).
  • 43. 6. Unidad de almacenamiento secundario. Ésta es la sección de “almacén” de alta capacidad y de larga duración. Los programas o datos que no se encuentran en ejecución por las otras unidades, normalmen- te se colocan en dispositivos de almacenamiento secundario (por ejemplo, el disco duro) hasta que son requeridos nuevamente, posiblemente horas, días, meses o incluso años después. El tiempo para acceder a la información en almacenamiento secundario es mucho mayor que el que se necesita para acceder a la información de la memoria principal, pero el costo por unidad de memoria secundaria es mucho menor que el correspondiente a la unidad de memoria principal. Los CDs y DVDs son ejemplos de dispositi- vos de almacenamiento secundario y pueden contener hasta cientos de millones y miles de millones de caracteres, respectivamente. 1.4 Los primeros sistemas operativos Las primeras computadoras eran capaces de realizar solamente una tarea o trabajo a la vez. A esta forma de ope- ración de la computadora a menudo se le conoce como procesamiento por lotes (batch) de un solo usuario. La computadora ejecuta un solo programa a la vez, mientras procesa los datos en grupos o lotes. En estos primeros sistemas, los usuarios generalmente asignaban sus trabajos a un centro de cómputo que los introducía en paquetes de tarjetas perforadas, y a menudo tenían que esperar horas, o incluso días, antes de que sus resultados impresos regresaran a sus escritorios. El software denominado sistema operativo se desarrolló para facilitar el uso de la computadora. Los prime- ros sistemas operativos administraban la suave transición entre trabajos, incrementando la cantidad de trabajo, o el flujo de datos, que las computadoras podían procesar. Conforme las computadoras se volvieron más poderosas, se hizo evidente que un proceso por lotes para un solo usuario era ineficiente, debido al tiempo que se malgastaba esperando a que los lentos dispositivos de entra- da/salida completaran sus tareas. Se pensó que era posible realizar muchos trabajos o tareas que podrían compartir los recursos de la computadora y lograr un uso más eficiente. A esto se le conoce como multiprogramación; que significa la operación simultánea de muchas tareas que compiten para compartir los recursos de la computadora. Aun con los primeros sistemas operativos con multiprogramación, los usuarios seguían enviando sus tareas en paquetes de tarjetas perforadas y esperaban horas, incluso hasta días, por los resultados. En la década de los sesenta, varios grupos en la industria y en las universidades marcaron la pauta de los sis- temas operativos de tiempo compartido. El tiempo compartido es un caso especial de la multiprogramación, ya que los usuarios acceden a la computadora a través de terminales que, por lo general, son dispositivos compuestos por un teclado y un monitor. Puede haber docenas o incluso cientos de usuarios compartiendo la computadora al mismo tiempo. La computadora en realidad no ejecuta los procesos de todos los usuarios a la vez. Lo que hace es ejecutar una pequeña porción del trabajo de un usuario y después procede a dar servicio al siguiente usuario, con la posibilidad de proporcionar el servicio a cada usuario varias veces por segundo. Así, los programas de los usuarios aparentemente se ejecutan de manera simultánea. Una ventaja del tiempo compartido es que el usuario recibe respuestas casi inmediatas a las peticiones. 1.5 Computación personal, distribuida y cliente/servidor En 1977, Apple Computer popularizó el fenómeno de la computación personal. Las computadoras se volvie- ron sumamente económicas, de manera que la gente pudo adquirirlas para su uso personal o para negocios. En 1981, IBM, el vendedor de computadoras más grande del mundo, introdujo la Computadora Personal (PC) de IBM. Con esto se legitimó rápidamente la computación en las empresas, en la industria y en las organizaciones gubernamentales. Estas computadoras eran unidades “independientes” (la gente transportaba sus discos de un lado a otro para compartir información; a esto se le conoce comúnmente como “sneakernet”). Aunque las primeras computadoras personales no eran lo suficientemente poderosas para compartir el tiempo entre varios usuarios, podían interco- nectarse mediante redes computacionales, algunas veces a través de líneas telefónicas y otras mediante redes de área local (LANs) dentro de una empresa. Esto derivó en el fenómeno denominado computación distribuida, en donde todos los cálculos informáticos de una empresa, en vez de realizarse estrictamente dentro de un centro de cómputo, se distribuyen mediante redes a los sitios en donde se realiza el trabajo de la empresa. Las compu- tadoras personales eran lo suficientemente poderosas para manejar los requerimientos de cómputo de usuarios individuales, y para manejar las tareas básicas de comunicación que involucraban la transferencia de información entre una computadora y otra, de manera electrónica. 1.5 Computación personal, distribuida y cliente/servidor 5
  • 44. 6 Capítulo 1 Introducción a las computadoras, Internet y Web Las computadoras personales actuales son tan poderosas como las máquinas de un millón de dólares de hace apenas unas décadas. Las máquinas de escritorio más poderosas (denominadas estaciones de trabajo) propor- cionan a cada usuario enormes capacidades. La información se comparte fácilmente a través de redes de compu- tadoras, en donde algunas computadoras denominadas servidores almacenan datos que pueden ser utilizados por computadoras cliente distribuidas en toda la red, de ahí el término de computación cliente/servidor. Java se está utilizando ampliamente para escribir software para redes de computadoras y para aplicaciones cliente/servi- dor distribuidas. Los sistemas operativos actuales más populares como Linux, Mac OS X y Microsoft Windows proporcionan el tipo de capacidades que explicamos en esta sección. 1.6 Internet y World Wide Web Internet (una red global de computadoras) tiene sus raíces en la década de 1960; su patrocinio estuvo a cargo del Departamento de Defensa de los Estados Unidos. Diseñada originalmente para conectar los sistemas de cómputo principales de aproximadamente una docena de universidades y organizaciones de investigación, actualmente, Internet es utilizada por cientos de millones de computadoras y dispositivos controlados por computadora en todo el mundo. Con la introducción de World Wide Web (que permite a los usuarios de computadora localizar y ver docu- mentos basados en multimedia, sobre casi cualquier tema, a través del ciberespacio), Internet se ha convertido explosivamente en uno de los principales mecanismos de comunicación en todo el mundo. Internet y World Wide Web se encuentran, sin duda, entre las creaciones más importantes y profundas de la humanidad. En el pasado, la mayoría de las aplicaciones de computadora se ejecutaban en equipos que no estaban conectados entre sí. Las aplicaciones de la actualidad pueden diseñarse para intercomunicarse entre computadoras en todo el mundo. Internet mezcla las tecnologías de la computación y las comunicaciones. Facilita nuestro tra- bajo. Hace que la información esté accesible en forma instantánea y conveniente para todo el mundo. Hace posi- ble que los individuos y negocios pequeños locales obtengan una exposición mundial. Está cambiando la forma en que se hacen los negocios. La gente puede buscar los mejores precios para casi cualquier producto o servicio. Los miembros de las comunidades con intereses especiales pueden mantenerse en contacto unos con otros. Los investigadores pueden estar inmediatamente al tanto de los últimos descubrimientos. Cómo programar en Java, 7ª edición presenta técnicas de programación que permiten a las aplicaciones en Java utilizar Internet y Web para interactuar con otras aplicaciones. Estas técnicas, junto con otras más, permiten a los programadores de Java desarrollar el tipo de aplicaciones distribuidas de nivel empresarial que se utilizan actual- mente en la industria. Se pueden escribir aplicaciones en Java para ejecutarse en cualquier tipo de computadora, con lo cual se reduce en gran parte el tiempo y el costo de desarrollo de sistemas. Si a usted le interesa desarrollar aplicaciones que se ejecuten a través de Internet y Web, aprender Java puede ser la clave para que reciba oportu- nidades retadoras y remuneradoras en su profesión. 1.7 Lenguajes máquina, ensambladores y de alto nivel Los programadores escriben instrucciones en diversos lenguajes de programación, algunos de los cuales compren- de directamente la computadora, mientras que otros requieren pasos intermedios de traducción. En la actualidad se utilizan cientos de lenguajes de computación. Éstos se dividen en tres tipos generales: 1. Lenguajes máquina. 2. Lenguajes ensambladores. 3. Lenguajes de alto nivel. Cualquier computadora puede entender de manera directa sólo su propio lenguaje máquina; que es su “lenguaje natural”, y como tal, está definido por el diseño del hardware de dicha computadora. Por lo general, los lenguajes máquina consisten en cadenas de números (que finalmente se reducen a 1s y 0s) que instruyen a las computadoras para realizar sus operaciones más elementales, una a la vez. Los lenguajes máquina son dependien- tes de la máquina (es decir, un lenguaje máquina en particular puede usarse solamente en un tipo de compu- tadora). Dichos lenguajes son difíciles de comprender para los humanos, el siguiente ejemplo muestra uno de los primeros programas en lenguaje máquina, el cual suma el pago de las horas extras al sueldo base y almacena el resultado en el sueldo bruto:
  • 45. +1300042774 +1400593419 +1200274027 La programación en lenguaje máquina era demasiado lenta y tediosa para la mayoría de los programadores. En vez de utilizar las cadenas de números que las computadoras podían entender directamente, los programadores empezaron a utilizar abreviaturas del inglés para representar las operaciones elementales. Estas abreviaturas for- maron la base de los lenguajes ensambladores. Los programas traductores conocidos como ensambladores se desarrollaron para convertir los primeros programas en lenguaje ensamblador a lenguaje máquina, a la velo- cidad de la computadora. A continuación se muestra un ejemplo de un programa en lenguaje ensamblador, que también suma el pago de las horas extras al sueldo base y almacena el resultado en el sueldo bruto: load sueldobase add sueldoextra store sueldobruto Aunque este código es más claro para los humanos, las computadoras no lo pueden entender sino hasta que se traduce en lenguaje máquina. El uso de las computadoras se incrementó rápidamente con la llegada de los lenguajes ensambladores, pero los programadores aún requerían de muchas instrucciones para llevar a cabo incluso hasta las tareas más simples. Para agilizar el proceso de programación se desarrollaron los lenguajes de alto nivel, en donde podían escribirse instrucciones individuales para realizar tareas importantes. Los programas traductores, denominados compilado- res, convierten, a lenguaje máquina, los programas que están en lenguaje de alto nivel. Estos últimos permiten a los programadores escribir instrucciones que son muy similares al inglés común, y contienen la notación matemá- tica común. Un programa de nómina escrito en un lenguaje de alto nivel podría contener una instrucción como la siguiente: sueldoBruto = sueldoBase + sueldoExtra Obviamente, desde el punto de vista del programador, los lenguajes de alto nivel son mucho más recomen- dables que los lenguajes máquina o ensamblador. C, C++ y los lenguajes .NET de Microsoft (por ejemplo, Visual Basic .NET, Visual C++ .NET y C#) son algunos de los lenguajes de programación de alto nivel que más se utilizan; sin embargo, Java es el más utilizado. El proceso de compilación de un programa escrito en lenguaje de alto nivel a un lenguaje máquina puede tardar un tiempo considerable en la computadora. Los programas intérpretes se desarrollaron para ejecutar programas en lenguaje de alto nivel directamente, aunque con más lentitud. Los intérpretes son populares en los entornos de desarrollo de programas, en los cuales se agregan nuevas características y se corrigen los errores. Una vez que se desarrolla un programa por completo, se puede producir una versión compilada para ejecutarse con la mayor eficiencia. Actualmente se sabe que existen dos formas de traducir un programa en lenguaje de alto nivel a un formato que la computadora pueda entender: compilación e interpretación. Como veremos en la sección 1.13, Java utiliza una mezcla inteligente de estas tecnologías. 1.8 Historia de C y C++ Java evolucionó de C++, el cual evolucionó de C, que a su vez evolucionó de BCPL y B. En 1967, Martin Richards desarrolló BCPL como un lenguaje para escribir software para sistemas operativos y compiladores. Ken Thompson modeló muchas características en su lenguaje B a partir del trabajo de sus contrapartes en BCPL, y utilizó a B para crear las primeras versiones del sistema operativo UNIX, en los laboratorios Bell en 1970. El lenguaje C evolucionó a partir de B, gracias al trabajo de Dennis Ritchie en los laboratorios Bell, y se implementó originalmente en 1972. Inicialmente, se hizo muy popular como lenguaje de desarrollo para el sis- tema operativo UNIX. En la actualidad, la mayoría del código para los sistemas operativos de propósito general (por ejemplo, los que se encuentran en las computadoras portátiles, de escritorio, estaciones de trabajo y pequeños servidores) se escribe en C o C++. A principios de la década de los ochenta, Bjarne Stroustrup desarrolló una extensión de C en los laboratorios Bell: C++. Este lenguaje proporciona un conjunto de características que “pulen” al lenguaje C pero, lo más impor- 1.8 Historia de C y C++ 7
  • 46. 8 Capítulo 1 Introducción a las computadoras, Internet y Web tante es que proporciona la capacidad de una programación orientada a objetos (que describiremos con más detalle en la sección 1.16 y en todo el libro). C++ es un lenguaje híbrido: es posible programar en un estilo parecido a C, en un estilo orientado a objetos, o en ambos. Una revolución se está gestando en la comunidad del software. Escribir software de manera rápida, correcta y económica es aún una meta difícil de alcanzar, en una época en que la demanda de nuevo y más poderoso software se encuentra a la alza. Los objetos, o dicho en forma más precisa (como veremos en la sección 1.16), las clases a partir de las cuales se crean los objetos, son en esencia componentes reutilizables de software. Hay objetos de: fecha, hora, audio, automóvil, personas, etcétera; de hecho, casi cualquier sustantivo puede represen- tarse como objeto de software en términos de atributos (como el nombre, color y tamaño) y comportamientos (como calcular, desplazarse y comunicarse). Los desarrolladores de software están descubriendo que utilizar una metodología de diseño e implementación modular y orientada a objetos puede hacer más productivos a los grupos de desarrollo de software, que mediante las populares técnicas de programación anteriores, como la programación estructurada. Los programas orientados a objetos son, a menudo, más fáciles de entender, corregir y modificar. Java es el lenguaje de programación orientada a objetos que más se utiliza en el mundo. 1.9 Historia de Java La contribución más importante a la fecha, por parte de la revolución del microprocesador, es que hizo posible el desarrollo de las computadoras personales, que ahora suman miles de millones a nivel mundial. Las computadoras personales han tenido un profundo impacto en la vida de las personas, y en la manera en que las empresas realizan y administran su negocio. Los microprocesadores están teniendo un profundo impacto en los dispositivos electrónicos inteligentes para uso doméstico. Al reconocer esto, Sun Microsystems patrocinó en 1991 un proyecto interno de investigación denominado Green, el cual desembocó en el desarrollo de un lenguaje basado en C++ al que su creador, James Gosling, llamó Oak debido a un roble que tenía a la vista desde su ventana en las oficinas de Sun. Posteriormente se descubrió que ya existía un lenguaje de computadora con el mismo nombre. Cuando un grupo de gente de Sun visitó una cafetería local, sugirieron el nombre Java (una variedad de café) y así se quedó. Pero el proyecto Green tuvo algunas dificultades. El mercado para los dispositivos electrónicos inteligentes de uso doméstico no se desarrollaba tan rápido a principios de los noventa como Sun había anticipado. El proyecto corría el riesgo de cancelarse. Pero para su buena fortuna, la popularidad de World Wide Web explotó en 1993 y la gente de Sun se dio cuenta inmediatamente del potencial de Java para agregar contenido dinámico, como interactividad y animaciones, a las páginas Web. Esto trajo nueva vida al proyecto. Sun anunció formalmente a Java en una importante conferencia que tuvo lugar en mayo de 1995. Java generó la atención de la comunidad de negocios debido al fenomenal interés en World Wide Web. En la actua- lidad, Java se utiliza para desarrollar aplicaciones empresariales a gran escala, para mejorar la funcionalidad de los servidores Web (las computadoras que proporcionan el contenido que vemos en nuestros exploradores Web), para proporcionar aplicaciones para los dispositivos domésticos (como teléfonos celulares, radiolocalizadores y asistentes digitales personales) y para muchos otros propósitos. 1.10 Bibliotecas de clases de Java Los programas en Java constan de varias piezas llamadas clases. Estas clases incluyen piezas llamadas métodos, los cuales realizan tareas y devuelven información cuando completan esas tareas. Los programadores pueden crear cada una de las piezas que necesitan para formar programas en Java. Sin embargo, la mayoría de los programado- res en Java aprovechan las ricas colecciones de clases existentes en las bibliotecas de clases de Java, que también se conocen como APIs (Interfaces de programación de aplicaciones) de Java. Por lo tanto, en realidad existen dos fundamentos para conocer el “mundo” de Java. El primero es el lenguaje Java en sí, de manera que usted pueda programar sus propias clases; el segundo son las clases incluidas en las extensas bibliotecas de clases de Java. A lo largo de este libro hablaremos sobre muchas bibliotecas de clases; que proporcionan principalmente los vendedo- res de compiladores, pero muchas de ellas las proporcionan vendedores de software independientes (ISVs). Observación de ingeniería de software 1.1 Utilice un método de construcción en bloques para crear programas. Evite reinventar la rueda: use piezas existentes siempre que sea posible. Esta reutilización de software es un beneficio clave de la programación orientada a objetos.
  • 47. Incluimos muchos tips como Observaciones de ingeniería de software a lo largo del texto para explicar los conceptos que afectan y mejoran la arquitectura y calidad de los sistemas de software. También resaltamos otras clases de tips, incluyendo las Buenas prácticas de programación (que le ayudarán a escribir programas más claros, comprensibles, de fácil mantenimiento, y fáciles de probar y depurar; es decir, eliminar errores de programación), los Errores comunes de programación (problemas de los que tenemos que cuidarnos y evitar), Tips de rendimiento (que servirán para escribir programas que se ejecuten más rápido y ocupen menos memo- ria), Tips de portabilidad (técnicas que le ayudarán a escribir programas que se ejecuten, con poca o ninguna modificación, en una variedad de computadoras; estos tips también incluyen observaciones generales acerca de cómo logra Java su alto grado de portabilidad), Tips para prevenir errores (que le ayudarán a eliminar errores de sus programas y, lo que es más importante, técnicas que le ayudarán a escribir programas libres de errores desde el principio) y Observaciones de apariencia visual (que le ayudarán a diseñar la apariencia visual de las interfaces gráficas de usuario de sus aplicaciones, además de facilitar su uso). Muchas de estas técnicas y prácticas son sólo guías. Usted deberá, sin duda, desarrollar su propio estilo de programación. Observación de ingeniería de software 1.2 Cuando programe en Java, generalmente utilizará los siguientes bloques de construcción: clases y métodos de las bibliotecas de clases, clases y métodos creados por usted mismo, y clases y métodos creados por otros y puestos a dispo- sición suya. La ventaja de crear sus propias clases y métodos es que sabe exactamente cómo funcionan y puede examinar el código en Java. La desventaja es el tiempo que consumen y el esfuerzo potencialmente complejo que se requiere. Tip de rendimiento 1.1 Utilizar las clases y métodos de las APIs de Java en vez de escribir sus propias versiones puede mejorar el rendimiento de sus programas, ya que estas clases y métodos están escritos cuidadosamente para funcionar de manera eficiente. Esta técnica también reduce el tiempo de desarrollo de los programas. Tip de portabilidad 1.1 Utilizar las clases y métodos de las APIs de Java en vez de escribir sus propias versiones mejora la portabilidad de sus programas, ya que estas clases y métodos se incluyen en todas las implementaciones de Java. Observación de ingeniería de software 1.3 Existen diversas bibliotecas de clases que contienen componentes reutilizables de software, y están disponibles a través de Internet y Web, muchas de ellas en forma gratuita. Para descargar la documentación de la API de Java, visite el sitio java.sun.com/javase/6/download.jsp de Sun para Java. 1.11 FORTRAN, COBOL, Pascal y Ada Se han desarrollado cientos de lenguajes de alto nivel, pero sólo unos cuantos han logrado una amplia aceptación. Fortran (FORmula TRANslator, Traductor de fórmulas) fue desarrollado por IBM Corporation a mediados de la década de los cincuenta para utilizarse en aplicaciones científicas y de ingeniería que requerían cálculos mate- máticos complejos. En la actualidad, Fortran se utiliza ampliamente en aplicaciones de ingeniería. COBOL (COmmon Business Oriented Language, Lenguaje común orientado a negocios) fue desa- rrollado a finales de la década de los cincuenta por fabricantes de computadoras, el gobierno estadounidense y usuarios de computadoras de la industria. COBOL se utiliza en aplicaciones comerciales que requieren de una manipulación precisa y eficiente de grandes volúmenes de datos. Gran parte del software de negocios aún se pro- grama en COBOL. Durante la década de los sesenta, muchos de los grandes esfuerzos para el desarrollo de software encontraron severas dificultades. Los itinerarios de software generalmente se retrasaban, los costos rebasaban en gran medida a los presupuestos y los productos terminados no eran confiables. La gente comenzó a darse cuenta de que el desarrollo de software era una actividad mucho más compleja de lo que habían imaginado. Las actividades de 1.11 FORTRAN, COBOL, Pascal y Ada 9
  • 48. 10 Capítulo 1 Introducción a las computadoras, Internet y Web investigación en la década de los sesenta dieron como resultado la evolución de la programación estructurada (un método disciplinado para escribir programas que fueran más claros, fáciles de probar y depurar, y más fáciles de modificar que los programas extensos producidos con técnicas anteriores). Uno de los resultados más tangibles de esta investigación fue el desarrollo del lenguaje de programación Pascal por el profesor Niklaus Wirth, en 1971. Pascal, cuyo nombre se debe al matemático y filósofo Blaise Pascal del siglo diecisiete, se diseñó para la enseñanza de la programación estructurada en ambientes académicos, y de inmediato se convirtió en el lenguaje de programación preferido en la mayoría de las universidades. Pascal carece de muchas de las características necesarias para poder utilizarse en aplicaciones comerciales, industriales y guber- namentales, por lo que no ha sido muy aceptado en estos entornos. El lenguaje de programación Ada se desarrolló bajo el patrocinio del Departamento de Defensa de los Esta- dos Unidos (DOD) durante la década de los setenta y los primeros años de la década de los ochenta. Cientos de lenguajes independientes se utilizaron para producir los sistemas de software masivos de comando y control del departamento de defensa. Éste quería un solo lenguaje que pudiera satisfacer la mayoría de sus necesidades. El nombre del lenguaje es en honor de Lady Ada Lovelace, hija del poeta Lord Byron. A Lady Lovelace se le atribuye el haber escrito el primer programa para computadoras en el mundo, a principios de la década de1800 (para la Máquina Analítica, un dispositivo de cómputo mecánico diseñado por Charles Babbage). Una de las característi- cas importantes de Ada se conoce como multitarea, la cual permite a los programadores especificar que muchas actividades ocurran en paralelo. Java, a través de una técnica que se conoce como subprocesamiento múltiple, tam- bién permite a los programadores escribir programas con actividades paralelas. 1.12 BASIC, Visual Basic, Visual C++, C# y .NET El lenguaje de programación BASIC (Beginner´s All-Purpose Symbolic Instruction Code, Código de instruc- ciones simbólicas de uso general para principiantes) fue desarrollado a mediados de la década de los sesenta en el Dartmouth College, como un medio para escribir programas simples. El propósito principal de BASIC era que los principiantes se familiarizaran con las técnicas de programación. El lenguaje Visual Basic de Microsoft se introdujo a principios de la década de los noventa para simplificar el desarrollo de aplicaciones para Microsoft Windows, y es uno de los lenguajes de programación más populares en el mundo. Las herramientas de desarrollo más recientes de Microsoft forman parte de su estrategia a nivel corporativo para integrar Internet y Web en las aplicaciones de computadora. Esta estrategia se implementa en la plataforma .NET de Microsoft, la cual proporciona a los desarrolladores las herramientas que necesitan para crear y ejecu- tar aplicaciones de computadora que puedan ejecutarse en computadoras distribuidas a través de Internet. Los tres principales lenguajes de programación de Microsoft son Visual Basic .NET (basado en el lenguaje BASIC original), Visual C++ .NET (basado en C++) y C# (basado en C++ y Java, y desarrollado expresamente para la plataforma .NET). Los desarrolladores que utilizan .NET pueden escribir componentes de software en el lenguaje con el que estén más familiarizados y formar aplicaciones al combinar esos componentes con los ya escritos en cualquier lenguaje .NET. 1.13 Entorno de desarrollo típico en Java Ahora explicaremos los pasos típicos usados para crear y ejecutar un programa en Java, utilizando un entorno de desarrollo de Java (el cual se ilustra en la figura 1.1). Por lo general, los programas en Java pasan a través de cinco fases: edición, compilación, carga, verificación y ejecución. Hablamos sobre estos conceptos en el contexto del JDK 6.0 de Sun Microsystems, Inc. Puede descargar el JDK más actualizado y su documentación en java.sun.com/javase/6/download.jsp. Siga cuidadosamente las instrucciones de instalación para el JDK que se proporcionan en la sección Antes de empezar (o en java.sun.com/ javase/6/webnotes/install/index.htm) para asegurarse de configurar su computadora apropiadamente para compilar y ejecutar programas en Java. También es conveniente que visite el centro para principiantes en Java (New to Java Center) de Sun en: java.sun.com/developer/onlineTraining/new2java/index.html [Nota: este sitio Web proporciona las instrucciones de instalación para Windows, Linux y MacOS X. Si usted no utiliza uno de estos sistemas operativos, consulte los manuales del entorno de Java de su sistema, o pregunte a su
  • 49. Para ejecutar el programa, la JVM lee los códigos de bytes y los compila “justo a tiempo” (JIT); es decir, los traduce en un lenguaje que la computadora pueda entender. A medida que se ejecuta el programa, existe la posibilidad de que almacene los valores de datos en la memoria principal Disco Editor El programa se crea en un editor y se almacena en disco, en un archivo con la terminación .java Fase 1: edición Disco Compilador El compilador crea los códigos de bytes y los almacena en disco, en un archivo con la terminación .class Fase 2: compilación Disco Cargador de clases El cargador de clases lee los archivos .class que contienen códigos de bytes del disco y coloca esos códigos de bytes en la memoria Fase 3: carga ... Verificador de código de bytes El verificador de código de bytes confirma que todos los códigos de bytes sean válidos y no violen las restricciones de seguridad de Java Fase 4: verificación ... Máquina Virtual de Java (JVM) Memoria principal Memoria principal Memoria principal Fase 5: ejecución ... Figura 1.1 | Entorno de desarrollo típico de Java. 1.13 Entorno de desarrollo típico en Java 11
  • 50. 12 Capítulo 1 Introducción a las computadoras, Internet y Web instructor cómo puede lograr estas tareas con base en el sistema operativo de su computadora. Además, tenga en cuenta que en ocasiones los vínculos Web se rompen a medida que las compañías evolucionan sus sitios Web. Si encuentra un problema con este vínculo o con cualquier otro al que se haga referencia en este libro, visite www. deitel.com para consultar la fe de erratas y notifíquenos su problema al correo electrónico deitel@deitel. com. Le responderemos a la brevedad]. Fase 1: Creación de un programa La fase 1 consiste en editar un archivo con un programa de edición (conocido comúnmente como editor). Usted escribe un programa en Java (conocido, por lo general, como código fuente) utilizando el editor, realiza las correc- ciones necesarias y guarda el programa en un dispositivo de almacenamiento secundario, como su disco duro. Un nombre de archivo que termina con la extensión .java indica que éste contiene código fuente en Java. En este libro asumimos que usted ya sabe cómo editar un archivo. Dos de los editores que se utilizan ampliamente en sistemas Linux son vi y emacs. En Windows, basta con usar un programa editor simple, como el Bloc de notas. También hay muchos editores de freeware y shareware disponibles para descargarlos de Internet, en sitios como www.download.com. Para las organizaciones que desarrollan sistemas de información extensos, hay entornos de desarrollo inte- grados (IDEs) disponibles de la mayoría de los proveedores de software, incluyendo Sun Microsystems. Los IDEs proporcionan herramientas que dan soporte al proceso de desarrollo del software, incluyendo editores para escribir y editar programas, y depuradores para localizar errores lógicos. Los IDEs populares son: Eclipse (www.eclipse.org), NetBeans (www.netbeans.org), JBuilder (www. borland.com), JCreator (www.jcreator.com), BlueJ (www.blueJ.org), jGRASP (www.jgrasp.org) y JEdit (www.jedit.org). Java Studio Enterprise de Sun Microsystems (developers.sun.com/prodtech/javatools/ jsenterprise/index.jsp) es una versión mejorada de NetBeans. [Nota: la mayoría de nuestros programas de ejemplo deben operar de manera apropiada con cualquier entorno de desarrollo integrado de Java que cuente con soporte para el JDK 6]. Fase 2: Compilación de un programa en Java para convertirlo en códigos de bytes En la fase 2, el programador utiliza el comando javac (el compilador de Java) para compilar un programa. Por ejemplo, para compilar un programa llamado Bienvenido.java, escriba javac Bienvenido.java en la ventana de comandos de su sistema (es decir, el indicador de MS-DOS en Windows 95/98/ME, el Símbolo del sistema en Windows NT/2000/XP, el indicador de shell en Linux o la aplicación Terminal en Mac OS X). Si el programa se compila, el compilador produce un archivo .class llamado Bienvenido.class, que contiene la versión compilada del programa. El compilador de Java traduce el código fuente en códigos de bytes que representan las tareas a ejecutar en la fase de ejecución (fase 5). La Máquina Virtual de Java (JVM), una parte del JDK y la base de la plataforma Java, ejecuta los códigos de bytes. Una máquina virtual (VM) es una aplicación de software que simula a una computadora, pero oculta el sistema operativo y el hardware subyacentes de los programas que interactúan con la VM. Si se implementa la misma VM en muchas plataformas computacionales, las aplicaciones que ejecute se podrán utilizar en todas esas plataformas. La JVM es una de las máquinas virtuales más utilizadas. A diferencia del lenguaje máquina, que depende del hardware de una computadora específica, los códigos de bytes son instrucciones independientes de la plataforma; no dependen de una plataforma de hardware en espe- cial. Entonces, los códigos de bytes de Java son portables (es decir, se pueden ejecutar en cualquier plataforma que contenga una JVM que comprenda la versión de Java en la que se compilaron). La JVM se invoca mediante el comando java. Por ejemplo, para ejecutar una aplicación llamada Bienvenido, debe escribir el comando java Bienvenido en una ventana de comandos para invocar la JVM, que a su vez inicia los pasos necesarios para ejecutar la aplica- ción. Esto comienza la fase 3. Fase 3: Cargar un programa en memoria En la fase 3, el programa debe colocarse en memoria antes de ejecutarse; a esto se le conoce como cargar. El cargador de clases toma los archivos .class que contienen los códigos de bytes del programa y los transfiere a
  • 51. la memoria principal. El cargador de clases también carga cualquiera de los archivos .class que su programa utilice, y que sean proporcionados por Java. Puede cargar los archivos .class desde un disco en su sistema o a través de una red (como la de su universidad local o la red de la empresa, o incluso desde Internet). Fase 4: Verificación del código de bytes En la fase 4, a medida que se cargan las clases, el verificador de códigos de bytes examina sus códigos de bytes para asegurar que sean válidos y que no violen las restricciones de seguridad. Java implementa una estrecha segu- ridad para asegurar que los programas que llegan a través de la red no dañen sus archivos o su sistema (como podrían hacerlo los virus de computadora y los gusanos). Fase 5: Ejecución En la fase 5, la JVM ejecuta los códigos de bytes del programa, realizando así las acciones especificadas por el mismo. En las primeras versiones de Java, la JVM era tan sólo un intérprete de códigos de bytes de Java. Esto hacía que la mayoría de los programas se ejecutaran con lentitud, ya que la JVM tenía que interpretar y ejecutar un código de bytes a la vez. Por lo general, las JVMs actuales ejecutan códigos de bytes usando una combinación de la interpretación y la denominada compilación justo a tiempo (JIT). En este proceso, la JVM analiza los códigos de bytes a medida que se interpretan, buscando puntos activos: partes de los códigos de bytes que se ejecutan con frecuencia. Para estas partes, un compilador justo a tiempo (JIT) (conocido como compilador HotSpot de Java) traduce los códigos de bytes al lenguaje máquina correspondiente a la computadora. Cuando la JVM encuentra estas partes compiladas nuevamente, se ejecuta el código en lenguaje máquina, que es más rápido. Por ende, los programas en Java en realidad pasan por dos fases de compilación: una en la cual el código fuente se traduce a código de bytes (para tener portabilidad a través de las JVMs en distintas plataformas computacionales) y otra en la que, durante la ejecución, los códigos de bytes se traducen en lenguaje máquina para la computadora actual en la que se ejecuta el programa. Problemas que pueden ocurrir en tiempo de ejecución Es probable que los programas no funcionen la primera vez. Cada una de las fases anteriores puede fallar, debido a diversos errores que describiremos en este texto. Por ejemplo, un programa en ejecución podría intentar una divi- sión entre cero (una operación ilegal para la aritmética con números enteros en Java). Esto haría que el programa de Java imprimiera un mensaje de error. Si esto ocurre, tendría que regresar a la fase de edición, hacer las correc- ciones necesarias y proseguir con las fases restantes nuevamente, para determinar que las correcciones resolvieron el(los) problema(s). [Nota: la mayoría de los programas en Java reciben o producen datos. Cuando decimos que un programa muestra un mensaje, por lo general, queremos decir que muestra ese mensaje en la pantalla de su computadora. Los mensajes y otros datos pueden enviarse a otros dispositivos, como los discos y las impresoras, o incluso a una red para transmitirlos a otras computadoras]. Error común de programación 1.1 Los errores, como la división entre cero, ocurren a medida que se ejecuta un programa, de manera que a estos errores se les llama errores en tiempo de ejecución. Los errores fatales en tiempo de ejecución hacen que los programas terminen de inmediato, sin haber realizado correctamente su trabajo. Los errores no fatales en tiempo de ejecución permiten a los programas ejecutarse hasta terminar su trabajo, lo que a menudo produce resultados incorrectos. 1.14 Generalidades acerca de Java y este libro Java es un poderoso lenguaje de programación. En ocasiones, los programadores experimentados se enorgullecen en poder crear un uso excéntrico, deformado e intrincado de un lenguaje. Ésta es una mala práctica de programa- ción; ya que hace que: los programas sean más difíciles de leer, se comporten en forma extraña, sean más difíciles de probar y depurar, y más difíciles de adaptarse a los cambiantes requerimientos. Este libro está enfocado en la claridad. A continuación se muestra nuestro primer tip de Buena práctica de programación: Buena práctica de programación 1.1 Escriba sus programas de Java en una manera simple y directa. A esto se le conoce algunas veces como KIS (Keep It Simple, simplifíquelo). No “extiendas” el lenguaje experimentando con usos excéntricos. 1.14 Generalidades acerca de Java y este libro 13
  • 52. 14 Capítulo 1 Introducción a las computadoras, Internet y Web Seguramente habrá escuchado que Java es un lenguaje portable y que los programas escritos en él pueden ejecutarse en diversas computadoras. En general, la portabilidad es una meta elusiva. Tip de portabilidad 1.2 Aunque es más fácil escribir programas portables en Java que en la mayoría de los demás lenguajes de programación, las diferencias entre compiladores, JVMs y computadoras pueden hacer que la portabilidad sea difícil de lograr. No basta con escribir programas en Java para garantizar su portabilidad. Tip para prevenir errores 1.1 Para asegurarse de que sus programas de Java trabajen correctamente para las audiencias a las que están destinados, pruébelos siempre en todos los sistemas en los que tenga pensado ejecutarlos. Comparamos nuestra presentación con la documentación de Java de Sun, para verificar que sea completa y precisa. Sin embargo, Java es un lenguaje extenso, y ningún libro puede cubrir todos los temas. En la página java.sun.com/javase/6/docs/api/index.html existe una versión de la documentación de las APIs de Java; también puede descargar esta documentación en su propia computadora, visitando java.sun.com/javase/6/ download.jsp. Para obtener detalles adicionales sobre muchos aspectos del desarrollo en Java, visite java.sun. com/reference/docs/index.html. Buena práctica de programación 1.2 Lea la documentación para la versión de Java que esté utilizando. Consulte esta documentación con frecuencia, para asegurarse de conocer la vasta colección de herramientas disponibles en Java, y para asegurarse de que las está utilizando correctamente. Buena práctica de programación 1.3 Su computadora y su compilador son buenos maestros. Si, después de leer cuidadosamente el manual de documenta- ción de Java, todavía no está seguro de cómo funciona alguna de sus características, experimente y vea lo que ocurre. Analice cada error o mensaje de advertencia que obtenga al compilar sus programas (a éstos se les llama errores en tiempo de compilación o errores de compilación), y corrija los programas para eliminar estos mensajes. Observación de ingeniería de software 1.4 Algunos programadores gustan de leer el código fuente para las clases de la API de Java, para determinar cómo fun- cionan las clases y aprender técnicas de programación adicionales. 1.15 Prueba de una aplicación en Java En esta sección, ejecutará su primera aplicación en Java e interactuará con ella. Para empezar, ejecutará una aplica- ción de ATM, la cual simula las transacciones que se llevan a cabo al utilizar una máquina de cajero automático, o ATM (por ejemplo, retirar dinero, realizar depósitos y verificar los saldos de las cuentas). Aprenderá a crear esta aplicación en el ejemplo práctico opcional orientado a objetos que se incluye en los capítulos 1-8 y 10. La figura 1.10 al final de esta sección sugiere otras aplicaciones interesantes que también puede probar después de terminar con la prueba del ATM. Para los fines de esta sección supondremos que está utilizando Microsoft Windows. En los siguientes pasos, ejecutará la aplicación y realizará diversas transacciones. Los elementos y la funcio- nalidad que podemos ver en esta aplicación son típicos de lo que aprenderá a programar en este libro. [Nota: utilizamos diversos tipos de letra para diferenciar las características que se ven en una pantalla (por ejemplo, el Símbolo del sistema) y los elementos que no se relacionan directamente con una pantalla. Nuestra convención es enfatizar las características de la pantalla como los títulos y menús (por ejemplo, el menú Archivo) en una fuente Helvetica sans-serif en negritas, y enfatizar los elementos que no son de la pantalla, como los nombres de archivo o los datos de entrada (como NombrePrograma.java) en una fuente Lucida sans-serif. Como tal vez ya se haya dado cuenta, cuando se ofrece la definición de algún término ésta aparece en negritas. En las figuras en esta sección, resaltamos en gris la entrada del usuario requerida por cada paso, y señalamos las partes importantes de la aplicación con líneas y texto. Para aumentar la visibilidad de estas características, modificamos el color de fondo de las ventanas del Símbolo del sistema].
  • 53. 1. Revise su configuración. Lea la sección Antes de empezar para verificar si instaló correctamente Java, y observe si copió los ejemplos del libro en su disco duro. 2 Localice la aplicación completa. Abra una ventana Símbolo del sistema. Para ello, puede seleccionar Inicio | Todos los programas | Accesorios | Símbolo del sistema. Cambie al directorio de la aplicación del ATM escribiendo cd C:ejemplosATM, y después oprima Intro (figura 1.2). El comando cd se utiliza para cambiar de directorio. 3. Ejecute la aplicación del ATM. Escriba el comando java EjemploPracticoATM (figura 1.3) oprima Intro. Recuerde que el comando java, seguido del nombre del archivo .class (en este caso, Ejemplo- PracticoATM), ejecuta la aplicación. Si especificamos la extensión .class al usar el comando java se produce un error. [Nota: los comandos en Java son sensibles a mayúsculas/minúsculas. Es importante escribir el nombre de esta aplicación con las letras A, T y M mayúsculas en “ATM”, una letra E mayúscu- la en “Ejemplo” y una letra P mayúscula en “Practico”. De no ser así, la aplicación no se ejecutará]. Si recibe el mensaje de error "Exception in thread "main" java.lang.NoClassDefFoundError: EjemploPracticoATM", entonces su sistema tiene un problema con CLASSPATH. Consulte la sección Antes de empezar para obtener instrucciones acerca de cómo corregir este problema. 4. Escriba un número de cuenta. Cuando la aplicación se ejecuta por primera vez, muestra el mensaje "Bienvenido!" y le pide un número de cuenta. Escriba 12345 en el indicador "Escriba su numero de cuenta:" (figura 1.4) y oprima Intro. Use el comando cd para cambiar de directorio Ubicación de los archivos de la aplicación del ATM Figura 1.2 | Abrir una ventana Símbolo del sistema en Windows XP y cambiar de directorio. Figura 1.3 | Uso del comando java para ejecutar la aplicación del ATM. Figura 1.4 | La aplicación pide al usuario un número de cuenta. 1.15 Prueba de una aplicación en Java 15 Mensaje de bienvenida del ATM Indicador que pide el número de cuenta
  • 54. 16 Capítulo 1 Introducción a las computadoras, Internet y Web 5. Escriba un NIP. Una vez que introduzca un número de cuenta válido, la aplicación mostrará el indica- dor "Escriba su NIP:". Escriba "54321" como su NIP (Número de Identificación Personal) válido y oprima Intro. A continuación aparecerá el menú principal del ATM, que contiene una lista de opciones (figura 1.5). 6. Revise el saldo de la cuenta. Seleccione la opción 1, "Ver mi saldo" del menú del ATM (figura 1.6). A continuación la aplicación mostrará dos números: el Saldo disponible ($1,000.00) y el Saldo total ($1,200.00). El saldo disponible es la cantidad máxima de dinero en su cuenta, disponible para retirarla en un momento dado. En algunos casos, ciertos fondos como los depósitos recientes, no están disponibles de inmediato para que el usuario pueda retirarlos, por lo que el saldo disponible puede ser menor que el saldo total, como en este caso. Después de mostrar la información de los saldos de la cuen- ta, se muestra nuevamente el menú principal de la aplicación. 7. Retire dinero de la cuenta. Seleccione la opción 2, "Retirar efectivo", del menú de la aplicación. A continuación aparecerá (figura 1.7) una lista de montos en dólares (por ejemplo: 20, 40, 60, 100 y 200). También tendrá la oportunidad de cancelar la transacción y regresar al menú principal. Retire $100 seleccionando la opción 4. La aplicación mostrará el mensaje "Tome su efectivo ahora" y regresará al menú principal. [Nota: por desgracia, esta aplicación sólo simula el comportamiento de un verdadero ATM, por lo cual no dispensa efectivo en realidad]. Figura 1.6 | La aplicación del ATM muestra la información del saldo de la cuenta del usuario. Figura 1.5 | El usuario escribe un número NIP válido y aparece el menú principal de la aplicación del ATM. Escriba un NIP válido Menú principal del ATM Información del saldo de la cuenta
  • 55. 8. Confirme que la información de la cuenta se haya actualizado. En el menú principal, seleccione la opción 1 nuevamente para ver el saldo actual de su cuenta (figura 1.8). Observe que tanto el saldo dis- ponible como el saldo total se han actualizado para reflejar su transacción de retiro. 9. Finalice la transacción. Para finalizar su sesión actual en el ATM, seleccione, del menú principal, la opción 4, "Salir" (figura 1.9). El ATM saldrá del sistema y mostrará un mensaje de despedida al usua- rio. A continuación, la aplicación regresará a su indicador original, pidiendo el número de cuenta del siguiente usuario. 10. Salga de la aplicación del ATM y cierre la ventana Símbolo del sistema. La mayoría de las aplicacio- nes cuentan con una opción para salir y regresar al directorio del Símbolo del sistema desde el cual se ejecutó la aplicación. Un ATM real no proporciona al usuario la opción de apagar la máquina ATM. En vez de ello, cuando el usuario ha completado todas las transacciones deseadas y elige la opción del menú para salir, el ATM se reinicia a sí mismo y muestra un indicador para el número de cuenta del siguiente Figura 1.7 | Se retira el dinero de la cuenta y la aplicación regresa al menú principal. Figura 1.8 | Verificación del nuevo saldo. 1.15 Prueba de una aplicación en Java 17 Menú de retiro del ATM Confirmación de la información del saldo de la cuenta actualizado después de la transacción de retiro
  • 56. 18 Capítulo 1 Introducción a las computadoras, Internet y Web usuario. Como se muestra en la figura 1.9, la aplicación del ATM se comporta de manera similar. Al elegir la opción del menú para salir sólo se termina la sesión del usuario actual con el ATM, no toda la aplicación completa. Para salir realmente de la aplicación del ATM, haga clic en el botón Cerrar (x) en la esquina superior derecha de la ventana Símbolo del sistema. Al cerrar la ventana, la aplicación ter- mina su ejecución. Aplicaciones adicionales incluidas en Cómo programar en Java, 7ª edición La figura 1.10 lista unas cuantas de los cientos de aplicaciones que se incluyen en los ejemplos y ejercicios del libro. Estos programas presentan muchas de las poderosas y divertidas características de Java. Ejecute estos progra- mas para que conozca más acerca de las aplicaciones que aprenderá a construir en este libro de texto. La carpeta de ejemplos para este capítulo contiene todos los archivos requeridos para ejecutar cada aplicación. Sólo escriba los comandos que se listan en la figura 1.10 en una ventana de Símbolo del sistema. Figura 1.9 | Finalización de una sesión de transacciones con el ATM. Nombre de la aplicación Capítulo(s) en donde se ubica Comandos a ejecutar Tic-Tac-Toe Capítulos 8 y 24 cd C:ejemploscap01Tic-Tac-Toe Java PruebaTicTacToe Juego de adivinanza Capítulo 11 cd C:ejemploscap01JuegoAdivinanza Java JuegoAdivinanza Animador de logotipos Capítulo 21 cd C:ejemploscap01AnimadorLogotipos Java AnimadorLogotipos Pelota rebotadora Capítulo 23 cd C:ejemploscap01PelotaRebotadora Java PelotaRebotadora Figura 1.10 | Ejemplos de aplicaciones de Java adicionales, incluidas en Cómo programar en Java, 7ª edición. Mensaje de despedida del ATM Indicador del número de cuenta para el siguiente usuario
  • 57. 1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML Ahora empezaremos nuestra primera introducción al tema de la orientación a objetos, una manera natural de pensar acerca del mundo real y de escribir programas de cómputo. Los capítulos 1-8 y 10 terminan con una sección breve titulada Ejemplo práctico de Ingeniería de Software, en la cual presentamos una introducción cui- dadosamente guiada al tema de la orientación a objetos. Nuestro objetivo aquí es ayudarle a desarrollar una forma de pensar orientada a objetos, y de presentarle el Lenguaje Unificado de Modelado™ (UML™), un lenguaje gráfico que permite a las personas que diseñan sistemas de software utilizar una notación estándar en la industria para representarlos. En esta única sección requerida (1.16), presentamos los conceptos y la terminología de la orientación a objetos. Las secciones opcionales en los capítulos 2-8 y 10 presentan el diseño y la implementación orientados a objetos de un software para una máquina de cajero automático (ATM) simple. Las secciones tituladas Ejemplo práctico de Ingeniería de Software al final de los capítulos 2 al 7: analizan un documento de requerimientos típico que describe un sistema de software (el ATM) que construirá determinan los objetos requeridos para implementar ese sistema establecen los atributos que deben tener estos objetos fijan los comportamientos que exhibirán estos objetos especifican la forma en que los objetos deben interactuar entre sí para cumplir con los requerimientos del sistema Las secciones tituladas Ejemplo práctico de Ingeniería de Software al final de los capítulos 8 y 10 modifican y mejoran el diseño presentado en los capítulos 2 al 7. El apéndice M contiene una implementación completa y funcional en Java del sistema ATM orientado a objetos. Usted experimentará una concisa pero sólida introducción al diseño orientado a objetos con UML. Además, afinará sus habilidades para leer código al ver paso a paso la implementación del ATM en Java, cuidadosamente escrita y bien documentada, en el apéndice M. Conceptos básicos de la tecnología de objetos Comenzaremos nuestra introducción al tema de la orientación a objetos con cierta terminología clave. En cual- quier parte del mundo real puede ver objetos: gente, animales, plantas, automóviles, aviones, edificios, compu- tadoras, etcétera. Los humanos pensamos en términos de objetos. Los teléfonos, casas, semáforos, hornos de microondas y enfriadores de agua son sólo unos cuantos objetos más. Los programas de cómputo, como los programas de Java que leerá en este libro y los que usted mismo escriba, están compuestos por muchos objetos de software con capacidad de interacción. En ocasiones dividimos a los objetos en dos categorías: animados e inanimados. Los objetos animados están “vivos” en cierto sentido; se mueven a su alrededor y hacen cosas. Por otro lado, los objetos inanimados no se mueven por su propia cuenta. Sin embargo, los objetos de ambos tipos tienen ciertas cosas en común. Todos ellos tienen atributos (como tamaño, forma, color y peso), y todos exhiben comportamientos (por ejemplo, una pelota rueda, rebota, se infla y desinfla; un bebé llora, duerme, gatea, camina y parpadea; un automóvil acelera, frena y da vuelta; una toalla absorbe agua). Estudiaremos los tipos de atributos y comportamientos que tienen los objetos de software. Los humanos aprenden acerca de los objetos existentes estudiando sus atributos y observando sus compor- tamientos. Distintos objetos pueden tener atributos similares y pueden exhibir comportamientos similares. Por ejemplo, pueden hacerse comparaciones entre los bebés y los adultos, y entre los humanos y los chimpancés. El diseño orientado a objetos (DOO) modela el software en términos similares a los que utilizan las per- sonas para describir objetos del mundo real. Este diseño aprovecha las relaciones entre las clases, en donde los objetos de cierta clase (como una clase de vehículos) tienen las mismas características; los automóviles, camiones, pequeños vagones rojos y patines tienen mucho en común. El DOO también aprovecha las relaciones de heren- cia, en donde las nuevas clases de objetos se derivan absorbiendo las características de las clases existentes y agre- gando sus propias características únicas. Un objeto de la clase “convertible” ciertamente tiene las características de la clase más general “automóvil” pero, de manera más específica, el techo de un convertible puede ponerse y quitarse. • • • • • 1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML 19
  • 58. 20 Capítulo 1 Introducción a las computadoras, Internet y Web El diseño orientado a objetos proporciona una manera natural e intuitiva de ver el proceso de diseño de soft- ware: a saber, modelando los objetos por sus atributos y comportamientos, de igual forma que como describimos los objetos del mundo real. El DOO también modela la comunicación entre los objetos. Así como las personas se envían mensajes unas a otras (por ejemplo, un sargento ordenando a un soldado que permanezca firme), los objetos también se comunican mediante mensajes. Un objeto cuenta de banco puede recibir un mensaje para reducir su saldo por cierta cantidad, debido a que el cliente ha retirado esa cantidad de dinero. El DOO encapsula (es decir, envuelve) los atributos y las operaciones (comportamientos) en los objetos; los atributos y las operaciones de un objeto se enlazan íntimamente entre sí. Los objetos tienen la propiedad de ocultamiento de información. Esto significa que los objetos pueden saber cómo comunicarse entre sí a través de interfaces bien definidas, pero por lo general no se les permite saber cómo se implementan otros objetos; los detalles de la implementación se ocultan dentro de los mismos objetos. Por ejemplo, podemos conducir un automóvil con efectividad, sin necesidad de saber los detalles acerca de cómo funcionan internamente los moto- res, las transmisiones y los sistemas de escape; siempre y cuando sepamos cómo usar el pedal del acelerador, el pedal del freno, el volante, etcétera. Más adelante veremos por qué el ocultamiento de información es tan impres- cindible para la buena ingeniería de software. Los lenguajes como Java son orientados a objetos. La programación en dichos lenguajes se llama progra- mación orientada a objetos (POO), y permite a los programadores de computadoras implementar un diseño orientado a objetos como un sistema funcional. Por otra parte, los lenguajes como C son por procedimientos, de manera que la programación tiende a ser orientada a la acción. En C, la unidad de programación es la función. Los grupos de acciones que realizan cierta tarea común se forman en funciones, y las funciones se agrupan para formar programas. En Java, la unidad de programación es la clase a partir de la cual se instancian (crean) los obje- tos en un momento dado. Las clases en Java contienen métodos (que implementan operaciones y son similares a las funciones en C) y campos (que implementan atributos). Los programadores de Java se concentran en crear clases. Cada clase contiene campos, además del conjunto de métodos que manipulan esos campos y proporcionan servicios a clientes (es decir, otras clases que utilizan esa clase). El programador utiliza las clases existentes como bloques de construcción para crear nuevas clases. Las clases son para los objetos lo que los planos de construcción, para las casas. Así como podemos construir muchas casas a partir de un plano, podemos instanciar (crear) muchos objetos a partir de una clase. No puede cocinar alimentos en la cocina de un plano de construcción; puede cocinarlos en la cocina de una casa. Las clases pueden tener relaciones con otras clases. Por ejemplo, en un diseño orientado a objetos de un banco, la clase “cajero” necesita relacionarse con las clases “cliente”, “cajón de efectivo”, “bóveda”, etcétera. A estas relaciones se les llama asociaciones. Al empaquetar el software en forma de clases, los sistemas de software posteriores pueden reutilizar esas clases. Los grupos de clases relacionadas se empaquetan comúnmente como componentes reutilizables. Así como los corredores de bienes raíces dicen a menudo que los tres factores más importantes que afectan el precio de los bienes raíces son “la ubicación, la ubicación y la ubicación”, las personas en la comunidad de software dicen a menudo que los tres factores más importantes que afectan el futuro del desarrollo de software son “la reutiliza- ción, la reutilización y la reutilización”. Reutilizar las clases existentes cuando se crean nuevas clases y programas es un proceso que ahorra tiempo y esfuerzo; también ayuda a los programadores a crear sistemas más confiables y efectivos, ya que las clases y componentes existentes a menudo han pasado por un proceso extenso de prueba, depuración y optimización del rendimiento. Evidentemente, con la tecnología de objetos podemos crear la mayoría del software que necesitaremos median- te la combinación de clases, así como los fabricantes de automóviles combinan las piezas intercambiables. Cada nueva clase que usted cree tendrá el potencial de convertirse en una valiosa pieza de software, que usted y otros programadores podrán usar para agilizar y mejorar la calidad de los futuros esfuerzos de desarrollo de software. Introducción al análisis y diseño orientados a objetos (A/DOO) Pronto estará escribiendo programas en Java. ¿Cómo creará el código para sus programas? Tal vez, como muchos programadores principiantes, simplemente encenderá su computadora y empezará a teclear. Esta metodología puede funcionar para programas pequeños (como los que presentamos en los primeros capítulos del libro) pero ¿qué haría usted si se le pidiera crear un sistema de software para controlar miles de máquinas de cajero automá- tico para un importante banco? O suponga que le pidieron trabajar en un equipo de 1,000 desarrolladores de software para construir el nuevo sistema de control de tráfico aéreo de Estados Unidos. Para proyectos tan grandes y complejos, no podría simplemente sentarse y empezar a escribir programas.
  • 59. Para crear las mejores soluciones, debe seguir un proceso detallado para analizar los requerimientos de su proyecto (es decir, determinar qué es lo que se supone debe hacer el sistema) y desarrollar un diseño que cumpla con esos requerimientos (es decir, decidir cómo debe hacerlo el sistema). Idealmente usted pasaría por este proceso y revisaría cuidadosamente el diseño (o haría que otros profesionales de software lo revisaran) antes de escribir cualquier código. Si este proceso implica analizar y diseñar su sistema desde un punto de vista orientado a objetos, lo llamamos un proceso de análisis y diseño orientado a objetos (A/DOO). Los programadores experimentados saben que el análisis y el diseño pueden ahorrar innumerables horas, ya que les ayudan a evitar un método de de- sarrollo de un sistema mal planeado, que tiene que abandonarse en plena implementación, con la posibilidad de desperdiciar una cantidad considerable de tiempo, dinero y esfuerzo. A/DOO es el término genérico para el proceso de analizar un problema y desarrollar un método para resol- verlo. Los pequeños problemas como los que se describen en los primeros capítulos de este libro no requieren de un proceso exhaustivo de A/DOO. Podría ser suficiente con escribir pseudocódigo antes de empezar a escribir el código en Java; el pseudocódigo es un medio informal para expresar la lógica de un programa. En realidad no es un lenguaje de programación, pero podemos usarlo como un tipo de bosquejo para guiarnos mientras escribimos nuestro código. En el capítulo 4 presentamos el pseudocódigo. A medida que los problemas y los grupos de personas que los resuelven aumentan en tamaño, los métodos de A/DOO se vuelven más apropiados que el pseudocódigo. Idealmente, un grupo debería acordar un proce- so estrictamente definido para resolver su problema, y establecer también una manera uniforme para que los miembros del grupo se comuniquen los resultados de ese proceso entre sí. Aunque existen diversos procesos de A/DOO, hay un lenguaje gráfico para comunicar los resultados de cualquier proceso A/DOO que se ha vuelto muy popular. Este lenguaje, conocido como Lenguaje Unificado de Modelado (UML), se desarrolló a mediados de la década de los noventa, bajo la dirección inicial de tres metodologistas de software: Grady Booch, James Rumbaugh e Ivar Jacobson. Historia de UML En la década de los ochenta, un creciente número de empresas comenzó a utilizar la POO para crear sus aplica- ciones, lo cual generó la necesidad de un proceso estándar de A/DOO. Muchos metodologistas (incluyendo a Booch, Rumbaugh y Jacobson) produjeron y promocionaron, por su cuenta, procesos separados para satisfacer esta necesidad. Cada uno de estos procesos tenía su propia notación, o “lenguaje” (en forma de diagramas gráfi- cos), para transmitir los resultados del análisis y el diseño. A principios de la década de los noventa, diversas compañías (e inclusive diferentes divisiones dentro de la misma compañía) utilizaban sus propios procesos y notaciones únicos. Al mismo tiempo, estas compañías que- rían utilizar herramientas de software que tuvieran soporte para sus procesos particulares. Con tantos procesos, se les dificultó a los distribuidores de software proporcionar dichas herramientas. Evidentemente era necesario contar con una notación y procesos estándar. En 1994, James Rumbaugh se unió con Grady Booch en Rational Software Corporation (ahora una división de IBM), y comenzaron a trabajar para unificar sus populares procesos. Pronto se unió a ellos Ivar Jacobson. En 1996, el grupo liberó las primeras versiones de UML para la comunidad de ingeniería de software, solicitan- do retroalimentación. Casi al mismo tiempo, una organización conocida como Object Management Group™ (OMG™, Grupo de administración de objetos) hizo una invitación para participar en la creación de un lenguaje común de modelado. El OMG (www.omg.org) es una organización sin fines de lucro que promueve la estanda- rización de las tecnologías orientadas a objetos, emitiendo lineamientos y especificaciones como UML. Varias empresas (entre ellas HP, IBM, Microsoft, Oracle y Rational Software) habían reconocido ya la necesidad de un lenguaje común de modelado. Estas compañías formaron el consorcio UML Partners (Socios de UML) en respuesta a la solicitud de proposiciones por parte del OMG (el consorcio que desarrolló la versión 1.1 de UML y la envió al OMG). La propuesta fue aceptada y, en 1997, el OMG asumió la responsabilidad del mantenimiento y revisión de UML en forma continua. La versión 2 que está ahora disponible marca la primera modificación importante al UML desde el estándar de la versión 1.1 de 1997. A lo largo de este libro, presentaremos la termi- nología y notación de UML 2. ¿Qué es UML? UML es ahora el esquema de representación gráfica más utilizado para modelar sistemas orientados a objetos. Evi- dentemente ha unificado los diversos esquemas de notación populares. Aquellos quienes diseñan sistemas utilizan el lenguaje (en forma de diagramas) para modelar sus sistemas. 1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML 21
  • 60. 22 Capítulo 1 Introducción a las computadoras, Internet y Web Una característica atractiva es su flexibilidad. UML es extensible (es decir, capaz de mejorarse con nuevas características) e independiente de cualquier proceso de A/DOO específico. Los modeladores de UML tienen la libertad de diseñar sistemas utilizando varios procesos, pero todos los desarrolladores pueden ahora expresar esos diseños con un conjunto de notaciones gráficas estándar. UML es un lenguaje gráfico complejo, con muchas características. En nuestras secciones del Ejemplo prácti- co de Ingeniería de Software, presentamos un subconjunto conciso y simplificado de estas características. Luego utilizamos este subconjunto para guiarlo a través de la experiencia de su primer diseño con UML, la cual está dirigida a los programadores principiantes orientados a objetos en cursos de programación de primer o segundo semestre. Recursos Web de UML Para obtener más información acerca de UML, consulte los siguientes sitios Web: www.uml.org Esta página de recursos de UML del Grupo de Administración de Objetos (OMG) proporciona documentos de la especificación para UML y otras tecnologías orientadas a objetos. www.ibm.com/software/rational/uml Ésta es la página de recursos de UML para IBM Rational, sucesor de Rational Software Corporation (la compañía que creó a UML). en.wikipedia.org/wiki/UML La definición de Wikipedia de UML. Este sitio también ofrece vínculos a muchos recursos adicionales de UML. es.wikipedia.org/wiki/UML La definición de Wikipedia del UML en español. Lecturas recomendadas Los siguientes libros proporcionan información acerca del diseño orientado a objetos con UML: Ambler, S. The Object Primer: Agile Model-Driven Development with UML 2.0, Third Edition. Nueva York: Cambridge University Press, 2005. Arlow, J. e I. Neustadt. UML and the Unified Process: Practical Object-Oriented Analysis and Design, Second Edition. Boston: Addison-Wesley Professional, 2006. Fowler, M. UML Distilled, Third Edition: A Brief Guide to the Standard Object Modeling Language. Boston: Addison- Wesley Professional, 2004. Rumbaugh, J., I. Jacobson y G. Booch. The Unified Modeling Language User Guide, Second Edition. Boston: Addison- Wesley Professional, 2006. Ejercicios de autorrepaso de la sección 1.16 1.1 Liste tres ejemplos de objetos reales que no mencionamos. Para cada objeto, liste varios atributos y compor- tamientos. 1.2 El pesudocódigo es __________. a) otro término para el A/DOO b) un lenguaje de programación utilizado para visualizar diagramas de UML c) un medio informal para expresar la lógica de un programa d) un esquema de representación gráfica para modelar sistemas orientados a objetos 1.3 El UML se utiliza principalmente para __________. a) probar sistemas orientados a objetos b) diseñar sistemas orientados a objetos c) implementar sistemas orientados a objetos d) a y b Respuestas a los ejercicios de autorrepaso de la sección 1.16 1.1 [Nota: las respuestas pueden variar]. a) Los atributos de una televisión incluyen el tamaño de la pantalla, el número de colores que puede mostrar, su canal actual y su volumen actual. Una televisión se enciende y se apaga, cam-
  • 61. bia de canales, muestra video y reproduce sonidos. b) Los atributos de una cafetera incluyen el volumen máximo de agua que puede contener, el tiempo requerido para preparar una jarra de café y la temperatura del plato calentador bajo la jarra de café. Una cafetera se enciende y se apaga, prepara café y lo calienta. c) Los atributos de una tortuga incluyen su edad, el tamaño de su caparazón y su peso. Una tortuga camina, se mete en su caparazón, emerge del mismo y come vegetación. 1.2 c. 1.3 b. 1.17 Web 2.0 Literalmente, la Web explotó a mediados de la década de los noventa, pero surgieron tiempos difíciles a principios del año 2000, debido al desplome económico de punto com. Al resurgimiento que empezó aproximadamente en el 2004, se le conoce como Web 2.0. La primera Conferencia sobre Web 2.0 se realizó en el 2004. Un año después, el término “Web 2.0” obtuvo aproximadamente 10 millones de coincidencias en el motor de búsqueda Google, para crecer hasta 60 millones al año siguiente. A Google se le considera en muchas partes como la compañía carac- terística de Web 2.0. Algunas otras son Craigslist (listados gratuitos de anuncios clasificados), Flickr (sitio para compartir fotos), del.icio.us (sitios favoritos de carácter social), YouTube (sitio para compartir videos), MySpace y FaceBook (redes sociales), Salesforce (software de negocios que se ofrece como servicio en línea), Second Life (un mundo virtual), Skype (telefonía por Internet) y Wikipedia (una enciclopedia en línea gratuita). En Deitel & Associates, inauguramos nuestra Iniciativa de Negocios por Internet basada en Web 2.0 en el año 2005. Estamos investigando las tecnologías clave de Web 2.0 y las utilizamos para crear negocios en Internet. Compartimos nuestra investigación en forma de Centros de recursos en www.deitel.com/resourcecenters. html. Cada semana anunciamos los Centros de recursos más recientes en nuestro boletín de correo electrónico Deitel® Buzz Online (www.deitel.com/newsletter/subscribe.html). Cada uno de estos centros lista muchos vínculos a contenido y software gratuito en Internet. En este libro incluimos un tratamiento detallado sobre los servicios Web (capítulo 28) y presentamos la nue- va metodología de desarrollo de aplicaciones, conocida como mashups (apéndice H), en la que puede desarrollar rápidamente aplicaciones poderosas e intrigantes, al combinar servicios Web complementarios y otras fuentes de información provenientes de dos o más organizaciones. Un mashup popular es www.housingmaps.com, el cual combina los listados de bienes raíces de www.craigslist.org con las capacidades de los mapas de Google Maps para mostrar las ubicaciones de los apartamentos para renta en un área dada. Ajax es una de las tecnologías más importantes de Web 2.0. Aunque el uso del término explotó en el 2005, es sólo un término que nombra a un grupo de tecnologías y técnicas de programación que han estado en uso desde finales de la década de los noventa. Ajax ayuda a las aplicaciones basadas en Internet a funcionar como las aplicaciones de escritorio; una tarea difícil, dado que dichas aplicaciones sufren de retrasos en la transmisión, a medida que los datos se intercambian entre su computadora y las demás computadoras en Internet. Mediante el uso de Ajax, las aplicaciones como Google Maps han logrado un desempeño excelente, además de la apariencia visual de las aplicaciones de escritorio. Aunque no hablaremos sobre la programación “pura” con Ajax en este libro (que es bastante compleja), en el capítulo 27 mostraremos cómo crear aplicaciones habilitadas para Ajax mediante el uso de los componentes de JavaServer Faces (JSF) habilitados para Ajax. Los blogs son sitios Web (actualmente hay como 60 millones de ellos) similares a un diario en línea, en donde las entradas más recientes aparecen primero. Los “bloggers” publican rápidamente sus opiniones acerca de las noticias, lanzamientos de productos, candidatos políticos, temas controversiales, y de casi todo lo demás. A la colección de todos los blogs y de la comunidad de “blogging” se le conoce como blogósfera, y cada vez está teniendo más influencia. Technorati es el líder en motores de búsqueda de blogs. Las fuentes RSS permiten a los sitios enviar información a sus suscriptores. Un uso común de las fuentes RSS es enviar las publicaciones más recientes de los blogs, a las personas que se suscriben a éstos. Los flujos de información RSS en Internet están creciendo de manera exponencial. Web 3.0 es otro nombre para la siguiente generación de la Web, que también se le conoce como Web Semántica. Casi todo el contenido de Web 1.0 estaba basado en HTML. Web 2.0 está utilizando cada vez más el XML, en especial en tecnologías como las fuentes RSS. Web 3.0 utilizará aún más el XML, creando una “Web de significado”. Si usted es un estudiante que busca un excelente artículo de presentación o un tema para una tesis, o si es un emprendedor que busca oportunidades de negocios, dé un vistazo a nuestro Centro de recursos sobre Web 3.0. 1.17 Web 2.0 23
  • 62. 24 Capítulo 1 Introducción a las computadoras, Internet y Web Para seguir los últimos desarrollos en Web 2.0, visite www.techcrunch.com y www.slashdot.org, y revise la lista creciente de Centros de recursos relacionados con Web 2.0 en www.deitel.com/resourcecenters.html. 1.18 Tecnologías de software En esta sección hablaremos sobre varias “palabras de moda” que escuchará en la comunidad de desarrollo de soft- ware. Creamos Centros de recursos sobre la mayoría de estos temas, y hay muchos por venir. Agile Software Development (Desarrollo Ágil de Software) es un conjunto de metodologías que tratan de implementar software rápidamente, con menos recursos que las metodologías anteriores. Visite los sitios de Agile Alliance (www.agilealliance.org) y Agile Manifesto (www.agilemanifesto.org). También puede visitar el sitio en español www.agile-spain.com. Extreme programming (XP) (Programación extrema (PX)) es una de las diversas tecnologías de desarrollo ágil. Trata de desarrollar software con rapidez. El software se libera con frecuencia en pequeños incrementos, para alentar la rápida retroalimentación de los usuarios. PX reconoce que los requerimientos de los usuarios cambian a menudo, y que el software debe cumplir con esos requerimientos rápidamente. Los programadores trabajan en pares en una máquina, de manera que la revisión del código se realiza de inmediato, a medida que se crea el código. Todos en el equipo deben poder trabajar con cualquier parte del código. Refactoring (Refabricación) implica la reformulación del código para hacerlo más claro y fácil de mantener, al tiempo que se preserva su funcionalidad. Se emplea ampliamente con las metodologías de desarrollo ágil. Hay muchas herramientas de refabricación disponibles para realizar las porciones principales de la reformulación de manera automática. Los patrones de diseño son arquitecturas probadas para construir software orientado a objetos flexible y que pueda mantenerse (vea el apéndice P Web adicional). El campo de los patrones de diseño trata de enumerar a los patrones recurrentes, y de alentar a los diseñadores de software para que los reutilicen y puedan desarrollar un software de mejor calidad con menos tiempo, dinero y esfuerzo. Programación de juegos. El negocio de los juegos de computadora es más grande que el negocio de las películas de estreno. Ahora hay cursos universitarios, e incluso maestrías, dedicados a las técnicas sofisticadas de software que se utilizan en la programación de juegos. Vea nuestros Centros de recursos sobre Programación de juegos y Proyectos de programación. El software de código fuente abierto es un estilo de desarrollo de software que contrasta con el desarrollo propietario, que dominó los primeros años del software. Con el desarrollo de código fuente abierto, individuos y compañías contribuyen sus esfuerzos en el desarrollo, mantenimiento y evolución del software, a cambio del derecho de usar ese software para sus propios fines, comúnmente sin costo. Por lo general, el código fuente abierto se examina a detalle por una audiencia más grande que el software propietario, por lo cual los errores se eliminan con más rapidez. El código fuente abierto también promueve más innovación. Sun anunció recientemente que piensa abrir el código fuente de Java. Algunas de las organizaciones de las que se habla mucho en la comunidad de código fuente abierto son Eclipse Foundation (el IDE de Eclipse es popular para el desarrollo de software en Java), Mozilla Foundation (creadores del explorador Web Firefox), Apache Software Foundation (creadores del servidor Web Apache) y SourceForge (que proporciona las herramientas para administrar proyectos de código fuente abierto y en la actualidad cuenta con más de 100,000 proyectos en desarrollo). Linux es un sistema operativo de código fuente abierto, y uno de los más grandes éxitos de la iniciativa de código fuente abierto. MySQL es un sistema de administración de bases de datos con código fuente abierto. PHP es el lenguaje de secuencias de comandos del lado servidor para Internet de código fuente abierto más popular, para el desarrollo de aplicaciones basadas en Internet. LAMP es un acrónimo para el conjunto de tecnologías de código fuente abierto que utilizaron muchos desarrolladores para crear aplicaciones Web: representa a Linux, Apache, MySQL y PHP (o Perl, o Python; otros dos lenguajes que se utilizan para propósitos similares). Ruby on Rails combina el lenguaje de secuencias de comandos Ruby con el marco de trabajo para aplicacio- nes Web Rails, desarrollado por la compañía 37Signals. Su libro, Getting Real, es una lectura obligatoria para los desarrolladores de aplicaciones Web de la actualidad; puede leerlo sin costo en gettingreal.37signals.com/ toc.php. Muchos desarrolladores de Ruby on Rails han reportado un considerable aumento en la productividad, en comparación con otros lenguajes al desarrollar aplicaciones Web con uso intensivo de bases de datos. Por lo general, el software siempre se ha visto como un producto; la mayoría del software aún se ofrece de esta manera. Si desea ejecutar una aplicación, compra un paquete de software de un distribuidor. Después instala ese software en su computadora y lo ejecuta según sea necesario. Al aparecer nuevas versiones del software, usted lo
  • 63. actualiza, a menudo con un costo considerable. Este proceso puede volverse incómodo para empresas con decenas de miles de sistemas que deben mantenerse en una extensa colección de equipo de cómputo. Con Software as a Service (SAAS), el software se ejecuta en servidores ubicados en cualquier parte de Internet. Cuando se actualiza ese servidor, todos los clientes a nivel mundial ven las nuevas características; no se necesita instalación local. Usted accede al servidor a través de un explorador Web; éstos son bastante portables, por lo que puede ejecutar las mis- mas aplicaciones en distintos tipos de computadoras, desde cualquier parte del mundo. Salesforce.com, Google, Microsoft Office Live y Windows Live ofrecen SAAS. 1.19 Conclusión Este capítulo presentó los conceptos básicos de hardware y software, y los conceptos de la tecnología básica de objetos, incluyendo clases, objetos, atributos, comportamientos, encapsulamiento, herencia y polimorfismo. Hablamos sobre los distintos tipos de lenguajes de programación y cuáles son los más utilizados. Conoció los pasos para crear y ejecutar una aplicación de Java mediante el uso del JDK 6 de Sun. El capítulo exploró la historia de Internet y World Wide Web, y la función de Java en cuanto al desarrollo de aplicaciones cliente/servidor dis- tribuidas para Internet y Web. También aprendió acerca de la historia y el propósito de UML: el lenguaje gráfico estándar en la industria para modelar sistemas de software. Por último, realizó pruebas de una o más aplicaciones de Java, similares a los tipos de aplicaciones que aprenderá a programar en este libro. En el capítulo 2 creará sus primeras aplicaciones en Java. Verá ejemplos que muestran cómo los programas imprimen mensajes en pantalla y obtienen información del usuario para procesarla. Analizaremos y explicaremos cada ejemplo, para facilitarle el proceso de aprender a programar en Java. 1.20 Recursos Web Esta sección proporciona muchos recursos que le serán de utilidad a medida que aprenda Java. Los sitios incluyen recursos de Java, herramientas de desarrollo de Java para estudiantes y profesionales, y nuestros propios sitios Web, en donde podrá encontrar descargas y recursos asociados con este libro. También le proporcionaremos un vínculo, en donde podrá suscribirse a nuestro boletín de correo electrónico Deitel® Buzz Online sin costo. Sitios Web de Deitel & Associates www.deitel.com Contiene actualizaciones, correcciones y recursos adicionales para todas las publicaciones Deitel. www.deitel.com/newsletter/subscribe.html Suscríbase al boletín de correo electrónico gratuito Deitel® Buzz Online, para seguir el programa de publicaciones de Deitel & Associates, incluyendo actualizaciones y fe de erratas para este libro. www.prenhall.com/deitel La página de inicio de Prentice Hall para las publicaciones Deitel. Aquí encontrará información detallada sobre los productos, capítulos de ejemplo y Sitios Web complementarios con recursos para estudiantes e instructores. www.deitel.com/books/jhtp7/ La página de inicio de Deitel & Associates para Cómo programar en Java, 7a edición. Aquí encontrará vínculos a los ejemplos del libro (que también se incluyen en el CD que viene con el libro) y otros recursos. Centros de recursos de Deitel sobre Java www.deitel.com/Java/ Nuestro Centro de recursos sobre Java se enfoca en la enorme cantidad de contenido gratuito sobre Java, disponible en línea. Empiece aquí su búsqueda de recursos, descargas, tutoriales, documentación, libros, libros electrónicos, diarios, artículos, blogs y más, que le ayudarán a crear aplicaciones en Java. www.deitel.com/JavaSE6Mustang/ Nuestro Centro de recursos sobre Java SE 6 (Mustang) es su guía para la última versión de Java. Este sitio incluye los mejores recursos que encontramos en línea, para ayudarle a empezar con el desarrollo en Java SE 6. www.deitel.com/JavaEE5/ Nuestro Centro de recursos sobre Java Enterprise Edition 5 (Java EE 5). www.deitel.com/JavaCertification/ Nuestro Centro de recursos de evaluación de certificación y valoración. 1.20 Recursos Web 25
  • 64. 26 Capítulo 1 Introducción a las computadoras, Internet y Web www.deitel.com/JavaDesignPatterns/ Nuestro Centro de recursos sobre los patrones de diseño de Java. En su libro, Design Patterns: Elements of Reusable Object-Oriented Software (Boston: Addison-Wesley Professional, 1995), la “Banda de los cuatro” (E. Gamma, R. Helm, R. Jonson y J. Vlissides) describen 23 patrones de diseño que proporcionan arquitecturas demostradas para construir sistemas de software orientados a objetos. En este centro de recursos, encontrará discusiones sobre muchos de éstos y otros patrones de diseño. www.deitel.com/CodeSearchEngines/ Nuestro Centro de recursos sobre Motores de Búsqueda de Código y Sitios de Código incluye recursos que los desarro- lladores utilizan para buscar código fuente en línea. www.deitel.com/ProgrammingProjects/ Nuestro Centro de recursos sobre Proyectos de Programación es su guía para proyectos de programación estudiantiles en línea. Sitios Web de Sun Microsystems java.sun.com/developer/onlineTraining/new2java/index.html El centro “New to Java Center” (Centro para principiantes en Java) en el sitio Web de Sun Microsystems ofrece recursos de capacitación en línea para ayudarle a empezar con la programación en Java. java.sun.com/javase/6/download.jsp La página de descarga para el Kit de Desarrollo de Java 6 (JDK 6) y su documentación. El JDK incluye todo lo necesario para compilar y ejecutar sus aplicaciones en Java SE 6 (Mustang). java.sun.com/javase/6/webnotes/install/index.html Instrucciones para instalar el JDK 6 en plataformas Solaris, Windows y Linux. java.sun.com/javase/6/docs/api/index.html El sitio en línea para la documentación de la API de Java SE 6. java.sun.com/javase La página de inicio para la plataforma Java Standard Edition. java.sun.com La página de inicio de la tecnología Java de Sun ofrece descargas, referencias, foros, tutoriales en línea y mucho más. java.sun.com/reference/docs/index.html El sitio de documentación de Sun para todas las tecnologías de Java. developers.sun.com La página de inicio de Sun para los desarrolladores de Java proporciona descargas, APIs, ejemplos de código, artículos con asesoría técnica y otros recursos sobre las mejores prácticas de desarrollo en Java. Editores y Entornos de Desarrollo Integrados www.eclipse.org El entorno de desarrollo Eclipse puede usarse para desarrollar código en cualquier lenguaje de programación. Puede descargar el entorno y varios complementos (plug-ins) de Java para desarrollar sus programas en Java. www.netbeans.org El IDE NetBeans. Una de las herramientas de desarrollo para Java más populares, de distribución gratuita. borland.com/products/downloads/download_jbuilder.html Borland ofrece una versión Foundation Edition gratuita de su popular IDE JBuilder para Java. Este sitio también ofrece versiones de prueba de 30 días de las ediciones Enterprise y Developer. www.blueJ.org BlueJ: una herramienta gratuita diseñada para ayudar a enseñar Java orientado a objetos a los programadores novatos. www.jgrasp.org Descargas, documentación y tutoriales sobre jGRASP. Esta herramienta muestra representaciones visuales de programas en Java, para ayudar a su comprensión. www.jedit.org jEdit: un editor de texto escrito en Java. developers.sun.com/prodtech/javatools/jsenterprise/index.jsp El IDE Sun Java Studio Enterprise: la versión mejorada de NetBeans de Sun Microsystems.
  • 65. www.jcreator.com JCreator: un IDE popular para Java. JCreator Lite Edition está disponible como descarga gratuita. También está dispo- nible una versión de prueba de 30 días de JCreator Pro Edition. www.textpad.com TextPad: compile, edite y ejecute sus programas en Java desde este editor, que proporciona coloreo de sintaxis y una interfaz fácil de usar. www.download.com Un sitio que contiene descargas de aplicaciones de freeware y shareware, incluyendo programas editores. Sitios de recursos adicionales sobre Java www.javalobby.org Proporciona noticias actualizadas sobre Java, foros en donde los desarrolladores pueden intercambiar tips y consejos, y una base de conocimiento de Java extensa, que organiza artículos y descargas en toda la Web. www.jguru.com Ofrece foros, descargas, artículos, cursos en línea y una extensa colección de FAQs (Preguntas frecuentes) sobre Java. www.javaworld.com Ofrece recursos para desarrolladores de Java, como artículos, índices de libros populares sobre Java, tips y FAQs. www.ftponline.com/javapro La revista JavaPro contiene artículos mensuales, tips de programación, reseñas de libros y mucho más. sys-con.com/java/ El Diario de Desarrolladores de Java de Sys-Con Media ofrece artículos, libros electrónicos y otros recursos sobre Java. Resumen Sección 1.1 Introducción • Java se ha convertido en el lenguaje de elección para implementar aplicaciones basadas en Internet y software para dispositivos que se comunican a través de una red. • Java Enterprise Edition (Java EE) está orientada hacia el desarrollo de aplicaciones de redes distribuidas de gran escala, y aplicaciones basadas en Web. • Java Micro Edition (Java ME) está orientada hacia el desarrollo de aplicaciones para dispositivos pequeños, con memoria limitada, como teléfonos celulares, radiolocalizadotes y PDAs. Sección 1.2 ¿Qué es una computadora? • Una computadora es un dispositivo capaz de realizar cálculos y tomar decisiones lógicas a velocidades de millones (incluso de miles de millones) de veces más rápidas que los humanos. • Las computadoras procesan los datos bajo el control de conjuntos de instrucciones llamadas programas de cómputo. Los programas guían a las computadoras a través de acciones especificadas por gente llamada programadores de computadoras. • Una computadora está compuesta por varios dispositivos conocidos como hardware. A los programas que se ejecu- tan en una computadora se les denomina software. Sección 1.3 Organización de una computadora • Casi todas las computadoras pueden representarse mediante seis unidades lógicas o secciones. • La unidad de entrada obtiene información desde los dispositivos de entrada y pone esta información a disposición de las otras unidades para que pueda procesarse. • La unidad de salida toma información que ya ha sido procesada por la computadora y la coloca en los diferentes dispositivos de salida, para que esté disponible fuera de la computadora. • La unidad de memoria es la sección de “almacén” de acceso rápido, pero con relativa baja capacidad, de la compu- tadora. Retiene la información que se introduce a través de la unidad de entrada, para que la información pueda estar disponible de manera inmediata para procesarla cuando sea necesario. También retiene la información procesada hasta que ésta pueda ser colocada en los dispositivos de salida por la unidad de salida. • La unidad aritmética y lógica (ALU) es la responsable de realizar cálculos (como suma, resta, multiplicación y divi- sión) y tomar decisiones. Resumen 27
  • 66. • La unidad central de procesamiento (CPU) coordina y supervisa la operación de las demás secciones. La CPU le indica a la unidad de entrada cuándo debe grabarse la información dentro de la unidad de memoria, a la ALU cuán- do debe utilizarse la información de la unidad de memoria para los cálculos, y a la unidad de salida cuándo enviar la información desde la unidad de memoria hasta ciertos dispositivos de salida. • Los multiprocesadores contienen múltiples CPUs y, por lo tanto, pueden realizar muchas operaciones de manera simultánea. • La unidad de almacenamiento secundario es la sección de “almacén” de alta capacidad y de larga duración de la computadora. Los programas o datos que no se encuentran en ejecución por las otras unidades, normalmente se colocan en dispositivos de almacenamiento secundario hasta que son requeridos de nuevo. Sección 1.4 Los primeros sistemas operativos • Las primeras computadoras eran capaces de realizar solamente una tarea o trabajo a la vez. • Los sistemas operativos se desarrollaron para facilitar el uso de la computadora. • La multiprogramación significa la operación simultánea de muchas tareas. • Con el tiempo compartido, la computadora ejecuta una pequeña porción del trabajo de un usuario y después pro- cede a dar servicio al siguiente usuario, con la posibilidad de proporcionar el servicio a cada usuario varias veces por segundo. Sección 1.5 Computación personal, distribuida y cliente/servidor • En 1977, Apple Computer popularizó el fenómeno de la computación personal. • En 1981, IBM, el vendedor de computadoras más grande del mundo, introdujo la Computadora Personal (PC) de IBM, que legitimó rápidamente la computación en las empresas, en la industria y en las organizaciones guberna- mentales. • En la computación distribuida, en vez de que la computación se realice sólo en una computadora central, se distri- buye mediante redes a los sitios en donde se realiza el trabajo de la empresa. • Los servidores almacenan datos que pueden utilizar las computadoras cliente distribuidas a través de la red, de ahí el término de computación cliente/servidor. • Java se está utilizando ampliamente para escribir software para redes de computadoras y para aplicaciones cliente/ servidor distribuidas. Sección 1.6 Internet y World Wide Web • Internet es accesible por más de mil millones de computadoras y dispositivos controlados por computadora. • Con la introducción de World Wide Web, Internet se ha convertido explosivamente en uno de los principales meca- nismos de comunicación en todo el mundo. Sección 1.7 Lenguajes máquina, lenguajes ensambladores y lenguajes de alto nivel • Cualquier computadora puede entender de manera directa sólo su propio lenguaje máquina. • El lenguaje máquina es el “lenguaje natural” de una computadora. • Por lo general, los lenguajes máquina consisten en cadenas de números (que finalmente se reducen a 1s y 0s) que instruyen a las computadoras para realizar sus operaciones más elementales, una a la vez. • Los lenguajes máquina son dependientes de la máquina. • Los programadores empezaron a utilizar abreviaturas del inglés para representar las operaciones elementales. Estas abreviaturas formaron la base de los lenguajes ensambladores. • Los programas traductores conocidos como ensambladores se desarrollaron para convertir los primeros programas en lenguaje ensamblador a lenguaje máquina, a la velocidad de la computadora. • Los lenguajes de alto nivel permiten a los programadores escribir instrucciones parecidas al lenguaje inglés cotidiano, y contienen notaciones matemáticas de uso común. • Java es el lenguaje de programación de alto nivel más utilizado en todo el mundo. • Los programas intérpretes ejecutan los programas en lenguajes de alto nivel directamente. Sección 1.8 Historia de C y C++ • Java evolucionó de C++, el cual evolucionó de C, que a su vez evolucionó de BCPL y B. • El lenguaje C evolucionó a partir de B, gracias al trabajo de Dennis Ritchie en los laboratorios Bell. Inicialmente, se hizo muy popular como lenguaje de desarrollo para el sistema operativo UNIX. • A principios de la década de los ochenta, Bjarne Stroustrup desarrolló una extensión de C en los laboratorios Bell: C++. Este lenguaje proporciona un conjunto de características que “pulen” al lenguaje C, además de la capacidad de una programación orientada a objetos. 28 Capítulo 1 Introducción a las computadoras, Internet y Web
  • 67. Sección 1.9 Historia de Java • Java se utiliza para desarrollar aplicaciones empresariales a gran escala, para mejorar la funcionalidad de los servidores Web, para proporcionar aplicaciones para los dispositivos domésticos y para muchos otros propósitos. • Los programas en Java consisten en piezas llamadas clases. Las clases incluyen piezas llamadas métodos, los cuales realizan tareas y devuelven información cuando se completan estas tareas. Sección 1.10 Bibliotecas de clases de Java • La mayoría de los programadores en Java aprovechan las ricas colecciones de clases existentes en las bibliotecas de clases de Java, que también se conocen como APIs (Interfaces de programación de aplicaciones) de Java. • La ventaja de crear sus propias clases y métodos es que sabe cómo funcionan y puede examinar el código. La desven- taja es que se requiere una cantidad considerable de tiempo y un esfuerzo potencialmente complejo. Sección 1.11 FORTRAN, COBOL, Pascal y Ada • Fortran (FORmula TRANslator, Traductor de fórmulas) fue desarrollado por IBM Corporation a mediados de la década de los cincuenta para utilizarse en aplicaciones científicas y de ingeniería que requerían cálculos matemáticos complejos. • COBOL (COmmon Business Oriented Language, Lenguaje común orientado a negocios) se utiliza en aplicaciones comerciales que requieren de una manipulación precisa y eficiente de grandes volúmenes de datos. • Las actividades de investigación en la década de los sesenta dieron como resultado la evolución de la programación estructurada (un método disciplinado para escribir programas que sean más claros, fáciles de probar y depurar, y más fáciles de modificar que los programas extensos producidos con técnicas anteriores). • Pascal se diseñó para la enseñanza de la programación estructurada en ambientes académicos, y de inmediato se convirtió en el lenguaje de programación preferido en la mayoría de las universidades. • El lenguaje de programación Ada se desarrolló bajo el patrocinio del Departamento de Defensa de los Estados Unidos (DOD) para satisfacer la mayoría de sus necesidades. Una característica de Ada conocida como multitarea permite a los programadores especificar que muchas actividades ocurrirán en paralelo. Java, a través de una técnica que se conoce como subprocesamiento múltiple, también permite a los programadores escribir programas con activi- dades paralelas. Sección 1.12 BASIC, Visual Basic, Visual C++, C# y .NET • BASIC fue desarrollado a mediados de la década de los sesenta para escribir programas simples. • El lenguaje Visual Basic de Microsoft simplifica el desarrollo de aplicaciones para Windows. • La plataforma .NET de Microsoft integra Internet y Web en las aplicaciones de computadora. Sección 1.13 Entorno de desarrollo típico en Java • Por lo general, los programas en Java pasan a través de cinco fases: edición, compilación, carga, verificación y ejecu- ción. • La fase 1 consiste en editar un archivo con un editor. Usted escribe un programa utilizando el editor, realiza las correcciones necesarias y guarda el programa en un dispositivo de almacenamiento secundario, tal como su disco duro. • Un nombre de archivo que termina con la extensión .java indica que éste contiene código fuente en Java. • Los entornos de desarrollo integrados (IDEs) proporcionan herramientas que dan soporte al proceso de desarrollo del software, incluyendo editores para escribir y editar programas, y depuradores para localizar errores lógicos. • En la fase 2, el programador utiliza el comando javac para compilar un programa. • Si un programa se compila, el compilador produce un archivo .class que contiene el programa compilado. • El compilador de Java traduce el código fuente de Java en códigos de bytes que representan las tareas a ejecutar. La Máquina Virtual de Java (JVM) ejecuta los códigos de bytes. • En la fase 3, de carga, el cargador de clases toma los archivos .class que contienen los códigos de bytes del progra- ma y los transfiere a la memoria principal. • En la fase 4, a medida que se cargan las clases, el verificador de códigos de bytes examina sus códigos de bytes para asegurar que sean válidos y que no violen las restricciones de seguridad de Java. • En la fase 5, la JVM ejecuta los códigos de bytes del programa. Sección 1.16 Ejemplo práctico de Ingeniería de Software: introducción a la tecnología de objetos y UML • El Lenguaje Unificado de Modelado (UML) es un lenguaje gráfico que permite a las personas que crean sistemas representar sus diseños orientados a objetos en una notación común. Resumen 29
  • 68. • El diseño orientado a objetos (DOO) modela los componentes de software en términos de objetos reales. • Los objetos tienen la propiedad de ocultamiento de la información: por lo general, no se permite a los objetos de una clase saber cómo se implementan los objetos de otras clases. • La programación orientada a objetos (POO) implementa diseños orientados a objetos. • Los programadores de Java se concentran en crear sus propios tipos definidos por el usuario, conocidos como clases. Cada clase contiene datos y métodos que manipulan a esos datos y proporcionan servicios a los clientes. • Los componentes de datos de una clase son los atributos o campos; los componentes de operación son los métodos. • Las clases pueden tener relaciones con otras clases; a estas relaciones se les llama asociaciones. • El proceso de empaquetar software en forma de clases hace posible que los sistemas de software posteriores reutilicen esas clases. • A una instancia de una clase se le llama objeto. • El proceso de analizar y diseñar un sistema desde un punto de vista orientado a objetos se llama análisis y diseño orientados a objetos (A/DOO). Terminología 30 Capítulo 1 Introducción a las computadoras, Internet y Web Ada ALU (unidad aritmética y lógica) ANSI C API de Java (Interfaz de Programación de Aplicaciones) atributo BASIC bibliotecas de clases C C# C++ cargador de clases clase .class, archivo COBOL código de bytes compilador compilador HotSpot™ compilador JIT (justo a tiempo) componente reutilizable comportamiento computación cliente/servidor computación distribuida computación personal computadora contenido dinámico CPU (unidad central de procesamiento) disco diseño orientado a objetos (DOO) dispositivo de entrada dispositivo de salida documento de requerimientos editor encapsulamiento ensamblador entrada/salida (E/S) enunciado del problema error en tiempo de compilación error en tiempo de ejecución error fatal en tiempo de ejecución error no fatal en tiempo de ejecución fase de carga fase de compilación fase de edición fase de ejecución fase de verificación flujo de datos Fortran Hardware herencia HTML (Lenguaje de Marcado de Hipertexto) IDE (Entorno Integrado de Desarrollo) Internet Intérprete Java Java Enterprise Edition (Java EE) .java, extensión de nombre de archivo Java Micro Edition (Java ME) Java Standard Edition (Java SE) java, intérprete javac, compilador KIS (simplifíquelo) Kit de Desarrollo de Java (JDK) LAN (red de área local) lenguaje de alto nivel lenguaje ensamblador lenguaje máquina Lenguaje Unificado de Modelado (UML) Máquina virtual de Java (JVM) memoria principal método método de código activo (live-code) Microsoft Internet Explorer, navegador Web modelado multiprocesador multiprogramación .NET objeto ocultamiento de información
  • 69. Pascal plataforma portabilidad programa de cómputo programa traductor programación estructurada programación orientada a objetos (POO) programación por procedimientos programador de computadoras pseudocódigo reutilización de software servidor de archivos sistema heredado sistema operativo software subprocesamiento múltiple Sun Microsystems tiempo compartido tipo definido por el usuario traducción unidad aritmética y lógica (ALU) unidad central de procesamiento (CPU) unidad de almacenamiento secundario unidad de entrada unidad de memoria unidad de salida verificador de código de bytes Visual Basic .NET Visual C++ .NET World Wide Web Ejercicios de autoevaluación 31 Ejercicios de autoevaluación 1.1 Complete las siguientes oraciones: a) La compañía que popularizó la computación personal fue ______________. b) La computadora que legitimó la computación personal en los negocios y la industria fue _____________. c) Las computadoras procesan los datos bajo el control de conjuntos de instrucciones llamadas ___________. d) Las seis unidades lógicas clave de la computadora son _____________, _____________, _____________, _____________, _____________ y _____________. e) Los tres tipos de lenguajes descritos en este capítulo son _____________, _____________ y __________ ____________________________. f) Los programas que traducen programas en lenguaje de alto nivel a lenguaje máquina se denominan _____ _____________. g) La __________ permite a los usuarios de computadora localizar y ver documentos basados en multimedia sobre casi cualquier tema, a través de Internet. h) _____________, permite a un programa en Java realizar varias actividades en paralelo. 1.2 Complete cada una de las siguientes oraciones relacionadas con el entorno de Java: a) El comando _____________ del JDK ejecuta una aplicación en Java. b) El comando _____________ del JDK compila un programa en Java. c) El archivo de un programa en Java debe terminar con la extensión de archivo _____________. d) Cuando se compila un programa en Java, el archivo producido por el compilador termina con la exten- sión _____________. e) El archivo producido por el compilador de Java contiene _____________ que se ejecutan mediante la Máquina Virtual de Java. 1.3 Complete cada una de las siguientes oraciones (basándose en la sección 1.16): a) Los objetos tienen una propiedad que se conoce como _____________; aunque éstos pueden saber cómo comunicarse con los demás objetos a través de interfaces bien definidas, generalmente no se les permite saber cómo están implementados los otros objetos. b) Los programadores de Java se concentran en crear _____________, que contienen campos y el conjunto de métodos que manipulan a esos campos y proporcionan servicios a los clientes. c) Las clases pueden tener relaciones con otras clases; a éstas relaciones se les llama _____________. d) El proceso de analizar y diseñar un sistema desde un punto de vista orientado a objetos se conoce como _____________ . e) El DOO aprovecha las relaciones _____________, en donde se derivan nuevas clases de objetos al absorber las características de las clases existentes y después agregar sus propias características únicas. f) _____________ es un lenguaje gráfico que permite a las personas que diseñan sistemas de software utilizar una notación estándar en la industria para representarlos. g) El tamaño, forma, color y peso de un objeto se consideran _____________ del mismo.
  • 70. 32 Capítulo 1 Introducción a las computadoras, Internet y Web Respuestas a los ejercicios de autoevaluación 1.1 a) Apple. b) PC de IBM. c) programas. d) unidad de entrada, unidad de salida, unidad de memoria, unidad aritmética y lógica, unidad central de procesamiento, unidad de almacenamiento secundario. e) lenguajes máquina, lenguajes ensambladores, lenguajes de alto nivel. f) compiladores. g) World Wide Web. h) Subprocesa- miento múltiple. 1.2 a) java. b) javac. c) .java. d) .class. e) códigos de bytes. 1.3 a) ocultamiento de información. b) clases. c) asociaciones. d) análisis y diseño orientados a objetos (A/ DOO). e) herencia. f) El Lenguaje Unificado de Modelado (UML). g) atributos. Ejercicios 1.4 Clasifique cada uno de los siguientes elementos como hardware o software: a) CPU b) compilador de Java c) JVM d) unidad de entrada e) editor 1.5 Complete cada una de las siguientes oraciones: a) La unidad lógica de la computadora que recibe información desde el exterior de la computadora para que ésta la utilice se llama _____________. b) El proceso de indicar a la computadora cómo resolver problemas específicos se llama _____________. c) _____________ es un tipo de lenguaje computacional que utiliza abreviaturas del inglés para las instruc- ciones de lenguaje máquina. d) _____________ es una unidad lógica de la computadora que envía información, que ya ha sido procesada por la computadora, a varios dispositivos, de manera que la información pueda utilizarse fuera de la compu- tadora. e) _____________ y _____________ son unidades lógicas de la computadora que retienen información. f) _____________ es una unidad lógica de la computadora que realiza cálculos. g) _____________ es una unidad lógica de la computadora que toma decisiones lógicas. h) Los lenguajes _____________ son los más convenientes para que el programador pueda escribir programas rápida y fácilmente. i) Al único lenguaje que una computadora puede entender directamente se le conoce como el __________ de esa computadora. j) _____________ es una unidad lógica de la computadora que coordina las actividades de todas las demás unidades lógicas. 1.6 Indique la diferencia entre los términos error fatal y error no fatal. ¿Por qué sería preferible experimentar un error fatal, en vez de un error no fatal? 1.7 Complete cada una de las siguientes oraciones: a) _____________ se utiliza ahora para desarrollar aplicaciones empresariales de gran escala, para mejorar la funcionalidad de los servidores Web, para proporcionar aplicaciones para dispositivos domésticos y para muchos otros fines más. b) _____________ se diseñó específicamente para la plataforma .NET, de manera que los programadores pudieran migrar fácilmente a .NET. c) Inicialmente, _____________ se hizo muy popular como lenguaje de desarrollo para el sistema operativo UNIX. d) _____________ fue desarrollado a mediados de la década de los sesenta en el Dartmouth College, como un medio para escribir programas simples. e) _____________ fue desarrollado por IBM Corporation a mediados de la década de los cincuenta para utilizarse en aplicaciones científicas y de ingeniería que requerían cálculos matemáticos complejos. f) _____________ se utiliza para aplicaciones comerciales que requieren la manipulación precisa y eficiente de grandes cantidades de datos.
  • 71. g) El lenguaje de programación _____________ fue desarrollado por Bjarne Stroustrup a principios de la década de los ochenta, en los laboratorios Bell. 1.8 Complete cada una de las siguientes oraciones (basándose en la sección 1.13): a) Por lo general, los programas de Java pasan a través de cinco fases: _____________, _____________, _____________, _____________ y _____________. b) Un _____________ proporciona muchas herramientas que dan soporte al proceso de desarrollo de soft- ware, como los editores para escribir y editar programas, los depuradores para localizar los errores lógicos en los programas, y muchas otras características más. c) El comando java invoca al _____________, que ejecuta los programas de Java. d) Un(a) _____________ es una aplicación de software que simula a una computadora, pero oculta el sistema operativo subyacente y el hardware de los programas que interactúan con la VM. e) Un programa _____________ puede ejecutarse en múltiples plataformas. f) El _____________ toma los archivos .class que contienen los códigos de bytes del programa y los trans- fiere a la memoria principal. g) El _____________ examina los códigos de bytes para asegurar que sean válidos. 1.9 Explique las dos fases de compilación de los programas de Java. Ejercicios 33
  • 72. Introducción a las aplicaciones en Java OBJETIVO S En este capítulo aprenderá a: Escribir aplicaciones simples en Java. Utilizar las instrucciones de entrada y salida. Familiarizarse con los tipos primitivos de Java. Comprender los conceptos básicos de la memoria. Utilizar los operadores aritméticos. Comprender la precedencia de los operadores aritméticos. Escribir instrucciones para tomar decisiones. Utilizar los operadores relacionales y de igualdad. Q Q Q Q Q Q Q Q ¿Qué hay en un nombre? A eso a lo que llamamos rosa, si le diéramos otro nombre conservaría su misma fragancia dulce. — William Shakespeare Al hacer frente a una decisión, siempre me pregunto, “¿Cuál será la solución más divertida?” —Peggy Walker “Toma un poco más de té”, dijo el conejo blanco a Alicia, con gran seriedad. “No he tomado nada todavía.” Contestó Alicia en tono ofendido, “Entonces no puedo tomar más”. “Querrás decir que no puedes tomar menos”, dijo el sombrerero loco, “es muy fácil tomar más que nada”. —Lewis Carroll 2
  • 73. 2.1 Introducción Ahora presentaremos la programación de aplicaciones en Java, que facilita una metodología disciplinada para el diseño de programas. La mayoría de los programas en Java que estudiará en este libro procesan información y muestran resultados. Le presentaremos seis ejemplos que demuestran cómo sus programas pueden mostrar men- sajes y cómo pueden obtener información del usuario para procesarla. Comenzaremos con varios ejemplos que simplemente muestran mensajes en la pantalla. Después demostraremos un programa que obtiene dos números de un usuario, calcula su suma y muestra el resultado. Usted aprenderá a realizar varios cálculos aritméticos y a guardar sus resultados para usarlos más adelante. El último ejemplo en este capítulo demuestra los fundamentos de toma de decisiones, al mostrarle cómo comparar números y después mostrar mensajes con base en los resul- tados de la comparación. Por ejemplo, el programa muestra un mensaje que indica que dos números son iguales sólo si tienen el mismo valor. Analizaremos cada ejemplo, una línea a la vez, para ayudarle a aprender a programar en Java. En los ejercicios del capítulo proporcionamos muchos problemas retadores y divertidos, para ayudarle a aplicar las habilidades que aprenderá aquí. 2.2 Su primer programa en Java: imprimir una línea de texto Cada vez que utiliza una computadora, ejecuta diversas aplicaciones que realizan tareas por usted. Por ejemplo, su aplicación de correo electrónico le permite enviar y recibir mensajes de correo, y su navegador Web le permite ver páginas de sitios Web en todo el mundo. Los programadores de computadoras crean dichas aplicaciones, escribiendo programas de cómputo. Una aplicación en Java es un programa de computadora que se ejecuta cuando usted utiliza el comando java para iniciar la Máquina Virtual de Java (JVM). Consideremos una aplicación simple que muestra una línea de texto. (Más adelante en esta sección hablaremos sobre cómo compilar y ejecutar una aplicación). El programa y su salida se muestran en la figura 2.1. La salida aparece en el recuadro al final del programa. El programa ilustra varias características importantes del lenguaje. Java utiliza notaciones que pueden parecer extrañas a los no pro- gramadores. Además, cada uno de los programas que presentamos en este libro tiene números de línea incluidos para su conveniencia; los números de línea no son parte de los programas en Java. Pronto veremos que la línea 9 se encarga del verdadero trabajo del programa; a saber, mostrar la frase Bienvenido a la programacion en Java!en la pantalla. Ahora consideremos cada línea del programa en orden. La línea 1 // Fig. 2.1: Bienvenido1.java empieza con //, indicando que el resto de la línea es un comentario. Los programadores insertan comentarios para documentar los programas y mejorar su legibilidad. Los comentarios también ayudan a otras personas a leer y comprender un programa. El compilador de Java ignora estos comentarios, de manera que la computadora no hace nada cuando el programa se ejecuta. Por convención, comenzamos cada uno de los programas con un comentario, el cual indica el número de figura y el nombre del archivo. 2.1 Introducción 2.2 Su primer programa en Java: imprimir una línea de texto 2.3 Modificación de nuestro primer programa en Java 2.4 Cómo mostrar texto con printf 2.5 Otra aplicación en Java: suma de enteros 2.6 Conceptos acerca de la memoria 2.7 Aritmética 2.8 Toma de decisiones: operadores de igualdad y relacionales 2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de requerimientos 2.10 Conclusión Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios Pla n g e ne r a l 2.2 Su primer programa en Java: imprimir una línea de texto 35
  • 74. 36 Capítulo 2 Introducción a las aplicaciones en Java Un comentario que comienza con // se llama comentario de fin de línea (o de una sola línea), ya que termina al final de la línea en la que aparece. Un comentario que se especifica con // puede empezar también en medio de una línea, y continuar solamente hasta el final de esa línea (como en las líneas 11 y 13). Los comentarios tradicionales (también conocidos como comentarios de múltiples líneas), como el que se muestra a continuación /* Éste es un comentario Tradicional. Puede dividirse en muchas líneas */ se distribuyen en varias líneas. Este tipo de comentario comienza con el delimitador /* y termina con */. El com- pilador ignora todo el texto que esté entre los delimitadores. Java incorporó los comentarios tradicionales y los comentarios de fin de línea de los lenguajes de programación C y C++, respectivamente. En este libro utilizamos co- mentarios de fin de línea. Java también cuenta con comentarios Javadoc, que están delimitados por /** y */. Al igual que con los comentarios tradicionales, el compilador ignora todo el texto entre los delimitadores de los comentarios Javadoc. Estos comentarios permiten a los programadores incrustar la documentación del programa directamente en éste. Dichos comentarios son el formato preferido en la industria. El programa de utilería javadoc (parte del Kit de Desarrollo de Java SE) lee esos comentarios y los utiliza para preparar la documentación de su programa, en for- mato HTML. Hay algunas sutilezas en cuanto al uso apropiado de los comentarios estilo Java. En el apéndice K, Creación de documentación con javadoc, demostramos el uso de los comentarios Javadoc y la herramienta javadoc. Para obtener información completa, visite la página de herramientas de javadoc de Sun en java.sun. com/javase/6/docs/technotes/guides/javadoc/index.html. Error común de programación 2.1 Olvidar uno de los delimitadores de un comentario tradicional o Javadoc es un error de sintaxis. La sintaxis de un lenguaje de programación que especifica las reglas para crear un programa apropiado en ese lenguaje. Un error de sintaxis ocurre cuando el compilador encuentra código que viola las reglas del lenguaje Java (es decir, su sintaxis). En este caso, el compilador muestra un mensaje de error para ayudar al programador a identificar y corregir el código incorrecto. Los errores de sintaxis se conocen también como errores del compilador, errores en tiempo de com- pilación o errores de compilación, ya que el compilador los detecta durante la fase de compilación. Usted no podrá ejecutar su programa sino hasta que corrija todos los errores de sintaxis que éste contenga. La línea 2 // Programa para imprimir texto. es un comentario de fin de línea que describe el propósito del programa. 1 // Fig. 2.1: Bienvenido1.java 2 // Programa para imprimir texto. 3 4 public class Bienvenido1 5 { 6 // el método main empieza la ejecución de la aplicación en Java 7 public static void main( String args[] ) 8 { 9 System.out.println( "Bienvenido a la programacion en Java!" ); 10 11 } // fin del método main 12 13 } // fin de la clase Bienvenido1 Figura 2.1 | Programa para imprimir texto. Bienvenido a la programacion en Java!
  • 75. 2.2 Su primer programa en Java: imprimir una línea de texto 37 Buena práctica de programación 2.1 Es conveniente que todo programa comience con un comentario que explique su propósito, el autor, la fecha y la hora de la última modificación del mismo. (No mostraremos el autor, la fecha y la hora en los programas de este libro, ya que sería redundante). La línea 3 es una línea en blanco. Los programadores usan líneas en blanco y espacios para facilitar la lectura de los programas. En conjunto, las líneas en blanco, los espacios y los tabuladores se conocen como espacio en blanco. (Los espacios y tabuladores se conocen específicamente como caracteres de espacio en blanco). El com- pilador ignora el espacio en blanco. En éste y en los siguientes capítulos, hablaremos sobre las convenciones para utilizar espacios en blanco para mejorar la legibilidad de los programas. Buena práctica de programación 2.2 Utilice líneas en blanco y espacios para mejorar la legibilidad del programa. La línea 4 public class Bienvenido1 comienza una declaración de clase para la clase Bienvenido1. Todo programa en Java consiste de, cuando menos, una declaración de clase que usted, el programador, debe definir. Estas clases se conocen como clases defi- nidas por el programador o clases definidas por el usuario. La palabra clave class introduce una declaración de clase en Java, la cual debe ir seguida inmediatamente por el nombre de la clase (Bienvenido1). Las palabras clave (algunas veces conocidas como palabras reservadas) se reservan para uso exclusivo de Java (hablaremos sobre las diversas palabras clave a lo largo de este texto) y siempre se escriben en minúscula. En el apéndice C se muestra la lista completa de palabras clave de Java. Por convención, todos los nombres de clases en Java comienzan con una letra mayúscula, y la primera letra de cada palabra en el nombre de la clase debe ir en mayúscula (por ejemplo, EjemploDeNombreDeClase). En Java, el nombre de una clase se conoce como identificador: una serie de caracteres que pueden ser letras, dígitos, guiones bajos (_) y signos de moneda ($), que no comience con un dígito ni tenga espacios. Algunos identificadores váli- dos son Bienvenido1, $valor, _valor, m_campoEntrada1 y boton7. El nombre 7boton no es un identificador válido, ya que comienza con un dígito, y el nombre campo entrada tampoco lo es debido a que contiene un espacio. Por lo general, un identificador que no empieza con una letra mayúscula no es el nombre de una clase. Java es sensible a mayúsculas y minúsculas; es decir, las letras mayúsculas y minúsculas son distintas, por lo que a1 y A1 son distintos identificadores (pero ambos son válidos). Buena práctica de programación 2.3 Por convención, el identificador del nombre de una clase siempre debe comenzar con una letra mayúscula, y la pri- mera letra de cada palabra subsiguiente del identificador también debe ir en mayúscula. Los programadores de Java saben que, por lo general, dichos identificadores representan clases de Java, por lo que si usted nombra a sus clases de esta forma, sus programas serán más legibles. Error común de programación 2.2 Java es sensible a mayúsculas y minúsculas. No utilizar la combinación apropiada de letras minúsculas y mayúsculas para un identificador, generalmente produce un error de compilación. En los capítulos 2 al 7, cada una de las clases que definimos comienza con la palabra clave public. Cuando usted guarda su declaración de clase public en un archivo, el nombre del mismo debe ser el nombre de la clase, seguido de la extensión de nombre de archivo .java. Para nuestra aplicación, el nombre del archivo es Bien- venido1.java. En el capítulo 8 aprenderá más acerca de las clases public y las que no son public. Error común de programación 2.3 Una clase public debe colocarse en un archivo que tenga el mismo nombre que la clase (en términos de ortografía y uso de mayúsculas) y la extensión .java; en caso contrario, ocurre un error de compilación.
  • 76. 38 Capítulo 2 Introducción a las aplicaciones en Java Error común de programación 2.4 Es un error que un archivo que contiene la declaración de una clase, no finalice con la extensión .java. El compi- lador de Java sólo compila archivos con la extensión .java. Una llave izquierda (en la línea 5 de este programa), {, comienza el cuerpo de todas las declaraciones de clases. Su correspondiente llave derecha (en la línea 13), }, debe terminar cada declaración de una clase. Observe que las líneas de la 6 a la 11 tienen sangría; ésta es una de las convenciones de espaciado que se mencionaron ante- riormente. Definimos cada una de las convenciones de espaciado como una Buena práctica de programación. Buena práctica de programación 2.4 Siempre que escriba una llave izquierda de apertura ({) en su programa, escriba inmediatamente la llave derecha de cierre (}) y luego vuelva a colocar el cursor entre las llaves y utilice sangría para comenzar a escribir el cuerpo. Esta práctica ayuda a evitar errores debido a la omisión de una de las llaves. Buena práctica de programación 2.5 Aplique sangría a todo el cuerpo de la declaración de cada clase, usando un “nivel” de sangría entre la llave izquierda ({) y la llave derecha (}), las cuales delimitan el cuerpo de la clase. Este formato enfatiza la estructura de la decla- ración de la clase, y facilita su lectura. Buena práctica de programación 2.6 Establezca una convención para el tamaño de sangría que usted prefiera, y después aplique uniformemente esta con- vención. La tecla Tab puede utilizarse para crear sangrías, pero las posiciones de los tabuladores pueden variar entre los diversos editores de texto. Le recomendamos utilizar tres espacios para formar un nivel de sangría. Error común de programación 2.5 Es un error de sintaxis no utilizar las llaves por pares. La línea 6 // el método main empieza la ejecución de la aplicación en Java es un comentario de fin de línea que indica el propósito de las líneas 7 a 11 del programa. La línea 7 public static void main( String args[] ) es el punto de inicio de toda aplicación en Java. Los paréntesis después del identificador main indican que éste es un bloque de construcción del programa, al cual se le llama método. Las declaraciones de clases en Java general- mente contienen uno o más métodos. En una aplicación en Java, sólo uno de esos métodos debe llamarse main y debe definirse como se muestra en la línea 7; de no ser así, la JVM no ejecutará la aplicación. Los métodos pueden realizar tareas y devolver información una vez que las hayan concluido. La palabra clave void indica que este método realizará una tarea, pero no devolverá ningún tipo de información cuando complete su tarea. Más adelante veremos que muchos métodos devuelven información cuando finalizan sus tareas. Aprenderá más acerca de los métodos en los capítulos 3 y 6. Por ahora, simplemente copie la primera línea de main en sus aplicaciones en Java. En la línea 7, las palabras String args[] entre paréntesis son una parte requerida de la declaración del método main. Hablaremos sobre esto en el capítulo 7, Arreglos. La llave izquierda ({) en la línea 8 comienza el cuerpo de la declaración del método; su correspondiente llave derecha (}) debe terminar el cuerpo de esa declaración (línea 11 del programa). Observe que la línea 9, entre las llaves, tiene sangría. Buena práctica de programación 2.7 Aplique un “nivel” de sangría a todo el cuerpo de la declaración de cada método, entre la llave izquierda ({) y la llave derecha (}), las cuales delimitan el cuerpo del método. Este formato resalta la estructura del método y ayuda a que su declaración sea más fácil de leer.
  • 77. 2.2 Su primer programa en Java: imprimir una línea de texto 39 La línea 9 System.out.println( "Bienvenido a la programacion en Java!" ); indica a la computadora que realice una acción; es decir, que imprima la cadena de caracteres contenida entre los caracteres de comillas dobles (sin incluirlas). A una cadena también se le denomina cadena de caracteres, mensaje o literal de cadena. Genéricamente, nos referimos a los caracteres entre comillas dobles como cadenas. El compilador no ignora los caracteres de espacio en blanco dentro de las cadenas. System.out se conoce como el objeto de salida estándar. System.out permite a las aplicaciones en Java mostrar conjuntos de caracteres en la ventana de comandos, desde la cual se ejecuta la aplicación en Java. En Microsoft Windows 95/98/ME, la ventana de comandos es el símbolo de MS-DOS. En versiones más recientes de Microsoft Windows, la ventana de comandos es el Símbolo del sistema. En UNIX/Linux/Mac OS X, la ventana de comandos se llama ventana de terminal o shell. Muchos programadores se refieren a la ventana de comandos simplemente como la línea de comandos. El método System.out.println muestra (o imprime) una línea de texto en la ventana de comandos. La cadena dentro de los paréntesis en la línea 9 es el argumento para el método. El método System.out.println realiza su tarea, mostrando (o enviando) su argumento en la ventana de comandos. Cuando System.out. println completa su tarea, posiciona el cursor de salida (la ubicación en donde se mostrará el siguiente carácter) al principio de la siguiente línea en la ventana de comandos. [Este desplazamiento del cursor es similar a cuando un usuario oprime la tecla Intro, al escribir en un editor de texto (el cursor aparece al principio de la siguiente línea en el archivo)]. Toda la línea 9, incluyendo System.out.println, el argumento "Bienvenido a la programacion en Java!" entre paréntesis y el punto y coma (;), se conoce como una instrucción; y siempre debe terminar con un punto y coma. Cuando se ejecuta la instrucción de la línea 9 de nuestro programa, ésta muestra el mensaje Bienvenido a la programacion en Java! en la ventana de comandos. Por lo general, un método está com- puesto por una o más instrucciones que realizan la tarea, como veremos en los siguientes programas. Error común de programación 2.6 Omitir el punto y coma al final de una instrucción es un error de sintaxis. Tip para prevenir errores 2.1 Al aprender a programar, es conveniente, en ocasiones, “descomponer” un programa funcional, para poder familiari- zarse con los mensajes de error de sintaxis del compilador; ya que este tipo de mensajes no siempre indican el problema exacto en el código. Y de esta manera, cuando se encuentren dichos mensajes de error de sintaxis, tendrá una idea de qué fue lo que ocasionó el error. Trate de quitar un punto y coma o una llave del programa de la figura 2.1, y vuelva a compilarlo de manera que pueda ver los mensajes de error generados por esta omisión. Tip para prevenir errores 2.2 Cuando el compilador reporta un error de sintaxis, éste tal vez no se encuentre en el número de línea indicado por el mensaje. Primero verifique la línea en la que se reportó el error; si esa línea no contiene errores de sintaxis, verifique las líneas anteriores. A algunos programadores se les dificulta, cuando leen o escriben un programa, relacionar las llaves izquierda y derecha ({ y }) que delimitan el cuerpo de la declaración de una clase o de un método. Por esta razón, incluyen un comentario de fin de línea después de una llave derecha de cierre (}) que termina la declaración de un método y que termina la declaración de una clase. Por ejemplo, la línea 11 } // fin del método main especifica la llave derecha de cierre (}) del método main, y la línea 13 } // fin de la clase Bienvenido1 especifica la llave derecha de cierre (}) de la clase Bienvenido1. Cada comentario indica el método o la clase que termina con esa llave derecha.
  • 78. 40 Capítulo 2 Introducción a las aplicaciones en Java Buena práctica de programación 2.8 Para mejorar la legibilidad de los programas, agregue un comentario de fin de línea después de la llave derecha de cierre (}), que indique a qué método o clase pertenece. Cómo compilar y ejecutar su primera aplicación en Java Ahora estamos listos para compilar y ejecutar nuestro programa. Para este propósito, supondremos que usted utiliza el Kit de Desarrollo 6.0 (JDK 6.0) de Java SE de Sun Microsystems. En nuestros centros de recursos en www.deitel.com/ResourceCenters.html proporcionamos vínculos a tutoriales que le ayudarán a empezar a trabajar con varias herramientas de desarrollo populares de Java. Para compilar el programa, abra una ventana de comandos y cambie al directorio en donde está guardado el programa. La mayoría de los sistemas operativos utilizan el comando cd para cambiar directorios. Por ejemplo, cd c:ejemploscap02fig02_01 cambia al directorio fig02_01 en Windows. El comando cd ~/ejemplos/cap02/fig02_01 cambia al directorio fig02_01 en UNIX/Linux/Mac OS X. Para compilar el programa, escriba javac Bienvenido1.java Si el programa no contiene errores de sintaxis, el comando anterior crea un nuevo archivo llamado Bienvenido1. class (conocido como el archivo de clase para Bienvenido1), el cual contiene los códigos de bytes de Java que representan nuestra aplicación. Cuando utilicemos el comando java para ejecutar la aplicación, la JVM ejecutará estos códigos de bytes. Tip para prevenir errores 2.3 Cuando trate de compilar un programa, si recibe un mensaje como “comando o nombre de archivo inco- rrecto”, “javac: comando no encontrado” o “'javac ' no se reconoce como un comando interno o externo, programa o archivo por lotes ejecutable”, entonces su instalación del software Java no se completó apropiadamente. Con el JDK, esto indica que la variable de entorno PATH del sistema no se estableció apropiadamente. Consulte cuidadosamente las instrucciones de instalación en la sección Antes de empezar de este libro. En algunos sistemas, después de corregir la variable PATH, es probable que necesite reiniciar su equipo o abrir una nueva ventana de comandos para que estos ajustes tengan efecto. Tip para prevenir errores 2.4 Cuando la sintaxis de un programa es incorrecta, el compilador de Java genera mensajes de error de sintaxis; éstos contienen el nombre de archivo y el número de línea en donde ocurrió el error. Por ejemplo, Bienvenido1.java:6 indica que ocurrió un error en el archivo Bienvenido1.java en la línea 6. El resto del mensaje proporciona infor- mación acerca del error de sintaxis. Tip para prevenir errores 2.5 El mensaje de error del compilador “Public class NombreClase must be defined in a file called NombreClase.java” indica que el nombre del archivo no coincide exactamente con el nombre de la clase public en el archivo, o que escribió el nombre de la clase en forma incorrecta al momento de compilarla. La figura 2.2 muestra el programa de la figura 2.1 ejecutándose en una ventana Símbolo del sistema de Microsoft® Windows® XP. Para ejecutar el programa, escriba java Bienvenido1; posteriormente se iniciará la JVM, que cargará el archivo “.class” para la clase Bienvenido1. Observe que la extensión “.class” del nombre de archivo se omite en el comando anterior; de no ser así, la JVM no ejecutaría el programa. La JVM llama al método main. A continuación, la instrucción de la línea 9 de main muestra "Bienvenido a la programacion en Java!" [Nota: muchos entornos muestran los símbolos del sistema con fondos negros y texto blanco. En nuestro entorno, ajustamos esta configuración para que nuestras capturas de pantalla fueran más legibles].
  • 79. 2.3 Modificación de nuestro primer programa en Java 41 Tip para prevenir errores 2.6 Al tratar de ejecutar un programa en Java, si recibe el mensaje “Exception in thread "main" java.1ang. NoC1assDefFoundError: Bienvenido1”, quiere decir que su variable de entorno CLASSPATH no se ha configu- rado apropiadamente. Consulte cuidadosamente las instrucciones de instalación en la sección Antes de empezar de este libro. En algunos sistemas, tal vez necesite reiniciar su equipo o abrir una nueva ventana de comandos para que estos ajustes tengan efecto. 2.3 Modificación de nuestro primer programa en Java Esta sección continúa con nuestra introducción a la programación en Java, con dos ejemplos que modifican el ejemplo de la figura 2.1 para imprimir texto en una línea utilizando varias instrucciones, y para imprimir texto en varias líneas utilizando una sola instrucción. Cómo mostrar una sola línea de texto con varias instrucciones Bienvenido a la programacion en Java! puede mostrarse en varias formas. La clase Bienvenido2, que se muestra en la figura 2.3, utiliza dos instrucciones para producir el mismo resultado que el de la figura 2.1. De aquí en adelante, resaltaremos las características nuevas y las características clave en cada listado de código, como se muestra en las línea 9 a 10 de este programa. El programa imprime en la pantalla Bienvenido a la programacion en Java! Figura 2.2 | Ejecución de Bienvenido1 en una ventana Símbolo del sistema de Microsoft Windows XP. 1 // Fig. 2.3: Bienvenido2.java 2 // Imprimir una línea de texto con varias instrucciones. 3 4 public class Bienvenido2 5 { 6 // el método main empieza la ejecución de la aplicación en Java 7 public static void main( String args[] ) 8 { 9 System.out.print( "Bienvenido a " ); 10 System.out.println( "la programacion en Java!" ); 11 12 } // fin del método main 13 14 } // fin de la clase Bienvenido2 Figura 2.3 | Impresión de una línea de texto con varias instrucciones. Bienvenido a la programacion en Java! Usted escribe este comando para ejecutar la aplicación
  • 80. 42 Capítulo 2 Introducción a las aplicaciones en Java El programa es similar al de la figura 2.1, por lo que aquí sólo hablaremos de los cambios. La línea 2 // Imprimir una línea de texto con varias instrucciones. es un comentario de fin de línea que describe el propósito de este programa. La línea 4 comienza la declaración de la clase Bienvenido2. Las líneas 9 y 10 del método main System.out.print( "Bienvenido a " ); System.out.println( "la programacion en Java!" ); muestran una línea de texto en la ventana de comandos. La primera instrucción utiliza el método print de System.out para mostrar una cadena. A diferencia de println, después de mostrar su argumento, print no posiciona el cursor de salida al inicio de la siguiente línea en la ventana de comandos; sino que el siguiente carác- ter aparecerá inmediatamente después del último que muestre print. Por lo tanto, la línea 10 coloca el primer carácter de su argumento (la letra “l”) inmediatamente después del último que muestra la línea 9 (el carácter de espacio antes del carácter de comilla doble de cierre de la cadena). Cada instrucción print o println continúa mostrando caracteres a partir de donde la última instrucción print o println dejó de mostrar caracteres. Cómo mostrar varias líneas de texto con una sola instrucción Una sola instrucción puede mostrar varias líneas, utilizando caracteres de nueva línea, los cuales indican a los métodos print y println de System.out cuándo deben colocar el cursor de salida al inicio de la siguiente línea en la ventana de comandos. Al igual que las líneas en blanco, los espacios y los tabuladores, los caracteres de nueva línea son caracteres de espacio en blanco. La figura 2.4 muestra cuatro líneas de texto, utilizando caracteres de nueva línea para determinar cuándo empezar cada nueva línea. La mayor parte del programa es idéntico a los de las figuras 2.1 y 2.3, por lo que aquí sólo veremos los cambios. La línea 2 // Imprimir varias líneas de texto con una sola instrucción. es un comentario que describe el propósito de este programa. La línea 4 comienza la declaración de la clase Bien- venido3. La línea 9 System.out.println( "Bienvenidonanla programacionnenJava!" ); muestra cuatro líneas separadas de texto en la ventana de comandos. Por lo general, los caracteres en una cadena se muestran exactamente como aparecen en las comillas dobles. Sin embargo, observe que los dos caracteres 1 // Fig. 2.4: Bienvenido3.java 2 // Imprimir varias líneas de texto con una sola instrucción. 3 4 public class Bienvenido3 5 { 6 // el método main empieza la ejecución de la aplicación en Java 7 public static void main( String args[] ) 8 { 9 System.out.println( "Bienvenidonanla programacionnen Java!" ); 10 11 } // fin del método main 12 13 } // fin de la clase Bienvenido3 Figura 2.4 | Impresión de varias líneas de texto con una sola instrucción. Bienvenido a la programacion en Java!
  • 81. 2.4 Cómo mostrar texto con printf 43 y n (que se repiten tres veces en la instrucción) no aparecen en la pantalla. La barra diagonal inversa () se conoce como carácter de escape. Este carácter indica a los métodos print y println de System.out que se va a imprimir un “carácter especial”. Cuando aparece una barra diagonal inversa en una cadena de caracteres, Java combina el siguiente carácter con la barra diagonal inversa para formar una secuencia de escape. La secuencia de escape n representa el carácter de nueva línea. Cuando aparece un carácter de nueva línea en una cadena que se va a imprimir con System.out, el carácter de nueva línea hace que el cursor de salida de la pantalla se despla- ce al inicio de la siguiente línea en la ventana de comandos. En la figura 2.5 se enlistan varias secuencias de escape comunes, con descripciones de cómo afectan la manera de mostrar caracteres en la ventana de comandos. Para obtener una lista completa de secuencias de escape, visite java.sun.com/docs/books/jls/third_edition/ html/lexical.html#3.10.6. 2.4 Cómo mostrar texto con printf Java SE 5.0 agregó el método System.out.printf para mostrar datos con formato; la f en el nombre printf representa la palabra “formato”. La figura 2.6 muestra las cadenas "Bienvenido a" y "la programacion en Java!" con System.out.printf. Las líneas 9 y 10 System.out.printf( "%sn%sn", "Bienvenido a", "la programacion en Java!" ); llaman al método System.out.printf para mostrar la salida del programa. La llamada al método especifica tres argumentos. Cuando un método requiere varios argumentos, éstos se separan con comas (,); a esto se le conoce como lista separada por comas. Buena práctica de programación 2.9 Coloque un espacio después de cada coma (,) en una lista de argumentos, para que sus programas sean más legi- bles. Recuerde que todas las instrucciones en Java terminan con un punto y coma (;). Por lo tanto, las líneas 9 y 10 sólo representan una instrucción. Java permite que las instrucciones largas se dividan en varias líneas. Sin embargo, no puede dividir una instrucción a la mitad de un identificador, o de una cadena. Error común de programación 2.7 Dividir una instrucción a la mitad de un identificador o de una cadena es un error de sintaxis. Secuencia de escape Descripción n Nueva línea. Coloca el cursor de la pantalla al inicio de la siguiente línea. t Tabulador horizontal. Desplaza el cursor de la pantalla hasta la siguiente posición de tabulación. r Retorno de carro. Coloca el cursor de la pantalla al inicio de la línea actual; no avanza a la siguiente línea. Cualquier carácter que se imprima después del retorno de carro sobrescribe los caracteres previamente impresos en esa línea. Barra diagonal inversa. Se usa para imprimir un carácter de barra diagonal inversa. ” Doble comilla. Se usa para imprimir un carácter de doble comilla. Por ejemplo, System.out.println( ""entre comillas"" ); muestra "entre comillas" Figura 2.5 | Algunas secuencias de escape comunes.
  • 82. 44 Capítulo 2 Introducción a las aplicaciones en Java El primer argumento del método printf es una cadena de formato que puede consistir en texto fijo y espe- cificadores de formato. El método printf imprime el texto fijo de igual forma que print o println. Cada es- pecificador de formato es un receptáculo para un valor, y especifica el tipo de datos a imprimir. Los especificadores de formato también pueden incluir información de formato opcional. Los especificadores de formato empiezan con un signo porcentual (%) y van seguidos de un carácter que representa el tipo de datos. Por ejemplo, el especificador de formato %s es un receptáculo para una cadena. La cadena de formato en la línea 9 especifica que printf debe imprimir dos cadenas, y que a cada cadena le debe seguir un carácter de nueva línea. En la posición del primer especificador de formato, printf sustituye el valor del primer argumento después de la cadena de formato. En cada posición posterior de los especificadores de formato, printf sustituye el valor del siguiente argumento en la lista. Así, este ejemplo sustituye "Bienvenido a" por el primer %s y "la programacion en Java!" por el segundo %s. La salida muestra que se imprimieron dos líneas de texto. En nuestros ejemplos, presentaremos las diversas características de formato a medida que se vayan necesitan- do. El capítulo 29 presenta los detalles de cómo dar formato a la salida con printf. 2.5 Otra aplicación en Java: suma de enteros Nuestra siguiente aplicación lee (o recibe como entrada) dos enteros (números completos, como –22, 7, 0 y 1024) introducidos por el usuario mediante el teclado, calcula la suma de los valores y muestra el resultado. Este programa debe llevar la cuenta de los números que suministra el usuario para los cálculos que el programa realiza posteriormente. Los programas recuerdan números y otros datos en la memoria de la computadora, y acceden a esos datos a través de elementos del programa, conocidos como variables. El programa de la figura 2.7 demuestra estos conceptos. En los resultados de ejemplo, resaltamos las diferencias entre la entrada del usuario y la salida del programa. 1 // Fig. 2.6: Bienvenido4.java 2 // Imprimir varias líneas en un cuadro de diálogo. 3 4 public class Bienvenido4 5 { 6 // el método main empieza la ejecución de la aplicación de Java 7 public static void main( String args[] ) 8 { 9 System.out.printf( "%sn%sn", 10 "Bienvenido a", "la programacion en Java!" ); 11 12 } // fin del método main 13 14 } // fin de la clase Bienvenido4 Figura 2.6 | Imprimir varias líneas de texto con el método System.out.printf. Bienvenido a la programacion en Java! 1 // Fig. 2.7: Suma.java 2 // Programa que muestra la suma de dos enteros. 3 import java.util.Scanner; // el programa usa la clase Scanner 4 5 public class Suma 6 { 7 // el método main empieza la ejecución de la aplicación en Java Figura 2.7 | Programa que muestra la suma de dos enteros. (Parte 1 de 2).
  • 83. 2.5 Otra aplicación en Java: suma de enteros 45 Las líneas 1 y 2 // Fig. 2.7: Suma.java // Programa que muestra la suma de dos enteros. indican el número de la figura, el nombre del archivo y el propósito del programa. La línea 3 import java.util.Scanner; // el programa usa la clase Scanner es una declaración import que ayuda al compilador a localizar una clase que se utiliza en este programa. Una gran fortaleza de Java es su extenso conjunto de clases predefinidas que podemos utilizar, en vez de “reinventar la rueda”. Estas clases se agrupan en paquetes (colecciones con nombre de clases relacionadas) y se conocen en conjunto como la biblioteca de clases de Java, o Interfaz de Programación de Aplicaciones de Java (API de Java). Los programadores utilizan declaraciones import para identificar las clases predefinidas que se utilizan en un programa de Java. La declaración import en la línea 3 indica que este ejemplo utiliza la clase Scanner pre- definida de Java (que veremos en breve) del paquete java.util. Después, el compilador trata de asegurarse que utilicemos la clase Scanner de manera apropiada. Error común de programación 2.8 Todas las declaraciones import deben aparecer antes de la primera declaración de clase en el archivo. Colocar una declaración import dentro del cuerpo de la declaración de una clase, o después de la declaración de la misma, es un error de sintaxis. Tip para prevenir errores 2.7 Por lo general, si olvida incluir una declaración import para una clase que utilice en su programa, se produce un error de compilación que contiene el mensaje: “cannot resolve symbol”. Cuando esto ocurra, verifique que haya proporcionado las declaraciones import apropiadas y que los nombres en las mismas estén escritos correctamente, incluyendo el uso apropiado de las letras mayúsculas y minúsculas. Figura 2.7 | Programa que muestra la suma de dos enteros. (Parte 2 de 2). 8 public static void main( String args[] ) 9 { 10 // crea objeto Scanner para obtener la entrada de la ventana de comandos 11 Scanner entrada = new Scanner( System.in ); 12 13 int numero1; // primer número a sumar 14 int numero2; // segundo número a sumar 15 int suma; // suma de numero1 y numero2 16 17 System.out.print( "Escriba el primer entero: " ); // indicador 18 numero1 = entrada.nextInt(); // lee el primer número del usuario 19 20 System.out.print( "Escriba el segundo entero: " ); // indicador 21 numero2 = entrada.nextInt(); // lee el segundo número del usuario 22 23 suma = numero1 + numero2; // suma los números 24 25 System.out.printf( "La suma es %dn", suma ); // muestra la suma 26 27 } // fin del método main 28 29 } // fin de la clase Suma Escriba el primer entero: 45 Escriba el segundo entero: 72 La suma es 117
  • 84. 46 Capítulo 2 Introducción a las aplicaciones en Java La línea 5 public class Suma empieza la declaración de la clase Suma. El nombre de archivo para esta clase public debe ser Suma.java. Recuer- de que el cuerpo de cada declaración de clase empieza con una llave izquierda de apertura (línea 6) ({) y termina con una llave derecha de cierre (línea 29) (}). La aplicación empieza a ejecutarse con el método main (líneas 8 a la 27). La llave izquierda (línea 9) marca el inicio del cuerpo de main, y la correspondiente llave derecha (línea 27) marca el final de main. Observe que al método main se le aplica un nivel de sangría en el cuerpo de la clase Suma, y que al código en el cuerpo de main se le aplica otro nivel para mejorar la legibilidad. La línea 11 Scanner entrada = new Scanner( System.in ); es una instrucción de declaración de variable (también conocida como declaración), la cual especifica el nom- bre (entrada) y tipo (Scanner) de una variable utilizada en este programa. Una variable es una ubicación en la memoria de la computadora, en donde se puede guardar un valor para utilizarlo posteriormente en un programa. Todas las variables deben declararse con un nombre y un tipo antes de poder usarse; este nombre permite al programa acceder al valor de la variable en memoria; y puede ser cualquier identificador válido. (Consulte en la sección 2.2 los requerimientos para nombrar identificadores). El tipo de una variable especifica el tipo de infor- mación que se guarda en esa ubicación de memoria. Al igual que las demás instrucciones, las instrucciones de declaración terminan con punto y coma (;). La declaración en la línea 11 especifica que la variable llamada entrada es de tipo Scanner. Un objeto Scanner permite a un programa leer datos (como números) para usarlos. Los datos pueden provenir de muchas fuentes, como un archivo en disco, o desde el teclado. Antes de usar un objeto Scanner, el programa debe crearlo y especificar el origen de los datos. El signo igual (=) en la línea 11 indica que la variable entrada tipo Scanner debe inicializarse (es decir, hay que prepararla para usarla en el programa) en su declaración con el resultado de la expresión new Scanner ( System.in ) a la derecha del signo igual. Esta expresión crea un objeto Scanner que lee los datos escritos por el usuario mediante el teclado. Recuerde que el objeto de salida estándar, System.out, permite a las aplicaciones de Java mostrar caracteres en la ventana de comandos. De manera similar, el objeto de entrada estándar, Sys- tem.in, permite a las aplicaciones de Java leer la información escrita por el usuario. Así, la línea 11 crea un objeto Scanner que permite a la aplicación leer la información escrita por el usuario mediante el teclado. Las instrucciones de declaración de variables en las líneas 13 a la 15 int numero1; // primer número a sumar int numero2; // segundo número a sumar int suma; // suma de numero1 y numero2 declaran que las variables numero1, numero2 y suma contienen datos de tipo int; estas variables pueden contener valores enteros (números completos, como 7, –11, 0 y 31,914). Estas variables no se han inicializado todavía. El rango de valores para un int es de –2,147,483,648 a +2,147,483,647. Pronto hablaremos sobre los tipos float y double, para guardar números reales, y sobre el tipo char, para guardar datos de caracteres. Los números reales son números que contienen puntos decimales, como 3.4, 0.0 y –11.19. Las variables de tipo char representan caracteres individuales, como una letra en mayúscula (como A), un dígito (como 7), un carácter especial (como * o %) o una secuencia de escape (como el carácter de nueva línea, n). Los tipos como int, float, double y char se conocen como tipos primitivos o tipos integrados. Los nombres de los tipos primitivos son palabras clave y, por lo tanto, deben aparecer completamente en minúsculas. El apéndice D, Tipos primitivos, sintetiza las carac- terísticas de los ocho tipos primitivos (boolean, byte, char, short, int, long, float y double). Las instrucciones de declaración de variables pueden dividirse en varias líneas, separando los nombres de las variables por comas (es decir, una lista de nombres de variables separados por comas). Varias variables del mismo tipo pueden declararse en una, o en varias declaraciones. Por ejemplo, las líneas 13 a la 15 se pueden escribir como una sola instrucción, de la siguiente manera: int numero1, // primer número a sumar numero2, // segundo número a sumar suma; // suma de numero1 y numero2
  • 85. 2.5 Otra aplicación en Java: suma de enteros 47 Observe que utilizamos comentarios de fin de línea en las líneas 13 a la 15. Este uso de comentarios es una prác- tica común de programación, para indicar el propósito de cada variable en el programa. Buena práctica de programación 2.10 Declare cada variable en una línea separada. Este formato permite insertar fácilmente un comentario descriptivo a continuación de cada declaración. Buena práctica de programación 2.11 Seleccionar nombres de variables significativos ayuda a que un programa se autodocumente (es decir, que sea más fácil entender con sólo leerlo, en lugar de leer manuales o ver un número excesivo de comentarios). Buena práctica de programación 2.12 Por convención, los identificadores de nombre de variables empiezan con una letra minúscula, y cada una de las palabras en el nombre, que van después de la primera, deben empezar con una letra mayúscula. Por ejemplo, el identificador primerNumero tiene una N mayúscula en su segunda palabra, Numero. La línea 17 System.out.print( "Escriba el primer entero: " ); // indicador utiliza System.out.print para mostrar el mensaje "Escriba el primer entero: ". Este mensaje se conoce como indicador, ya que indica al usuario que debe realizar una acción específica. En la sección 2.2 vimos que los identificadores que empiezan con letras mayúsculas representan nombres de clases. Por lo tanto, System es una clase; que forma parte del paquete java.lang. Observe que la clase System no se importa con una declaración import al principio del programa. Observación de ingeniería de software 2.1 El paquete java.lang se importa de manera predeterminada en todos los programas de Java; por ende, las clases en java.lang son las únicas en la API que no requieren una declaración import. La línea 18 numero1 = entrada.nextInt(); // lee el primer número del usuario utiliza el método nextInt del objeto entrada de la clase Scanner para obtener un entero del usuario mediante el teclado. En este punto, el programa espera a que el usuario escriba el número y oprima Intro para enviar el número al programa. Técnicamente, el usuario puede escribir cualquier cosa como valor de entrada. Nuestro programa asume que el usuario escribirá un valor de entero válido, según las indicaciones; si el usuario escribe un valor no entero, se producirá un error lógico en tiempo de ejecución y el programa no funcionará correctamente. El capítulo 13, Manejo de excepciones, habla sobre cómo hacer sus programas más robustos, al permitirles manejar dichos erro- res. Esto también se conoce como hacer que su programa sea tolerante a fallas. En la línea 18, el resultado de la llamada al método nextInt (un valor int) se coloca en la variable nume- ro1 mediante el uso del operador de asignación, =. La instrucción se lee como “numero1 obtiene el valor de entrada.nextInt()”. Al operador = se le llama operador binario, ya que tiene dos operandos: numero1 y el resultado de la llamada al método entrada.nextInt(). Esta instrucción se llama instrucción de asignación, ya que asigna un valor a una variable. Todo lo que está a la derecha del operador de asignación (=) se evalúa siempre antes de realizar la asignación. Buena práctica de programación 2.13 Coloque espacios en cualquier lado de un operador binario, para que resalte y el programa sea más legible. La línea 20 System.out.print( "Escriba el segundo entero: " ); // indicador
  • 86. 48 Capítulo 2 Introducción a las aplicaciones en Java pide al usuario que escriba el segundo entero. La línea 21 numero2 = entrada.nextInt(); // lee el segundo número del usuario lee el segundo entero y lo asigna a la variable numero2. La línea 23 suma = numero1 + numero2; // suma los números es una instrucción de asignación que calcula la suma de las variables numero1 y numero2, y asigna el resultado a la variable suma mediante el uso del operador de asignación, =. La instrucción se lee como “suma obtiene el valor de numero1 + numero2”. La mayoría de los cálculos se realizan en instrucciones de asignación. Cuando el pro- grama encuentra la operación de suma, utiliza los valores almacenados en las variables numero1 y numero2 para realizar el cálculo. En la instrucción anterior, el operador de suma es binario; sus dos operandos son numero1 y numero2. Las partes de las instrucciones que contienen cálculos se llaman expresiones. De hecho, una expresión es cualquier parte de una instrucción que tiene un valor asociado. Por ejemplo, el valor de la expresión numero1 + numero2 es la suma de los números. De manera similar, el valor de la expresión entrada.nextInt() es un entero escrito por el usuario. Una vez realizado el cálculo, la línea 25 System.out.printf( "La suma es %dn", suma ); // muestra la suma utiliza el método System.out.printf para mostrar la suma. El especificador de formato %d es un receptáculo para un valor int (en este caso, el valor de suma); la letra d representa “entero decimal”. Observe que aparte del especificador de formato %d, el resto de los caracteres en la cadena de formato son texto fijo. Por lo tanto, el méto- do printf imprime en pantalla "La suma es ", seguido del valor de suma (en la posición del especificador de formato %d) y una nueva línea. Observe que los cálculos también pueden realizarse dentro de instrucciones printf. Podríamos haber com- binado las instrucciones de las líneas 23 y 25 en la siguiente instrucción: System.out.printf( "La suma es %dn", ( numero1 + numero2 ) ); Los paréntesis alrededor de la expresión numero1 + numero2 no son requeridos; se incluyen para enfatizar que el valor de la expresión se imprime en la posición del especificador de formato %d. Documentación de la API de Java Para cada nueva clase de la API de Java que utilizamos, indicamos el paquete en el que se ubica. Esta información es importante, ya que nos ayuda a localizar las descripciones de cada paquete y clase en la documentación de la API de Java. Puede encontrar una versión basada en Web de esta documentación en java.sun.com/javase/6/docs/api/ También puede descargar esta documentación, en su propia computadora, de java.sun.com/javase/downloads/ea.jsp La descarga es de aproximadamente 53 megabytes (MB). El apéndice J, Uso de la documentación de la API de Java, describe cómo utilizar esta documentación. 2.6 Conceptos acerca de la memoria Los nombres de variables como numero1, numero2 y suma en realidad corresponden a ciertas ubicaciones en la memoria de la computadora. Toda variable tiene un nombre, un tipo, un tamaño y un valor. En el programa de suma de la figura 2.7, cuando se ejecuta la instrucción (línea 18) numero1 = entrada.nextInt(); // lee el primer número del usuario el número escrito por el usuario se coloca en una ubicación de memoria a la cual se asigna el nombre numero1. Suponga que el usuario escribe 45. La computadora coloca ese valor entero en la ubicación numero1, como se
  • 87. 2.7 Aritmética 49 muestra en la figura 2.8. Cada vez que se coloca un nuevo valor en una ubicación de memoria, se sustituye al valor anterior en esa ubicación; es decir, el valor anterior se pierde. Cuando se ejecuta la instrucción (línea 21) numero2 = entrada.nextInt(); // lee el segundo número del usuario suponga que el usuario escribe 72. La computadora coloca ese valor entero en la ubicación numero2. La memoria ahora aparece como se muestra en la figura 2.9. Una vez que el programa de la figura 2.7 obtiene valores para numero1 y numero2, los suma y coloca el resul- tado en la variable suma. La instrucción (línea 23) suma = numero1 + numero2; // suma los números realiza la suma y después sustituye el valor anterior de suma. Una vez que se calcula suma, la memoria aparece como se muestra en la figura 2.10. Observe que los valores de numero1 y numero2 aparecen exactamente como antes de usarlos en el cálculo de suma. Estos valores se utilizaron, pero no se destruyeron, cuando la computadora realizó el cálculo. Por ende, cuando se lee un valor de una ubicación de memoria, el proceso es no destructivo. 2.7 Aritmética La mayoría de los programas realizan cálculos aritméticos. Los operadores aritméticos se sintetizan en la figura 2.11. Observe el uso de varios símbolos especiales que no se utilizan en álgebra. El asterisco (*) indica la multi- plicación, y el signo de porcentaje (%) es el operador residuo (conocido como módulo en algunos lenguajes), el cual describiremos en breve. Los operadores aritméticos en la figura 2.11 son binarios, ya que funcionan con dos operandos. Por ejemplo, la expresión f + 7 contiene el operador binario + y los dos operandos f y 7. La división de enteros produce un cociente entero: por ejemplo, la expresión 7 / 4 da como resultado 1, y la expresión 17 / 5 da como resultado 3. Cualquier parte fraccionaria en una división de enteros simplemente se descarta (es decir, se trunca); no ocurre un redondeo. Java proporciona el operador residuo, %, el cual produce el residuo después de la división. La expresión x % y produce el residuo después de que x se divide entre y. Por lo tanto, 7 % 4 produce 3, y 17 % 5 produce 2. Por lo general, este operador se utiliza más con operandos enteros, pero también puede usarse con otros tipos aritméticos. En los ejercicios de este capítulo y de capítulos posterio- res, consideramos muchas aplicaciones interesantes del operador residuo, como determinar si un número es múltiplo de otro. Figura 2.8 | Ubicación de memoria que muestra el nombre y el valor de la variable numero1. Figura 2.9 | Ubicaciones de memoria, después de almacenar valores para numero1 y numero2. Figura 2.10 | Ubicaciones de memoria, después de almacenar la suma de numero1 y numero2. 45 72 117 numero1 numero2 suma 45 72 numero1 numero2 45 numero1
  • 88. 50 Capítulo 2 Introducción a las aplicaciones en Java Expresiones aritméticas en formato de línea recta Las expresiones aritméticas en Java deben escribirse en formato de línea recta para facilitar la escritura de pro- gramas en la computadora. Por lo tanto, las expresiones como “a dividida entre b” deben escribirse como a / b, de manera que todas las constantes, variables y operadores aparezcan en una línea recta. La siguiente notación algebraica no es generalmente aceptable para los compiladores: a b Paréntesis para agrupar subexpresiones Los paréntesis se utilizan para agrupar términos en las expresiones en Java, de la misma manera que en las expre- siones algebraicas. Por ejemplo, para multiplicar a por la cantidad b + c, escribimos a * ( b + c ) Si una expresión contiene paréntesis anidados, como ( ( a + b ) * c ) se evalúa primero la expresión en el conjunto más interno de paréntesis (a + b en este caso). Reglas de precedencia de operadores Java aplica los operadores en expresiones aritméticas en una secuencia precisa, determinada por las siguientes reglas de precedencia de operadores, que generalmente son las mismas que las que se utilizan en álgebra (figura 2.12): 1. Las operaciones de multiplicación, división y residuo se aplican primero. Si una expresión contiene varias de esas operaciones, los operadores se aplican de izquierda a derecha. Los operadores de multipli- cación, división y residuo tienen el mismo nivel de precedencia. 2. Las operaciones de suma y resta se aplican a continuación. Si una expresión contiene varias de esas operaciones, los operadores se aplican de izquierda a derecha. Los operadores de suma y resta tienen el mismo nivel de precedencia. Operación en Java Operador aritmético Expresión algebraica Expresión en Java Suma + f + 7 f + 7 Resta - p — c p — c Multiplicación * bm b * m División / x / y Residuo % r mod s r % s Operador(es) Operación(es) Orden de evaluación (precedencia) * Multiplicación Se evalúan primero. Si hay varios operadores de este tipo, se evalúan de izquierda a derecha. / División % Residuo + Suma Se evalúan después. Si hay varios operadores de este tipo, se evalúan de izquierda a derecha. - Resta Figura 2.12 | Precedencia de los operadores aritméticos. Figura 2.11 | Operadores aritméticos. x/y o o x ∏y x y - -
  • 89. 2.7 Aritmética 51 Estas reglas permiten a Java aplicar los operadores en el orden correcto. Cuando decimos que los operadores se aplican de izquierda a derecha, nos referimos a su asociatividad; veremos que algunos se asocian de derecha a izquierda. La figura 2.12 sintetiza estas reglas de precedencia de operadores; esta tabla se expandirá a medida que se introduzcan más operadores en Java. En el apéndice A, Tabla de precedencia de los operadores, se incluye una tabla de precedencias completa. Ejemplos de expresiones algebraicas y de Java Ahora, consideremos varias expresiones en vista de las reglas de precedencia de operadores. Cada ejemplo enlista una expresión algebraica y su equivalente en Java. El siguiente es un ejemplo de una media (promedio) aritmética de cinco términos: Álgebra: Java: a + b + c + d + e 5 m = ( a + b + c + d + e ) / 5; m = Los paréntesis son obligatorios, ya que la división tiene una mayor precedencia que la suma. La cantidad completa ( a + b + c + d + e ) va a dividirse entre 5. Si por error se omiten los paréntesis, obtenemos a + b + c + d + e / 5, lo cual da como resultado a + b + c + d + 5 e El siguiente es un ejemplo de una ecuación de línea recta: Álgebra: y = mx + b Java: y = m * x + b; No se requieren paréntesis. El operador de multiplicación se aplica primero, ya que la multiplicación tiene mayor precedencia sobre la suma. La asignación ocurre al último, ya que tiene menor precedencia que la multiplicación o suma. El siguiente ejemplo contiene las operaciones residuo (%), multiplicación, división, suma y resta: Álgebra: z = pr%q + w/x – y Java: z = p * r % q + w / x – y; 6 1 2 4 3 5 Los números dentro de los círculos bajo la instrucción, indican el orden en el que Java aplica los operadores. Las operaciones de multiplicación, residuo y división se evalúan primero, en orden de izquierda a derecha (es decir, se asocian de izquierda a derecha), ya que tienen mayor precedencia que la suma y la resta. Las operaciones de suma y resta se evalúan a continuación; estas operaciones también se aplican de izquierda a derecha. Evaluación de un polinomio de segundo grado Para desarrollar una mejor comprensión de las reglas de precedencia de operadores, considere la evaluación de un polinomio de segundo grado (y = ax2 + bx + c): y = a * x * x + b * x + c; 6 1 2 4 3 5 Los números dentro de los círculos indican el orden en el que Java aplica los operadores. Las operaciones de multiplicación se evalúan primero en orden de izquierda a derecha (es decir, se asocian de izquierda a derecha), ya que tienen mayor precedencia que la suma. Las operaciones de suma se evalúan a continuación y se aplican de izquierda a derecha. No existe un operador aritmético para la potencia de un número en Java, por lo que x2 se representa como x * x. La sección 5.4 muestra una alternativa para calcular la potencia de un número en Java.
  • 90. 52 Capítulo 2 Introducción a las aplicaciones en Java Suponga que a, b, c y x en el polinomio de segundo grado anterior se inicializan (reciben valores) como sigue: a = 2, b = 3, c = 7 y x = 5. La figura 2.13 muestra el orden en el que se aplican los operadores. Al igual que en álgebra, es aceptable colocar paréntesis innecesarios en una expresión para hacer que ésta sea más clara. A dichos paréntesis se les llama paréntesis redundantes. Por ejemplo, la instrucción de asignación anterior podría colocarse entre paréntesis, de la siguiente manera: y = ( a * x * x ) + ( b * x ) + c; Buena práctica de programación 2.14 El uso de paréntesis para las expresiones aritméticas complejas, incluso cuando éstos no sean necesarios, puede hacer que las expresiones aritméticas sean más fáciles de leer. 2.8 Toma de decisiones: operadores de igualdad y relacionales Una condición es una expresión que puede ser verdadera (true) o falsa (false). Esta sección presenta la ins- trucción if de Java, la cual permite que un programa tome una decisión, con base en el valor de una condición. Por ejemplo, la condición “calificación es mayor o igual que 60” determina si un estudiante pasó o no una prueba. Si la condición en una instrucción if es verdadera, el cuerpo de la instrucción if se ejecuta. Si la condición es falsa, el cuerpo no se ejecuta. Veremos un ejemplo en breve. Las condiciones en las instrucciones if pueden formarse utilizando los operadores de igualdad (== y !=) y los operadores relacionales (>, <, >= y <=) que se sintetizan en la figura 2.14. Ambos operadores de igualdad tienen el mismo nivel de precedencia, que es menor que la precedencia de los operadores relacionales. Los ope- radores de igualdad se asocian de izquierda a derecha; y los relacionales tienen el mismo nivel de precedencia y también se asocian de izquierda a derecha. La aplicación de la figura 2.15 utiliza seis instrucciones if para comparar dos enteros introducidos por el usuario. Si la condición en cualquiera de estas instrucciones if es verdadera, se ejecuta la instrucción de asigna- ción asociada. El programa utiliza un objeto Scanner para recibir los dos enteros del usuario y almacenarlos en las variables numero1 y numero2. Después, compara los números y muestra los resultados de las comparaciones que son verdaderas. Figura 2.13 | Orden en el cual se evalúa un polinomio de segundo grado. (Multiplicación de más a la izquierda) (Multiplicación de más a la izquierda) (Multiplicación antes de la suma) (Suma de más a la izquierda) (Última suma) (Última operación; colocar 72 en y) Paso 1. y = 2 * 5 * 5 + 3 * 5 + 7; 2 * 5 es 10 Paso 2. y = 10 * 5 + 3 * 5 + 7; 10 * 5 es 50 Paso 3. y = 50 + 3 * 5 + 7; 3 * 5 es 15 Paso 4. y = 50 + 15 + 7; 50 + 15 es 65 Paso 5. y = 65 + 7; 65 + 7 es 72 Paso 6. y = 72
  • 91. 2.8 Toma de decisiones: operadores de igualdad y relacionales 53 Operador estándar algebraico de igualdad o relacional Operador de igualdad o relacional de Java Ejemplo de condición en Java Significado de la condición en Java Operadores de igualdad = == x == y x es igual a y ≠ != x != y x no es igual a y Operadores relacionales > > x > y x es mayor que y < < x < y x es menor que y ≥ >= x >= y x es mayor o igual que y ≤ <= x <= y x es menor o igual que y 1 // Fig. 2.15: Comparacion.java 2 // Compara enteros utilizando instrucciones if, operadores relacionales 3 // y de igualdad. 4 import java.util.Scanner; // el programa utiliza la clase Scanner 5 6 public class Comparacion 7 { 8 // el método main empieza la ejecución de la aplicación en Java 9 public static void main( String args[] ) 10 { 11 // crea objeto Scanner para obtener la entrada de la ventana de comandos 12 Scanner entrada = new Scanner( System.in ); 13 14 int numero1; // primer número a comparar 15 int numero2; // segundo número a comparar 16 17 System.out.print( "Escriba el primer entero: " ); // indicador 18 numero1 = entrada.nextInt(); // lee el primer número del usuario 19 20 System.out.print( “Escriba el segundo entero: “ ); // indicador 21 numero2 = entrada.nextInt(); // lee el segundo número del usuario 22 23 if ( numero1 == numero2 ) 24 System.out.printf( “%d == %dn”, numero1, numero2 ); 25 26 if ( numero1 != numero2 ) 27 System.out.printf( “%d != %dn”, numero1, numero2 ); 28 29 if ( numero1 < numero2 ) 30 System.out.printf( “%d < %dn”, numero1, numero2 ); 31 32 if ( numero1 > numero2 ) 33 System.out.printf( “%d > %dn”, numero1, numero2 ); 34 35 if ( numero1 <= numero2 ) 36 System.out.printf( “%d <= %dn”, numero1, numero2 ); Figura 2.15 | Operadores de igualdad y relacionales. (Parte 1 de 2). Figura 2.14 | Operadores de igualdad y relacionales.
  • 92. 54 Capítulo 2 Introducción a las aplicaciones en Java La declaración de la clase Comparacion comienza en la línea 6 public class Comparacion El método main de la clase (líneas 9 a 41) empieza la ejecución del programa. La línea 12 Scanner entrada = new Scanner( System.in ); declara la variable entrada de la clase Scanner y le asigna un objeto Scanner que recibe datos de la entrada estándar (es decir, el teclado). Las líneas 14 y 15 int numero1; // primer número a comparar int numero2; // segundo número a comparar declaran las variables int que se utilizan para almacenar los valores introducidos por el usuario. Las líneas 17-18 System.out.print( "Escriba el primer entero: " ); // indicador numero1 = entrada.nextInt(); // lee el primer número del usuario piden al usuario que introduzca el primer entero y el valor, respectivamente. El valor de entrada se almacena en la variable numero1. Las líneas 20-21 System.out.print( "Escriba el segundo entero: " ); // indicador numero2 = entrada.nextInt(); // lee el segundo número del usuario piden al usuario que introduzca el segundo entero y el valor, respectivamente. El valor de entrada se almacena en la variable numero2. 37 38 if ( numero1 >= numero2 ) 39 System.out.printf( “%d >= %dn”, numero1, numero2 ); 40 41 } // fin del método main 42 43 } // fin de la clase Comparacion Figura 2.1 | Programa para imprimir texto. Escriba el primer entero: 777 Escriba el segundo entero: 777 777 == 777 777 <= 777 777 >= 777 Escriba el primer entero: 1000 Escriba el segundo entero: 2000 1000 != 2000 1000 < 2000 1000 <= 2000 Escriba el primer entero: 2000 Escriba el segundo entero: 1000 2000 != 1000 2000 > 1000 2000 >= 1000 Figura 2.15 | Operadores de igualdad y relacionales. (Parte 2 de 2).
  • 93. 2.8 Toma de decisiones: operadores de igualdad y relacionales 55 Las líneas 23-24 if ( numero1 == numero2 ) System.out.printf( “%d == %dn”, numero1, numero2 ); declaran una instrucción if que compara los valores de las variables numero1 y numero2, para determinar si son iguales o no. Una instrucción if siempre empieza con la palabra clave if, seguida de una condición entre parén- tesis. Una instrucción if espera una instrucción en su cuerpo. La sangría de la instrucción del cuerpo que se muestra aquí no es obligatoria, pero mejora la legibilidad del programa al enfatizar que la instrucción en la línea 24 forma parte de la instrucción if que empieza en la línea 23. La línea 24 sólo se ejecuta si los números alma- cenados en las variables numero1 y numero2 son iguales (es decir, si la condición es verdadera). Las instrucciones if en las líneas 26-27, 29-30, 32-33, 35-36 y 38-39 comparan a numero1 y numero2 con los operadores !=, <, >, <= y >=, respectivamente. Si la condición en cualquiera de las instrucciones if es verdadera, se ejecuta la instrucción del cuerpo correspondiente. Error común de programación 2.9 Olvidar los paréntesis izquierdo y/o derecho de la condición en una instrucción if es un error de sintaxis; los parén- tesis son obligatorios. Error común de programación 2.10 Confundir el operador de igualdad (==) con el de asignación (=) puede producir un error lógico o de sintaxis. El operador de igualdad debe leerse como “es igual a”, y el de asignación como “obtiene” u “obtiene el valor de”. Para evitar confusión, algunas personas leen el operador de igualdad como “doble igual” o “igual igual”. Error común de programación 2.11 Es un error de sintaxis si los operadores ==, !=, >= y <= contienen espacios entre sus símbolos, como en = =, ! =, > = y < =, respectivamente. Error común de programación 2.12 Invertir los operadores !=, >= y <=, como en =!, => y =<, es un error de sintaxis. Buena práctica de programación 2.15 Aplique sangría al cuerpo de una instrucción if para hacer que resalte y mejorar la legibilidad del programa. Buena práctica de programación 2.16 Coloque sólo una instrucción por línea en un programa. Este formato mejora la legibilidad del programa. Observe que no hay punto y coma (;) al final de la primera línea de cada instrucción if. Dicho punto y coma produciría un error lógico en tiempo de compilación. Por ejemplo, if ( numero1 == numero2 ); // error lógico System.out.printf( "%d == %dn", numero1, numero2 ); sería interpretada por Java de la siguiente manera: if ( numero1 == numero2 ) ; // instrucción vacía System.out.printf( "%d == %dn", numero1, numero2 ); en donde el punto y coma que aparece por sí solo en una línea (que se conoce como instrucción vacía o nula) es la instrucción que se va a ejecutar si la condición en la instrucción if es verdadera. Al ejecutarse la instrucción vacía, no se lleva a cabo ninguna tarea en el programa. Éste continúa con la instrucción de salida, que siempre se
  • 94. 56 Capítulo 2 Introducción a las aplicaciones en Java ejecuta, sin importar que la condición sea verdadera o falsa, ya que la instrucción de salida no forma parte de la instrucción if. Error común de programación 2.13 Colocar un punto y coma inmediatamente después del paréntesis derecho de la condición en una instrucción if es, generalmente, un error lógico. Observe el uso del espacio en blanco en la figura 2.15. Recuerde que los caracteres de espacio en blanco, como tabuladores, nuevas líneas y espacios, generalmente son ignorados por el compilador. Por lo tanto, las ins- trucciones pueden dividirse en varias líneas y pueden espaciarse de acuerdo a las preferencias del programador, sin afectar el significado de un programa. Es incorrecto dividir identificadores y cadenas. Idealmente las instrucciones deben mantenerse lo más reducidas que sea posible, pero no siempre se puede hacer esto. Buena práctica de programación 2.17 Una instrucción larga puede esparcirse en varias líneas. Si una sola instrucción debe dividirse entre varias líneas, los puntos que elija para hacer la división deben tener sentido, como después de una coma en una lista separada por comas, o después de un operador en una expresión larga. Si una instrucción se divide entre dos o más líneas, aplique sangría a todas las líneas subsecuentes hasta el final de la instrucción. La figura 2.16 muestra la precedencia de los operadores que se presentan en este capítulo. Los operadores se muestran de arriba hacia abajo, en orden descendente de precedencia; todos, con la excepción del operador de asignación, =, se asocian de izquierda a derecha. La suma es asociativa a la izquierda, por lo que una expresión como x + y + z se evalúa como si se hubiera escrito así: ( x + y ) + z. El operador de asignación, =, asocia de derecha a izquierda, por lo que una expresión como x = y = 0 se evalúa como si se hubiera escrito así: x = ( y = 0 ), en donde, como pronto veremos, primero se asigna el valor 0 a la variable y, y después se asigna el resultado de esa asignación, 0, a x. Buena práctica de programación 2.18 Cuando escriba expresiones que contengan muchos operadores, consulte la tabla de precedencia (apéndice A). Con- firme que las operaciones en la expresión se realicen en el orden que usted espera. Si no está seguro acerca del orden de evaluación en una expresión compleja, utilice paréntesis para forzar el orden, en la misma forma que lo haría con las expresiones algebraicas. Observe que algunos operadores como el de asignación, =, asocian de derecha a izquierda, en vez de hacerlo de izquierda a derecha. 2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de requerimientos de un problema Ahora empezaremos nuestro ejemplo práctico opcional de diseño e implementación orientados a objetos. Las sec- ciones del Ejemplo práctico de Ingeniería de Software al final de este y los siguientes capítulos le ayudarán a incur- sionar en la orientación a objetos, mediante el análisis de un ejemplo práctico de una máquina de cajero automático Operadores Asociatividad Tipo * / % izquierda a derecha multiplicativa + - izquierda a derecha suma < <= > >= izquierda a derecha relacional == != izquierda a derecha igualdad = derecha a izquierda asignación Figura 2.16 | Precedencia y asociatividad de los operadores descritos hasta ahora.
  • 95. 2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de ... 57 (Automated Teller Machine o ATM, por sus siglas en inglés). Este ejemplo práctico le brindará una experiencia de diseño e implementación substancial, cuidadosamente pautada y completa. En los capítulos 3 al 8 y 10, lleva- remos a cabo los diversos pasos de un proceso de diseño orientado a objetos (DOO) utilizando UML, mientras relacionamos estos pasos con los conceptos orientados a objetos que se describen en los capítulos. El apéndice M implementa el ATM utilizando las técnicas de la programación orientada a objetos (POO) en Java. Presentaremos la solución completa al ejemplo práctico. Éste no es un ejercicio, sino una experiencia de aprendizaje de extremo a extremo, que concluye con un análisis detallado del código en Java que implementamos, con base en nuestro diseño. Este ejemplo práctico le ayudará a acostumbrarse a los tipos de problemas substanciales que se encuentran en la industria, y sus soluciones potenciales. Esperamos que disfrute esta experiencia de aprendizaje. Empezaremos nuestro proceso de diseño con la presentación de un documento de requerimientos, el cual especifica el propósito general del sistema ATM y qué debe hacer. A lo largo del ejemplo práctico, nos referiremos al documento de requerimientos para determinar con precisión la funcionalidad que debe incluir el sistema. Documento de requerimientos Un banco local pretende instalar una nueva máquina de cajero automático (ATM), para permitir a los usuarios (es decir, los clientes del banco) realizar transacciones financieras básicas (figura 2.17). Cada usuario sólo puede tener una cuenta en el banco. Los usuarios del ATM deben poder ver el saldo de su cuenta, retirar efectivo (es decir, sacar dinero de una cuenta) y depositar fondos (es decir, meter dinero en una cuenta). La interfaz de usuario del cajero automático contiene los siguientes componentes: una pantalla que muestra mensajes al usuario un teclado que recibe datos numéricos de entrada del usuario un dispensador de efectivo que dispensa efectivo al usuario, y una ranura de depósito que recibe sobres para depósitos del usuario. El dispensador de efectivo comienza cada día cargado con 500 billetes de $20. [Nota: debido al alcance limitado de este ejemplo práctico, ciertos elementos del ATM que se describen aquí no imitan exactamente a los de un ATM real. Por ejemplo, generalmente un ATM contiene un dispositivo que lee el número de cuenta del usuario de una tarjeta para ATM, mientras que este ATM pide al usuario que escriba su número de cuenta. Un ATM real también imprime por lo general un recibo al final de una sesión, pero toda la salida de este ATM aparece en la pantalla]. • • • • Figura 2.17 | Interfaz de usuario del cajero automático. Teclado Pantalla Ranura de depósito Dispensador de efectivo Bienvenido! Escriba su número de cuenta: 12345 Escriba su NIP: 54321 Inserte aquí el sobre de depósito Inserte aquí el sobre de depósito Inserte aquí el sobre de depósito Tome aquí el efectivo Tome aquí el efectivo Tome aquí el efectivo
  • 96. 58 Capítulo 2 Introducción a las aplicaciones en Java El banco desea que usted desarrolle software para realizar las transacciones financieras que inicien los clientes a través del ATM. Posteriormente, el banco integrará el software con el hardware del ATM. El software debe encapsular la funcionalidad de los dispositivos de hardware (por ejemplo: dispensador de efectivo, ranura para depósito) dentro de los componentes de software, pero no necesita estar involucrado en la manera en que estos dispositivos ejecutan su tarea. El hardware del ATM no se ha desarrollado aún, en vez de que usted escriba un software para ejecutarse en el ATM, deberá desarrollar una primera versión del software para que se ejecute en una computadora personal. Esta versión debe utilizar el monitor de la computadora para simular la pantalla del ATM y el teclado de la computadora para simular el teclado numérico del ATM. Una sesión con el ATM consiste en la autenticación de un usuario (es decir, proporcionar la identidad del usuario) con base en un número de cuenta y un número de identificación personal (NIP), seguida de la creación y la ejecución de transacciones financieras. Para autenticar un usuario y realizar transacciones, el ATM debe interactuar con la base de datos de información sobre las cuentas del banco (es decir, una colección organizada de datos almacenados en una computadora). Para cada cuenta de banco, la base de datos almacena un número de cuenta, un NIP y un saldo que indica la cantidad de dinero en la cuenta. [Nota: asumiremos que el banco planea construir sólo un ATM, por lo que no necesitamos preocuparnos para que varios ATMs puedan acceder a esta base de datos al mismo tiempo. Lo que es más, supongamos que el banco no realizará modificaciones en la información que hay en la base de datos mientras un usuario accede al ATM. Además, cualquier sistema comer- cial como un ATM se topa con cuestiones de seguridad con una complejidad razonable, las cuales van más allá del alcance de un curso de programación de primer o segundo semestre. No obstante, para simplificar nuestro ejemplo supondremos que el banco confía en el ATM para que acceda a la información en la base de datos y la manipule sin necesidad de medidas de seguridad considerables]. Al acercarse al ATM (suponiendo que nadie lo está utilizando), el usuario deberá experimentar la siguiente secuencia de eventos (vea la figura 2.17): 1. La pantalla muestra un mensaje de bienvenida y pide al usuario que introduzca un número de cuenta. 2. El usuario introduce un número de cuenta de cinco dígitos, mediante el uso del teclado. 3. En la pantalla aparece un mensaje, en el que se pide al usuario que introduzca su NIP (número de iden- tificación personal) asociado con el número de cuenta especificado. 4. El usuario introduce un NIP de cinco dígitos mediante el teclado numérico. 5. Si el usuario introduce un número de cuenta válido y el NIP correcto para esa cuenta, la pantalla mues- tra el menú principal (figura 2.18). Si el usuario introduce un número de cuenta inválido o un NIP incorrecto, la pantalla muestra un mensaje apropiado y después el ATM regresa al paso 1 para reiniciar el proceso de autenticación. Una vez que el ATM autentica al usuario, el menú principal (figura 2.18) debe contener una opción nume- rada para cada uno de los tres tipos de transacciones: solicitud de saldo (opción 1), retiro (opción 2) y depó- sito (opción 3). El menú principal también debe contener una opción para que el usuario pueda salir del sistema (opción 4). Después el usuario elegirá si desea realizar una transacción (oprimiendo 1, 2 o 3) o salir del siste- ma (oprimiendo 4). Si el usuario oprime 1 para solicitar su saldo, la pantalla mostrará el saldo de esa cuenta bancaria. Para ello, el ATM deberá obtener el saldo de la base de datos del banco. Los siguientes pasos describen las acciones que ocurren cuando el usuario elige la opción 2 para hacer un retiro: 1. La pantalla muestra un menú (vea la figura 2.19) que contiene montos de retiro estándar: $20 (opción 1), $40 (opción 2), $60 (opción 3), $100 (opción 4) y $200 (opción 5). El menú también contiene una opción que permite al usuario cancelar la transacción (opción 6). 2. El usuario introduce la selección del menú mediante el teclado numérico. 3. Si el monto a retirar elegido es mayor que el saldo de la cuenta del usuario, la pantalla muestra un men- saje indicando esta situación y pide al usuario que seleccione un monto más pequeño. Entonces el ATM regresa al paso 1. Si el monto a retirar elegido es menor o igual que el saldo de la cuenta del usuario (es decir, un monto de retiro aceptable), el ATM procede al paso 4. Si el usuario opta por cancelar la tran- sacción (opción 6), el ATM muestra el menú principal y espera la entrada del usuario.
  • 97. 2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de ... 59 4. Si el dispensador contiene suficiente efectivo para satisfacer la solicitud, el ATM procede al paso 5. En caso contrario, la pantalla muestra un mensaje indicando el problema y pide al usuario que seleccione un monto de retiro más pequeño. Después el ATM regresa al paso 1. 5. El ATM carga el monto de retiro al saldo de la cuenta del usuario en la base de datos del banco (es decir, resta el monto de retiro al saldo de la cuenta del usuario). 6. El dispensador de efectivo entrega el monto deseado de dinero al usuario. Menú de retiro 1 - $20 4 - $100 2 - $40 5 - $200 3 - $60 6 - Cancelar transacción Elija un monto de retiro: Inserte aquí el sobre de depósito Inserte aquí el sobre de depósito Inserte aquí el sobre de depósito Tome aquí el efectivo Tome aquí el efectivo Tome aquí el efectivo Figura 2.19 | Menú de retiro del ATM. Menú principal 1 - Ver mi saldo 2 - Retirar efectivo 3 - Depositar fondos 4 - Salir Escriba una opción: Inserte aquí el sobre de depósito Inserte aquí el sobre de depósito Inserte aquí el sobre de depósito Tome aquí el efectivo Tome aquí el efectivo Tome aquí el efectivo Figura 2.18 | Menú principal del ATM.
  • 98. 60 Capítulo 2 Introducción a las aplicaciones en Java 7. La pantalla muestra un mensaje para recordar al usuario que tome el dinero. Los siguientes pasos describen las acciones que ocurren cuando el usuario elige la opción 3 para hacer un depósito: 1. La pantalla muestra un mensaje que pide al usuario que introduzca un monto de depósito o que escriba 0 (cero) para cancelar la transacción. 2. El usuario introduce un monto de depósito o 0 mediante el teclado numérico. [Nota: el teclado no contiene un punto decimal o signo de dólares, por lo que el usuario no puede escribir una cantidad real en dólares (por ejemplo, $1.25), sino que debe escribir un monto de depósito en forma de número de centavos (por ejemplo, 125). Después, el ATM divide este número entre 100 para obtener un número que represente un monto en dólares (por ejemplo, 125 ÷ 100 = 1.25)]. 3. Si el usuario especifica un monto a depositar, el ATM procede al paso 4. Si elije cancelar la transacción (escribiendo 0), el ATM muestra el menú principal y espera la entrada del usuario. 4. La pantalla muestra un mensaje indicando al usuario que introduzca un sobre de depósito en la ranura para depósitos. 5. Si la ranura de depósitos recibe un sobre dentro de un plazo de tiempo no mayor a 2 minutos, el ATM abona el monto del depósito al saldo de la cuenta del usuario en la base de datos del banco (es decir, suma el monto del depósito al saldo de la cuenta del usuario). [Nota: este dinero no está disponible de inmediato para retirarse. El banco debe primero verificar físicamente el monto de efectivo en el sobre de depósito, y cualquier cheque que éste contenga debe validarse (es decir, el dinero debe transferirse de la cuenta del emisor del cheque a la cuenta del beneficiario). Cuando ocurra uno de estos eventos, el banco actualizará de manera apropiada el saldo del usuario que está almacenado en su base de datos. Esto ocurre de manera independiente al sistema ATM]. Si la ranura de depósito no recibe un sobre dentro de un plazo de tiempo no mayor a dos minutos, la pantalla muestra un mensaje indicando que el sistema canceló la transacción debido a la inactividad. Después el ATM muestra el menú principal y espera la entrada del usuario. Una vez que el sistema ejecuta una transacción en forma exitosa, debe volver a mostrar el menú principal para que el usuario pueda realizar transacciones adicionales. Si el usuario elije salir del sistema, la pantalla debe mostrar un mensaje de agradecimiento y después el mensaje de bienvenida para el siguiente usuario. Análisis del sistema de ATM En la declaración anterior se presentó un ejemplo simplificado de un documento de requerimientos. Por lo gene- ral, dicho documento es el resultado de un proceso detallado de recopilación de requerimientos, el cual podría incluir entrevistas con usuarios potenciales del sistema y especialistas en campos relacionados con el mismo. Por ejemplo, un analista de sistemas que se contrate para preparar un documento de requerimientos para software bancario (por ejemplo, el sistema ATM que describimos aquí) podría entrevistar expertos financieros para obtener una mejor comprensión de qué es lo que debe hacer el software. El analista utilizaría la información recopilada para compilar una lista de requerimientos del sistema, para guiar a los diseñadores de sistemas en el proceso de diseño del mismo. El proceso de recopilación de requerimientos es una tarea clave de la primera etapa del ciclo de vida del software. El ciclo de vida del software especifica las etapas a través de las cuales el software evoluciona desde el momento en que fue concebido hasta que deja de utilizarse. Por lo general, estas etapas incluyen: análisis, diseño, implementación, prueba y depuración, despliegue, mantenimiento y retiro. Existen varios modelos de ciclo de vida del software, cada uno con sus propias preferencias y especificaciones con respecto a cuándo y qué tan a menudo deben llevar a cabo los ingenieros de software las diversas etapas. Los modelos de cascada realizan cada etapa una vez en sucesión, mientras que los modelos iterativos pueden repetir una o más etapas varias veces a lo largo del ciclo de vida de un producto. La etapa de análisis del ciclo de vida del software se enfoca en definir el problema a resolver. Al diseñar cualquier sistema, uno debe resolver el problema de la manera correcta, pero de igual manera uno debe resolver el problema correcto. Los analistas de sistemas recolectan los requerimientos que indican el problema específico a resolver. Nuestro documento de requerimientos describe nuestro sistema ATM con el suficiente detalle como para que usted no necesite pasar por una etapa de análisis exhaustiva; ya lo hicimos por usted.
  • 99. 2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de ... 61 Para capturar lo que debe hacer un sistema propuesto, los desarrolladores emplean a menudo una técnica conocida como modelado de caso-uso. Este proceso identifica los casos de uso del sistema, cada uno de los cua- les representa una capacidad distinta que el sistema provee a sus clientes. Por ejemplo, es común que los ATMs tengan varios casos de uso, como “Ver saldo de cuenta”, “Retirar efectivo”, “Depositar fondos”, “Transferir fondos entre cuentas” y “Comprar estampas postales”. El sistema ATM simplificado que construiremos en este ejemplo práctico requiere sólo los tres primeros casos de uso. Cada uno de los casos de uso describe un escenario común en el cual el usuario utiliza el sistema. Usted ya leyó las descripciones de los casos de uso del sistema ATM en el documento de requerimientos; las listas de pasos reque- ridos para realizar cada tipo de transacción (como solicitud de saldo, retiro y depósito) describen en realidad los tres casos de uso de nuestro ATM: “Ver saldo de cuenta”, “Retirar efectivo” y “Depositar fondos”, respectivamente. Diagramas de caso-uso Ahora presentaremos el primero de varios diagramas de UML en el ejemplo práctico. Crearemos un diagrama de caso-uso para modelar las interacciones entre los clientes de un sistema (en este ejemplo práctico, los clientes del banco) y sus casos de uso. El objetivo es mostrar los tipos de interacciones que tienen los usuarios con un sistema sin proveer los detalles; éstos se mostrarán en otros diagramas de UML (los cuales presentaremos a lo largo del ejemplo práctico). A menudo, los diagramas de caso-uso se acompañan de texto informal que describe los casos de uso con más detalle; como el texto que aparece en el documento de requerimientos. Los diagramas de caso-uso se producen durante la etapa de análisis del ciclo de vida del software. En sistemas más grandes, los diagramas de caso-uso son herramientas indispensables que ayudan a los diseñadores de sistemas a enfocarse en satisfacer las necesidades de los usuarios. La figura 2.20 muestra el diagrama de caso-uso para nuestro sistema ATM. La figura humana representa a un actor, el cual define los roles que desempeña una entidad externa (como una persona u otro sistema) cuando interactúa con el sistema. Para nuestro cajero automático, el actor es un Usuario que puede ver el saldo de una cuenta, retirar efectivo y depositar fondos del ATM. El Usuario no es una persona real, sino que constituye los roles que puede desempeñar una persona real (al desempeñar el papel de un Usuario) mientras interactúa con el ATM. Hay que tener en cuenta que un diagrama de caso-uso puede incluir varios actores. Por ejemplo, el diagra- ma de caso-uso para un sistema ATM de un banco real podría incluir también un actor llamado Administrador, que rellene el dispensador de efectivo a diario. Nuestro documento de requerimientos provee los actores: “los usuarios del ATM deben poder ver el saldo de su cuenta, retirar efectivo y depositar fondos”. Por lo tanto, el actor en cada uno de estos tres casos de uso es el usuario que interactúa con el ATM. Una entidad externa (una persona real) desempeña el papel del usuario para realizar transacciones financieras. La figura 2.20 muestra un actor, cuyo nombre (Usuario) aparece debajo del actor en el diagrama. UML modela cada caso de uso como un óvalo conectado a un actor con una línea sólida. Los ingenieros de software (más específicamente, los diseñadores de sistemas) deben analizar el documento de requerimientos o un conjunto de casos de uso, y diseñar el sistema antes de que los programadores lo imple- menten en un lenguaje de programación específico. Durante la etapa de análisis, los diseñadores de sistemas se enfocan en comprender el documento de requerimientos para producir una especificación de alto nivel que describa qué es lo que el sistema debe hacer. El resultado de la etapa de diseño (una especificación de diseño) Depositar fondos Retirar efectivo Ver saldo de cuenta Usuario Figura 2.20 | Diagrama de caso-uso para el sistema ATM, desde la perspectiva del usuario.
  • 100. 62 Capítulo 2 Introducción a las aplicaciones en Java debe detallar claramente cómo debe construirse el sistema para satisfacer estos requerimientos. En las siguientes secciones del Ejemplo práctico de Ingeniería de Software, llevaremos a cabo los pasos de un proceso simple de diseño orientado a objetos (DOO) con el sistema ATM, para producir una especificación de diseño que contenga una colección de diagramas de UML y texto de apoyo. UML está diseñado para utilizarse con cualquier pro- ceso de DOO. Existen muchos de esos procesos, de los cuales el más conocido es Rational Unified Process™ (RUP), desarrollado por Rational Software Corporation. RUP es un proceso robusto para diseñar aplicaciones a nivel industrial. Para este ejemplo práctico, presentaremos nuestro propio proceso de diseño simplificado, desa- rrollado para estudiantes de cursos de programación de primer y segundo semestre. Diseño del sistema ATM Ahora comenzaremos la etapa de diseño de nuestro sistema ATM. Un sistema es un conjunto de componentes que interactúan para resolver un problema. Por ejemplo, para realizar sus tareas designadas, nuestro sistema ATM tiene una interfaz de usuario (figura 2.17), contiene software para ejecutar transacciones financieras e interactúa con una base de datos de información de cuentas bancarias. La estructura del sistema describe los objetos del sis- tema y sus interrelaciones. El comportamiento del sistema describe la manera en que cambia el sistema a medida que sus objetos interactúan entre sí. Todo sistema tiene tanto estructura como comportamiento; los diseñadores deben especificar ambos. Existen diversos tipos de estructuras y comportamientos de un sistema. Por ejemplo, las interacciones entre los objetos en el sistema son distintas a las interacciones entre el usuario y el sistema, pero aun así ambas constituyen una porción del comportamiento del sistema. El estándar UML 2 especifica 13 tipos de diagramas para documentar los modelos de un sistema. Cada tipo de diagrama modela una característica distinta de la estructura o del comportamiento de un sistema; seis diagramas se relacionan con la estructura del sistema; los siete restantes se relacionan con su comportamiento. Aquí listaremos sólo los seis tipos de diagramas que utilizaremos en nuestro ejemplo práctico, uno de los cuales (el diagrama de clases) modela la estructura del sistema, mientras que los otros cinco modelan el comportamiento. En el apéndice O, UML 2: Tipos de diagramas adicionales, veremos las generalidades sobre los siete tipos restan- tes de diagramas de UML. 1. Los diagramas de caso-uso, como el de la figura 2.20, modelan las interacciones entre un sistema y sus entidades externas (actores) en términos de casos de uso (capacidades del sistema, como “Ver saldo de cuenta”, “Retirar efectivo” y “Depositar fondos”). 2. Los diagramas de clases, que estudiará en la sección 3.10, modelan las clases o “bloques de cons- trucción” que se utilizan en un sistema. Cada sustantivo u “objeto” que se describe en el documento de requerimientos es candidato para ser una clase en el sistema (por ejemplo, Cuenta, Teclado). Los diagramas de clases nos ayudan a especificar las relaciones estructurales entre las partes del sistema. Por ejemplo, el diagrama de clases del sistema ATM especificará que el ATM está compuesto físicamente de una pantalla, un teclado, un dispensador de efectivo y una ranura para depósitos. 3. Los diagramas de máquina de estado, que estudiará en la sección 5.11, modelan las formas en que un objeto cambia de estado. El estado de un objeto se indica mediante los valores de todos los atributos del objeto, en un momento dado. Cuando un objeto cambia de estado, puede comportarse de manera distinta en el sistema. Por ejemplo, después de validar el NIP de un usuario, el ATM cambia del estado “usuario no autenticado” al estado “usuario autenticado”, punto en el cual el ATM permite al usuario rea- lizar transacciones financieras (por ejemplo, ver el saldo de su cuenta, retirar efectivo, depositar fondos). 4. Los diagramas de actividad, que también estudiará en la sección 5.11, modelan la actividad de un objeto: el flujo de trabajo (secuencia de eventos) del objeto durante la ejecución del programa. Un diagrama de actividad modela las acciones que realiza el objeto y especifica el orden en el cual desem- peña estas acciones. Por ejemplo, un diagrama de actividad muestra que el ATM debe obtener el saldo de la cuenta del usuario (de la base de datos de información de las cuentas del banco) antes de que la pantalla pueda mostrar el saldo al usuario. 5. Los diagramas de comunicación (llamados diagramas de colaboración en versiones anteriores de UML) modelan las interacciones entre los objetos en un sistema, con un énfasis acerca de qué interac- ciones ocurren. En la sección 7.14 aprenderá que estos diagramas muestran cuáles objetos deben in- teractuar para realizar una transacción en el ATM. Por ejemplo, el ATM debe comunicarse con la base de datos de información de las cuentas del banco para obtener el saldo de una cuenta.
  • 101. 2.9 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo examinar el documento de ... 63 6. Los diagramas de secuencia modelan también las interacciones entre los objetos en un sistema, pero a diferencia de los diagramas de comunicación, enfatizan cuándo ocurren las interacciones. En la sección 7.14 aprenderá que estos diagramas ayudan a mostrar el orden en el que ocurren las interacciones al ejecutar una transacción financiera. Por ejemplo, la pantalla pide al usuario que escriba un monto de retiro antes de dispensar el efectivo. En la sección 3.10 seguiremos diseñando nuestro sistema ATM; ahí identificaremos las clases del documento de requerimientos. Para lograr esto, extraeremos sustantivos clave y frases nominales del documento de reque- rimientos. Mediante el uso de estas clases, desarrollaremos nuestro primer borrador del diagrama de clases que modelará la estructura de nuestro sistema ATM. Recursos en Internet y Web Los siguientes URLs proporcionan información sobre el diseño orientado a objetos con UML. www-306.ibm.com/software/rational/uml/ Lista preguntas frecuentes acerca del UML, proporcionado por IBM Rational. www.douglass.co.uk/documents/softdocwiz.com.UML.htm Sitio anfitrión del Diccionario del Lenguaje unificado de modelado, el cual lista y define todos los términos utilizados en el UML. www-306.ibm.com/software/rational/offerings/design.html Proporciona información acerca del software IBM Rational, disponible para el diseño de sistemas. Ofrece descargas de versiones de prueba de 30 días de varios productos, como IBM Rational Rose® XDE Developer. www.embarcadero.com/products/describe/index.html Proporciona una licencia gratuita de 14 días para descargar una versión de prueba de Describe™: una herramienta de modelado con UML de Embarcadero Technologies®. www.borland.com/us/products/together/index.html Proporciona una licencia gratuita de 30 días para descargar una versión de prueba de Borland® Together® Control Center™: una herramienta de desarrollo de software que soporta el UML. www.ilogix.com/sublevel.aspx?id=53 http://modelingcommunity.telelogic.com/developer-trial.aspx Proporciona una licencia gratuita de 30 días para descargar una versión de prueba de I-Logix Rhapsody®: un entorno de desarrollo controlado por modelos y basado en UML 2. argouml.tigris.org Contiene información y descargas para ArgoUML, una herramienta gratuita de código fuente abierto de UML, escrita en Java. www.objectsbydesign.com/books/booklist.html Provee una lista de libros acerca de UML y el diseño orientado a objetos. www.objectsbydesign.com/tools/umltools_byCompany.html Provee una lista de herramientas de software que utilizan UML, como IBM Rational Rose, Embarcadero Describe, Sparx Systems Enterprise Architect, I-Logix Rhapsody y Gentleware Poseidon para UML. www.ootips.org/ood-principles.html Proporciona respuestas a la pregunta “¿Qué se requiere para tener un buen diseño orientado a objetos?” parlezuml.com/tutorials/umlforjava.htm Ofrece un tutorial de UML para desarrolladores de Java, el cual presenta los diagramas de UML y los compara detalla- damente con el código que los implementa. www.cetus-links.org/oo_uml.html Introduce el UML y proporciona vínculos a numerosos recursos sobre UML. www.agilemodeling.com/essays/umlDiagrams.htm Proporciona descripciones detalladas y tutoriales acerca de cada uno de los 13 tipos de diagramas de UML 2. Lecturas recomendadas Los siguientes libros proporcionan información acerca del diseño orientado a objetos con UML. Booch, G. Object-Oriented Analysis and Design with Applications, Tercera edición. Boston: Addison-Wesley, 2004. Eriksson, H. et al. UML 2 Toolkit. Nueva York: John Wiley, 2003. Kruchten, P. The Rational Unified Process: An Introduction. Boston: Addison-Wesley, 2004.
  • 102. 64 Capítulo 2 Introducción a las aplicaciones en Java Larman, C. Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design, Segunda edición. Upper Saddle River, NJ: Prentice Hall, 2002. Roques, P. UML in Practice: The Art of Modeling Software Systems Demonstrated Through Worked Examples and Solutions. Nueva York: John Wiley, 2004. Rosenberg, D. y K. Scott. Applying Use Case Driven Object Modeling with UML: An Annotated e-Commerce Example. Reading, MA: Addison-Wesley, 2001. Rumbaugh, J., I. Jacobson y G. Booch. The Complete UML Training Course. Upper Saddle River, NJ: Prentice Hall, 2000. Rumbaugh, J., I. Jacobson y G. Booch. The Unified Modeling Language Reference Manual. Reading, MA: Addison- Wesley, 1999. Rumbaugh, J., I. Jacobson y G. Booch. The Unified Software Development Process. Reading, MA: Addison-Wesley, 1999. Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 2.1 Suponga que habilitamos a un usuario de nuestro sistema ATM para transferir dinero entre dos cuentas banca- rias. Modifique el diagrama de caso-uso de la figura 2.20 para reflejar este cambio. 2.2 Los modelan las interacciones entre los objetos en un sistema, con énfasis acerca de cuándo ocurren estas interacciones. a) Diagramas de clases b) Diagramas de secuencia c) Diagramas de comunicación d) Diagramas de actividad 2.3 ¿Cuál de las siguientes opciones lista las etapas de un típico ciclo de vida de software, en orden secuencial? a) diseño, análisis, implementación, prueba b) diseño, análisis, prueba, implementación c) análisis, diseño, prueba, implementación d) análisis, diseño, implementación, prueba Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 2.1 La figura 2.21 contiene un diagrama de caso-uso para una versión modificada de nuestro sistema ATM, que también permite a los usuarios transferir dinero entre cuentas. 2.2 b. 2.3 d. Transferir fondos entre cuentas Depositar fondos Retirar efectivo Ver saldo de cuenta Usuario Figura 2.21 | Diagrama de caso-uso para una versión modificada de nuestro sistema ATM, que también permite a los usuarios transferir dinero entre varias cuentas.
  • 103. Resumen 65 2.10 Conclusión En este capítulo aprendió muchas características importantes de Java, incluyendo cómo mostrar datos en la pan- talla en un Símbolo del sistema, recibir datos del teclado, realizar cálculos y tomar decisiones. Las aplicaciones que presentamos aquí le sirvieron como una introducción a los conceptos básicos de programación. Como verá en el capítulo 3, por lo general las aplicaciones de Java contienen sólo unas cuantas líneas de código en el método main; comúnmente estas instrucciones crean los objetos que realizan el trabajo de la aplicación. En el capítulo 3 aprenderá a implementar sus propias clases, y a utilizar objetos de esas clases en las aplicaciones. Resumen Sección 2.2 Su primer programa en Java: imprimir una línea de texto • Los programadores de computadoras crean aplicaciones, escribiendo programas de cómputo. Una aplicación de Java es un programa de computadora que se ejecuta cuando utilizamos el comando java para iniciar la JVM. • Los programadores insertan comentarios para documentar los programas y mejorar su legibilidad. El compilador de Java ignora los comentarios. • Un comentario que empieza con // se llama comentario de fin de línea (o de una sola línea), ya que termina al final de la línea en la que aparece. • Los comentarios tradicionales (de varias líneas) pueden dividirse en varias líneas, y están delimitados por /* y */. El compilador ignora todo el texto entre los delimitadores. • Los comentarios Javadoc se delimitan por /** y */. Estos comentarios permiten a los programadores incrustar la documentación directamente en sus programas. La herramienta javadoc genera documentación en HTML, con base en los comentarios Javadoc. • La sintaxis de un lenguaje de programación especifica las reglas para crear un programa apropiado en ese lenguaje. • Un error de sintaxis (también conocido como error de compilador, error en tiempo de compilación o error de com- pilación) ocurre cuando el compilador encuentra código que viola las reglas del lenguaje Java. • Los programadores utilizan líneas en blanco y espacios para facilitar la lectura de los programas. En conjunto, las líneas en blanco, los espacios y los tabuladores se conocen como espacio en blanco. Los espacios y los tabuladores se conocen específicamente como caracteres de espacio en blanco. El compilador ignora el espacio en blanco. • Todo programa en Java consiste en por lo menos una declaración de clase, definida por el programador (también conocida como clase definida por el programador, o clase definida por el usuario). • Las palabras clave están reservadas para el uso exclusivo de Java, y siempre se escriben con letras minúsculas. • La palabra clave class introduce una declaración de clase, y va seguida inmediatamente del nombre de la clase. • Por convención, todos los nombres de las clases en Java empiezan con una letra mayúscula, y la primera letra de cada palabra subsiguiente también se escribe en mayúscula (como NombreClaseDeEjemplo). • El nombre de una clase de Java es un identificador: una serie de caracteres formada por letras, dígitos, guiones bajos (_) y signos de dólar ($), que no empieza con un dígito y no contiene espacios. Por lo general, un identificador que no empieza con letra mayúscula no es el nombre de una clase de Java. • Java es sensible a mayúsculas/minúsculas; es decir, las letras mayúsculas y minúsculas son distintas. • El cuerpo de todas las declaraciones de clases debe estar delimitado por llaves, { y }. • La declaración de una clase public debe guardarse en un archivo con el mismo nombre que la clase, seguido de la extensión de nombre de archivo “.java”. • El método main es el punto de inicio de toda aplicación en Java, y debe empezar con: public static void main( String args[] ) en caso contrario, la JVM no ejecutará la aplicación. • Los métodos pueden realizar tareas y devolver información cuando completan éstas tareas. La palabra clave void indica que un método realizará una tarea, pero no devolverá información. • Las instrucciones instruyen a la computadora para que realice acciones. • Una secuencia de caracteres entre comillas dobles se llama cadena, cadena de caracteres, mensaje o literal de cadena. • System.out es el objeto de salida estándar; permite a las aplicaciones de Java mostrar caracteres en la ventana de comandos. • El método System.out.println muestra su argumento en la ventana de comandos, seguido de un carácter de nueva línea para colocar el cursor de salida en el inicio de la siguiente línea.
  • 104. • Toda instrucción termina con un punto y coma. • La mayoría de los sistemas operativos utilizan el comando cd para cambiar directorios en la ventana de comandos. • Para compilar un programa se utiliza el comando javac. Si el programa no contiene errores de sintaxis, se crea un archivo de clase que contiene los códigos de bytes de Java, los cuales representan a la aplicación. La JVM interpreta estos códigos de bytes cuando ejecutamos el programa. Sección 2.3 Modificación de nuestro primer programa en Java • System.out.print muestra su argumento en pantalla y coloca el cursor de salida justo después del último carácter visualizado. • Una barra diagonal inversa () en una cadena es un carácter de escape. Indica que se va a imprimir un “carácter especial”. Java combina el siguiente carácter con la barra diagonal inversa para formar una secuencia de escape. La secuencia de escape n representa el carácter de nueva línea, el cual coloca el cursor en la siguiente línea. Sección 2.4 Cómo mostrar texto con printf • El método System.out.printf (f significa “formato”) muestra datos con formato. • Cuando un método requiere varios argumentos, éstos se separan con comas (,); a esto se le conoce como lista sepa- rada por comas. • El primer argumento del método printf es una cadena de formato, que puede consistir en texto fijo y especifica- dores de formato. El método printf imprime el texto fijo de igual forma que print o println. Cada especificador de formato es un receptáculo para un valor, y especifica el tipo de datos a imprimir. • Los especificadores de formato empiezan con un signo porcentual (%), y van seguidos de un carácter que representa el tipo de datos. El especificador de formato %s es un receptáculo para una cadena. • En la posición del primer especificador de formato, printf sustituye el valor del primer argumento después de la cadena de formato. En la posición de los siguientes especificadores de formato, printf sustituye el valor del siguien- te argumento en la lista de argumentos. Sección 2.5 Otra aplicación en Java: suma de enteros • Los enteros son números completos, como –22 ,7, 0 y 1024. • Una declaración import ayuda al compilador a localizar una clase que se utiliza en un programa. • Java cuenta con un extenso conjunto de clases predefinidas que los programadores pueden reutilizar, en vez de tener que “reinventar la rueda”. Estas clases se agrupan en paquetes: llamados colecciones de clases. • En conjunto, a los paquetes de Java se les conoce como la biblioteca de clases de Java, o la Interfaz de Programación de Aplicaciones de Java (API de Java). • Una instrucción de declaración de variable especifica el nombre y el tipo de una variable. • Una variable es una ubicación en la memoria de la computadora, en la cual se puede guardar un valor para usarlo posteriormente en un programa. Todas las variables deben declararse con un nombre y un tipo para poder utili- zarlas. • El nombre de una variable permite al programa acceder al valor de la variable en memoria. Un nombre de variable puede ser cualquier identificador válido. • Al igual que otras instrucciones, las instrucciones de declaración de variables terminan con un punto y coma (;). • Un objeto Scanner (paquete java.util) permite a un programa leer datos para usarlos en éste. Los datos pueden provenir de muchas fuentes, como un archivo en disco o del usuario, a través del teclado. Antes de usar un objeto Scanner, el programa debe crearlo y especificar el origen de los datos. • Las variables deben inicializarse para poder usarlas en un programa. • La expresión new Scanner( System.in ) crea un objeto Scanner que lee datos desde el teclado. El objeto de entrada estándar, System.in, permite a las aplicaciones de Java leer los datos escritos por el usuario. • El tipo de datos int se utiliza para declarar variables que guardarán valores enteros. El rango de valores para un int es de –2,147,483,648 a +2,147,483,647. • Los tipos float y double especifican números reales, y el tipo char especifica datos de caracteres. Los números reales son números que contienen puntos decimales, como 3.4, 0.0 y –11.19. Las variables de tipo char representan caracteres individuales, como una letra mayúscula (por ejemplo, A), un dígito (por ejemplo, 7), un carácter especial (por ejemplo, * o %) o una secuencia de escape (por ejemplo, el carácter de nueva línea, n). • Los tipos como int, float, double y char se conocen comúnmente como tipos primitivos o tipos integrados. Los nombres de los tipos primitivos son palabras clave; por ende, deben aparecer escritos sólo con letras minúsculas. • Un indicador pide al usuario que realice una acción específica. • El método nextInt de Scanner obtiene un entero para usarlo en un programa. 66 Capítulo 2 Introducción a las aplicaciones en Java
  • 105. Terminología 67 • El operador de asignación, =, permite al programa dar un valor a una variable. El operador = se llama operador binario, ya que tiene dos operandos. Una instrucción de asignación utiliza un operador de asignación para asignar un valor a una variable. • Las partes de las instrucciones que tienen valores se llaman expresiones. • El especificador de formato %d es un receptáculo para un valor int. Sección 2.6 Conceptos acerca de la memoria • Los nombres de las variables corresponden a ubicaciones en la memoria de la computadora. Cada variable tiene un nombre, un tipo, un tamaño y un valor. • Cada vez que se coloca un valor en una ubicación de memoria, se sustituye al valor anterior en esa ubicación. El valor anterior se pierde. Sección 2.7 Aritmética • La mayoría de los programas realizan cálculos aritméticos. Los operadores aritméticos son + (suma), – (resta), * (multiplicación), / (división) y % (residuo). • La división de enteros produce un cociente entero. • El operador residuo, %, produce el residuo después de la división. • Las expresiones aritméticas en Java deben escribirse en formato de línea recta. • Si una expresión contiene paréntesis anidados, el conjunto de paréntesis más interno se evalúa primero. • Java aplica los operadores en las expresiones aritméticas en una secuencia precisa, la cual se determina mediante las reglas de precedencia de los operadores. • Cuando decimos que los operadores se aplican de izquierda a derecha, nos referimos a su asociatividad. Algunos operadores se asocian de derecha a izquierda. • Los paréntesis redundantes en una expresión pueden hacer que ésta sea más clara. Sección 2.8 Toma de decisiones: operadores de igualdad y relacionales • Una condición es una expresión que puede ser verdadera o falsa. La instrucción if de Java permite que un programa tome una decisión, con base en el valor de una condición. • Las condiciones en las instrucciones if se forman mediante el uso de los operadores de igualdad (== y !=) y relacio- nales (>, <, >= y <=). • Una instrucción if siempre empieza con la palabra clave if, seguida de una condición entre paréntesis, y espera una instrucción en su cuerpo. • La instrucción vacía es una instrucción que no realiza una tarea. Terminología %d, especificador de formato %s, especificador de formato .class, extensión de archivo .java, extensión de archivo aplicación archivo de clase argumento asociatividad de los operadores autodocumentado barra diagonal inversa (), carácter de escape biblioteca de clases de Java cadena cadena de caracteres cadena de formato carácter de escape caracteres de espacio en blanco cd, comando para cambiar directorios char, tipo primitivo clase definida por el programador clase definida por el usuario class, palabra clave comentario comentario de fin de línea (//) comentario de una sola línea (//) comentario de varias líneas (/* */) comentario tradicional (/* */) condición cuerpo de la declaración de un método cuerpo de la declaración de una clase cursor de salida decisión declaración de una clase declaración de variable división de enteros división, operador (/) documentación de la API de Java documento de un programa double, tipo primitivo entero error de compilación error de compilador error de sintaxis
  • 106. error en tiempo de compilación espacio en blanco especificador de formato false float, tipo primitivo formato de línea recta identificador if, instrucción igualdad, operadores == “es igual a” != “no es igual a” import, declaración indicador instrucción instrucción de asignación instrucción de declaración de variable instrucción vacía (;) int (entero), tipo primitivo Interfaz de Programación de Aplicaciones (API) de Java java, comando java.lang, paquete Javadoc, comentario (/** */) javadoc, programa de utilería línea de comandos lista separada por comas literal de cadena llave derecha (}) llave izquierda ({) main, método mensaje método multiplicación, operador (*) nombre de una clase nombre de una variable nombre de variable nueva línea, carácter (n) objeto objeto de entrada estándar (System.in) objeto de salida estándar (System.out) operador operador binario operador de asignación (=) operador de suma (+) operadores aritméticos (*, /, %, + y –) operando palabras reservadas paquete paréntesis () paréntesis anidados paréntesis redundantes precedencia programa de cómputo public, palabra clave punto y coma (;) realizar una acción reglas de precedencia de operadores relacionales, operadores < “es menor que” <= “es menor o igual a” > “es mayor que” >= “es mayor o igual a” residuo, operador (%) resta, operador (–) Scanner, clase secuencia de escape sensible a mayúsculas/minúsculas shell símbolo de MS-DOS Símbolo del sistema sintaxis System.in, objeto (entrada estándar) System.out, objeto (salida estándar) System.out.print, método System.out.printf, método System.out.println, método tamaño de una variable texto fijo en una cadena de formato tipo de una variable tipo integrado tipo primitivo tolerante a fallas true ubicación de memoria ubicación de una variable valor de variable variable ventana de comandos ventana de Terminal void, palabra clave 68 Capítulo 2 Introducción a las aplicaciones en Java Ejercicios de autoevaluación 2.1 Complete las siguientes oraciones: a) El cuerpo de cualquier método comienza con un(a) _____________ y termina con un(a) _____________. b) Toda instrucción termina con un _____________. c) La instrucción _____________ (presentada en este capítulo) se utiliza para tomar decisiones. d) _____________ indica el inicio de un comentario de fin de línea. e) ______________, ______________, ______________ y ______________ se conocen como espacio en blanco.
  • 107. Respuestas a los ejercicios de autoevaluación 69 f) Las _____________ están reservadas para su uso en Java. g) Las aplicaciones en Java comienzan su ejecución en el método _____________. h) Los métodos _____________, _____________ y _____________ muestran información en la ventana de comandos. 2.2 Indique si cada una de las siguientes instrucciones es verdadera o falsa. Si es falsa, explique por qué. a) Los comentarios hacen que la computadora imprima el texto que va después de los caracteres // en la pan- talla, al ejecutarse el programa. b) Todas las variables deben recibir un tipo cuando se declaran. c) Java considera que las variables numero y NuMeRo son idénticas. d) El operador residuo (%) puede utilizarse solamente con operandos enteros. e) Los operadores aritméticos *, /, %, + y – tienen todos el mismo nivel de precedencia. 2.3 Escriba instrucciones para realizar cada una de las siguientes tareas: a) Declarar las variables c, estaEsUnaVariable, q76354 y numero como de tipo int. b) Pedir al usuario que introduzca un entero. c) Recibir un entero como entrada y asignar el resultado a la variable int valor. Suponga que se puede utili- zar la variable entrada tipo Scanner para recibir un valor del teclado. d) Si la variable numero no es igual a 7, mostrar "La variable numero no es igual a 7". e) Imprimir "Este es un programa en Java" en una línea de la ventana de comandos. f) Imprimir "Este es un programa en Java" en dos líneas de la ventana de comandos. La primera línea debe terminar con es un. Use el método System.out.println. g) Imprimir "Este es un programa en Java" en dos líneas de la ventana de comandos. La primera línea debe terminar con es un. Use el método System.out.printf y dos especificadores de formato %s. 2.4 Identifique y corrija los errores en cada una de las siguientes instrucciones: a) if ( c < 7 ); System.out.println( "c es menor que 7" ); b) if ( c => 7 ) System.out.println( "c es igual o mayor que 7" ); 2.5 Escriba declaraciones, instrucciones o comentarios para realizar cada una de las siguientes tareas: a) Indicar que un programa calculará el producto de tres enteros. b) Crear un objeto Scanner que lea valores de la entrada estándar. c) Declarar las variables x, y, z y resultado de tipo int. d) Pedir al usuario que escriba el primer entero. e) Leer el primer entero del usuario y almacenarlo en la variable x. f) Pedir al usuario que escriba el segundo entero. g) Leer el segundo entero del usuario y almacenarlo en la variable y. h) Pedir al usuario que escriba el tercer entero. i) Leer el tercer entero del usuario y almacenarlo en la variable z. j) Calcular el producto de los tres enteros contenidos en las variables x, y y z, y asignar el resultado a la variable resultado. k) Mostrar el mensaje "El producto es", seguido del valor de la variable resultado. 2.6 Utilizando las instrucciones que escribió en el ejercicio 2.5, escriba un programa completo que calcule e impri- ma el producto de tres enteros. Respuestas a los ejercicios de autoevaluación 2.1 a) llave izquierda ({), llave derecha (}). b) punto y coma (;). c) if. d) //. e) Líneas en blanco, caracteres de espacio, caracteres de nueva línea y tabuladores. f) palabras clave. g) main. h) System.out.print, System.out. println y System.out.printf. 2.2 a) Falso. Los comentarios no producen ninguna acción cuando el programa se ejecuta. Se utilizan para docu- mentar programas y mejorar su legibilidad. b) Verdadero. c) Falso. Java es sensible a mayúsculas y minúsculas, por lo que estas variables son distintas.
  • 108. d) Falso. El operador residuo puede utilizarse también con operandos no enteros en Java. e) Falso. Los operadores *, / y % se encuentran en el mismo nivel de precedencia, y los operadores + y – se encuentran en un nivel menor de precedencia. 2.3 a) int c, estaEsUnaVariable, q76354, numero; o int c; int estaEsUnaVariable; int q76354; int numero; b) System.out.print( "Escriba un entero " ); c) valor = entrada.nextInt(); d) if ( numero != 7 ) System.out.println( "La variable numero no es igual a 7" ); e) System.out.println( "Este es un programa en Java" ); f) System.out.println( "Este es unn programa en Java" ); g) System.out.printf( "%s%sn", "Este es un", "programa en Java" ); 2.4 Las soluciones al ejercicio de autoevaluación 2.4 son las siguientes: a) Error: hay un punto y coma después del paréntesis derecho de la condición (c < 7 ) en la instrucción if. Corrección: quite el punto y coma que va después del paréntesis derecho. [Nota: como resultado, la instruc- ción de salida se ejecutará, sin importar que la condición en la instrucción if sea verdadera]. b) Error: el operador relacional => es incorrecto. Corrección: cambie => a >=. 2.5 a) // Calcula el producto de tres enteros b) Scanner entrada = new Scanner (System.in); c) int x, y, z, resultado; o int x; int y; int z; int resultado; d) System.out.print( "Escriba el primer entero: " ); e) x = entrada.nextInt(); f) System.out.print( "Escriba el segundo entero: " ); g) y = entrada.nextInt(); h) System.out.print( "Escriba el tercer entero: " ); i) z = entrada.nextInt(); j) resultado = x * y * z; k) System.out.printf( "El producto es %dn", resultado ); l) System.exit( 0 ); 2.6 La solución para el ejercicio 2.6 es la siguiente: 1 // Ejemplo 2.6: Producto.java 2 // Calcular el producto de tres enteros. 3 import java.util.Scanner; // el programa usa Scanner 4 5 public class Producto 6 { 7 public static void main( String args[] ) 8 { 9 // crea objeto Scanner para obtener la entrada de la ventana de comandos 10 Scanner entrada = new Scanner( System.in ); 11 12 int x; // primer número introducido por el usuario 13 int y; // segundo número introducido por el usuario 70 Capítulo 2 Introducción a las aplicaciones en Java
  • 109. Ejercicios 71 Ejercicios 2.7 Complete las siguientes oraciones: a) _____________ se utilizan para documentar un programa y mejorar su legibilidad. b) Una decisión puede tomarse en un programa en Java con un(a) _____________. c) Los cálculos se realizan normalmente mediante instrucciones _____________. d) Los operadores aritméticos con la misma precedencia que la multiplicación son _____________ y _____ ________ . e) Cuando los paréntesis en una expresión aritmética están anidados, el conjunto _____________ de parén- tesis se evalúa primero. f) Una ubicación en la memoria de la computadora que puede contener distintos valores en diversos instantes de tiempo, durante la ejecución de un programa, se llama _____________. 2.8 Escriba instrucciones en Java que realicen cada una de las siguientes tareas: a) Mostrar el mensaje "Escriba un entero:", dejando el cursor en la misma línea. b) Asignar el producto de las variables b y c a la variable a. c) Indicar que un programa va a realizar un cálculo de nómina de muestra (es decir, usar texto que ayude a documentar un programa). 2.9 Conteste con verdadero o falso a cada una de las siguientes proposiciones; en caso de ser falso, explique por qué. a) Los operadores en Java se evalúan de izquierda a derecha. b) Los siguientes nombres de variables son todos válidos: _barra_inferior_, m928134,t5, j7, sus_ventas$, su_$cuenta_total, a, b$, c, z y z2. c) Una expresión aritmética válida en Java sin paréntesis se evalúa de izquierda a derecha. d) Los siguientes nombres de variables son todos inválidos: 3g, 87, 67h2, h22 y 2h. 2.10 Suponiendo que x = 2 y y = 3, ¿qué muestra cada una de las siguientes instrucciones? a) System.out.printf( "x = %dn", x ); b) System.out.printf( "El valor de %d + %d es %dn", x, x, ( x + x ) ); c) System.out.printf( "x =" ); d) System.out.printf( "%d = %dn", ( x + y ), ( y + x ) ); 14 int z; // tercer número introducido por el usuario 15 int resultado; // producto de los números 16 17 System.out.print( "Escriba el primer entero: " ); // indicador de entrada 18 x = entrada.nextInt(); // lee el primer entero 19 20 System.out.print( "Escriba el segundo entero: " ); // indicador de entrada 21 y = entrada.nextInt(); // lee el segundo entero 22 23 System.out.print( "Escriba el tercer entero: " ); // indicador de entrada 24 z = entrada.nextInt(); // lee el tercer entero 25 26 resultado = x * y * z; // calcula el producto de los números 27 28 System.out.printf( "El producto es %dn", resultado ); 29 30 } // fin del método main 31 32 } // fin de la clase Producto Escriba el primer entero: 10 Escriba el segundo entero: 20 Escriba el tercer entero: 30 El producto es 6000
  • 110. 2.11 ¿Cuáles de las siguientes instrucciones de Java contienen variables, cuyos valores se modifican? a) p = i + j + k + 7; b) System.out.println( "variables cuyos valores se destruyen" ); c) System.out.println( "a = 5" ); d) valor = entrada.nextInt(); 2.12 Dado que y = ax3+ 7, ¿cuáles de las siguientes instrucciones en Java son correctas para esta ecuación? a) y = a * x * x * x + 7; b) y = a * x * x * ( x + 7 ); c) y = ( a * x ) * x * ( x + 7 ); d) y = ( a * x ) * x * x + 7; e) y = a * ( x * x * x ) + 7; f) y = a * x * ( x * x + 7 ); 2.13 Indique el orden de evaluación de los operadores en cada una de las siguientes instrucciones en Java, y muestre el valor x después de ejecutar cada una de ellas: a) x = 7 + 3 * 6 / 2 – 1; b) x = 2 % 2 + 2 * 2 – 2 / 2; c) x = ( 3 * 9 * ( 3 + ( 9 * 3 / ( 3 ) ) ) ); 2.14 Escriba una aplicación que muestre los números del 1 al 4 en la misma línea, con cada par de números adya- centes separado por un espacio. Escriba el programa utilizando las siguientes técnicas: a) Utilizando una instrucción System.out.println. b) Utilizando cuatro instrucciones System.out.print. c) Utilizando una instrucción System.out.printf. 2.15 Escriba una aplicación que pida al usuario que escriba dos números, que obtenga los números del usuario e imprima la suma, producto, diferencia y cociente (división) de los números. Use las técnicas que se muestran en la figura 2.7. 2.16 Escriba una aplicación que pida al usuario que escriba dos enteros, que obtenga los números del usuario y muestre el número más grande, seguido de las palabras "es más grande". Si los números son iguales, imprima el mensaje "Estos números son iguales". Utilice las técnicas que se muestran en la figura 2.15. 2.17 Escriba una aplicación que reciba tres enteros del usuario y muestre la suma, promedio, producto, menor y mayor de esos números. Utilice las técnicas que se muestran en la figura 2.15. [Nota: el cálculo del promedio en este ejercicio debe resultar en una representación entera del promedio. Por lo tanto, si la suma de los valores es 7, el prome- dio debe ser 2, no 2.3333...]. 2.18 Escriba una aplicación que muestre un cuadro, un óvalo, una flecha y un diamante usando asteriscos (*), como se muestra a continuación: ********* *** * * * * * * *** * * * * * * ***** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ********* *** * * 2.19 ¿Qué imprime el siguiente código? System.out.println( "*n**n***n****n*****" ); 2.20 ¿Qué imprime el siguiente código? System.out.println( "*" ); System.out.println( "***" ); System.out.println( "*****" ); System.out.println( "****" ); System.out.println( "**" ); 72 Capítulo 2 Introducción a las aplicaciones en Java
  • 111. Ejercicios 73 2.21 ¿Qué imprime el siguiente código? System.out.print( "*" ); System.out.print( "***" ); System.out.print( "*****" ); System.out.print( "****" ); System.out.println( "**" ); 2.22 ¿Qué imprime el siguiente código? System.out.print( "*" ); System.out.println( "***" ); System.out.println( "*****" ); System.out.print( "****" ); System.out.println( "**" ); 2.23 ¿Qué imprime el siguiente código? System.out.printf( "%sn%sn%sn", "*", "***", "*****" ); 2.24 Escriba una aplicación que lea cinco enteros y que determine e imprima los enteros mayor y menor en el grupo. Use solamente las técnicas de programación que aprendió en este capítulo. 2.25 Escriba una aplicación que lea un entero y que determine e imprima si es impar o par. [Sugerencia: use el operador residuo. Un número par es un múltiplo de 2. Cualquier múltiplo de 2 deja un residuo de 0 cuando se divide entre 2]. 2.26 Escriba una aplicación que lea dos enteros, determine si el primero es un múltiplo del segundo e imprima el resultado. [Sugerencia: use el operador residuo]. 2.27 Escriba una aplicación que muestre un patrón de tablero de damas, como se muestra a continuación: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2.28 He aquí un adelanto. En este capítulo, aprendió sobre los enteros y el tipo int. Java también puede representar números de punto flotante que contienen puntos decimales, como 3.14159. Escriba una aplicación que reciba del usua- rio el radio de un círculo como un entero, y que imprima el diámetro, la circunferencia y el área del círculo mediante el uso del valor de punto flotante 3.14159 para π. Use las técnicas que se muestran en la figura 2.7. [Nota: también puede utilizar la constante predefinida Math.PI para el valor de π. Esta constante es más precisa que el valor 3.14159. La clase Math se define en el paquete java.lang. Las clases en este paquete se importan de manera automática, por lo que no necesita importar la clase Math mediante la instrucción import para usarla]. Use las siguientes fórmulas (r es el radio): diámetro = 2r circunferencia = 2πr área = πr2 No almacene los resultados de cada cálculo en una variable. En vez de ello, especifique cada cálculo como el valor que se imprimirá en una instrucción System.out.printf. Observe que los valores producidos por los cálculos del área y la circunferencia son números de punto flotante. Dichos valores pueden imprimirse con el especificador de formato %f en una instrucción System.out.printf. En el capítulo 3 aprenderá más acerca de los números de punto flotante. 2.29 He aquí otro adelanto. En este capítulo, aprendió acerca de los enteros y el tipo int. Java puede también repre- sentar letras en mayúsculas, en minúsculas y una considerable variedad de símbolos especiales. Cada carácter tiene su correspondiente representación entera. El conjunto de caracteres que utiliza una computadora, y las correspondientes representaciones enteras de esos caracteres, se conocen como el conjunto de caracteres de esa computadora. Usted puede indicar un valor de carácter en un programa con sólo encerrar ese carácter entre comillas sencillas, como en 'A'.
  • 112. Usted puede determinar el equivalente entero de un carácter si antepone a ese carácter la palabra (int), como en (int) 'A' Esta forma se conoce como operador de conversión de tipo. (Hablaremos más sobre estos operadores en el capítulo 4). La siguiente instrucción imprime un carácter y su equivalente entero: System.out.printf( "El caracter %c tiene el valor %dn", 'A', ( (int) 'A' ) ); Cuando se ejecuta esta instrucción, muestra el carácter A y el valor 65 (del conjunto de caracteres conocido como Uni- code®) como parte de la cadena. Observe que el especificador de formato %c es un receptáculo para un carácter (en este caso, el carácter 'A'). Utilizando instrucciones similares a la mostrada anteriormente en este ejercicio, escriba una aplicación que muestre los equivalentes enteros de algunas letras en mayúsculas, en minúsculas, dígitos y símbolos especiales. Muestre los equivalentes enteros de los siguientes caracteres: A B C a b c 0 1 2 $ * + / y el carácter en blanco. 2.30 Escriba una aplicación que reciba del usuario un número compuesto por cinco dígitos, que separe ese número en sus dígitos individuales y los imprima, cada uno separado de los demás por tres espacios. Por ejemplo, si el usuario escribe el número 42339, el programa debe imprimir 4 2 3 3 9 Suponga que el usuario escribe el número correcto de dígitos. ¿Qué ocurre cuando ejecuta el programa y escribe un número con más de cinco dígitos? ¿Qué ocurre cuando ejecuta el programa y escribe un número con menos de cinco dígitos? [Sugerencia: es posible hacer este ejercicio con las técnicas que aprendió en este capítulo. Necesitará utilizar los operadores de división y residuo para “seleccionar” cada dígito]. 2.31 Utilizando sólo las técnicas de programación que aprendió en este capítulo, escriba una aplicación que calcule los cuadrados y cubos de los números del 0 al 10, y que imprima los valores resultantes en formato de tabla, como se muestra a continuación. [Nota: Este programa no requiere de ningún tipo de entrada por parte del usuario]. numero cuadrado cubo 0 0 0 1 1 1 2 4 8 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 2.32 Escriba un programa que reciba cinco números, y que determine e imprima la cantidad de números negativos, positivos, y la cantidad de ceros recibidos. 74 Capítulo 2 Introducción a las aplicaciones en Java
  • 113. Introducción a las clases y los objetos OBJETIVOS En este capítulo aprenderá a: Comprender qué son las clases, los objetos, los métodos y las variables de instancia. Declarar una clase y utilizarla para crear un objeto. Declarar métodos en una clase para implementar los comportamientos de ésta. Declarar variables de instancia en una clase para implementar los atributos de ésta. Saber cómo llamar a los métodos de un objeto para hacer que realicen sus tareas. Conocer las diferencias entre las variables de instancia de una clase y las variables locales de un método. Utilizar un constructor para asegurar que los datos de un objeto se inicialicen cuando se cree el objeto. Conocer las diferencias entre los tipos primitivos y los tipos por referencia. Q Q Q Q Q Q Q Q Usted verá algo nuevo. Dos cosas. Y las llamo Cosa Uno y Cosa Dos. —Dr. Theodor Seuss Geisel. Nada puede tener valor sin ser un objeto de utilidad. —Karl Marx Sus sirvientes públicos le sirven bien. —Adlai E. Stevenson Saber cómo responder a alguien que habla, contestar a alguien que envía un mensaje. —Amenemope 3
  • 114. 76 Capítulo 3 Introducción a las clases y los objetos 3.1 Introducción En la sección 1.16 le presentamos la terminología básica y los conceptos acerca de la programación orientada a objetos. El capítulo 2 comenzó a utilizar esos conceptos para crear aplicaciones simples que mostraran mensajes al usuario, que obtuvieran información del usuario, realizaran cálculos y tomaran decisiones. Una característica común de todas las aplicaciones del capítulo 2 fue que todas las instrucciones que realizaban tareas se encontraban en el método main. Por lo general, las aplicaciones que usted desarrollará en este libro consistirán de dos o más clases, cada una de las cuales contendrá dos o más métodos. Si usted se integra a un equipo de desarrollo en la industria, podría trabajar en aplicaciones que contengan cientos, o incluso hasta miles de clases. En este capítulo presentaremos un marco de trabajo simple para organizar las aplicaciones orientadas a objetos en Java. Primero explicaremos el concepto de las clases mediante el uso de un ejemplo real. Después presentaremos cinco aplicaciones completas para demostrarle cómo crear y utilizar sus propias clases. Los primeros cuatro ejem- plos empiezan nuestro ejemplo práctico acerca de cómo desarrollar una clase tipo libro de calificaciones, que los instructores pueden utilizar para mantener las calificaciones de las pruebas de sus estudiantes. Durante los siguientes capítulos ampliaremos este ejemplo práctico y culminaremos con la versión que se presenta en el capí- tulo 7, Arreglos. El último ejemplo en este capítulo introduce los números de punto flotante (es decir, números que contienen puntos decimales, como 0.0345, –7.23 y 100.7) en el contexto de una clase tipo cuenta bancaria, la cual mantiene el saldo de un cliente. 3.2 Clases, objetos, métodos y variables de instancia Comenzaremos con una analogía simple, para ayudarle a comprender el concepto de las clases y su contenido. Suponga que desea conducir un automóvil y, para hacer que aumente su velocidad, debe presionar el pedal del ace- lerador. ¿Qué debe ocurrir antes de que pueda hacer esto? Bueno, antes de poder conducir un automóvil, alguien tiene que diseñarlo. Por lo general, un automóvil empieza en forma de dibujos de ingeniería, similares a los planos de construcción que se utilizan para diseñar una casa. Estos dibujos de ingeniería incluyen el diseño del pedal del acelerador, para que el automóvil aumente su velocidad. El pedal “oculta” los complejos mecanismos que se encargan de que el automóvil aumente su velocidad, de igual forma que el pedal del freno “oculta” los mecanismos que disminuyen la velocidad del automóvil y por otro lado, el volante “oculta” los mecanismos que hacen que el automóvil de vuelta. Esto permite que las personas con poco o nada de conocimiento acerca de cómo funcionan los motores puedan conducir un automóvil con facilidad. Desafortunadamente, no puede conducir los dibujos de ingeniería de un auto. Antes de poder conducir un automóvil, éste debe construirse a partir de los dibujos de ingeniería que lo describen. Un automóvil completo tendrá un pedal acelerador verdadero para hacer que aumente su velocidad, pero aún así no es suficiente; el auto- móvil no acelerará por su propia cuenta, así que el conductor debe oprimir el pedal del acelerador. Ahora utilizaremos nuestro ejemplo del automóvil para introducir los conceptos clave de programación de esta sección. Para realizar una tarea en una aplicación se requiere un método. El método describe los mecanismos 3.1 Introducción 3.2 Clases, objetos, métodos y variables de instancia 3.3 Declaración de una clase con un método e instanciamiento de un objeto de una clase 3.4 Declaración de un método con un parámetro 3.5 Variables de instancia, métodos establecer y métodos obtener 3.6 Comparación entre tipos primitivos y tipos por referencia 3.7 Inicialización de objetos mediante constructores 3.8 Números de punto flotante y el tipo double 3.9 (Opcional) Ejemplo práctico de GUI y gráficos: uso de cuadros de diálogo 3.10 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un documento de requerimientos 3.11 Conclusión Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios Pla n g e ne r a l
  • 115. que se encargan de realizar sus tareas; y oculta al usuario las tareas complejas que realiza, de la misma forma que el pedal del acelerador de un automóvil oculta al conductor los complejos mecanismos para hacer que el automóvil vaya más rápido. En Java, empezamos por crear una unidad de aplicación llamada clase para alojar a un método, así como los dibujos de ingeniería de un automóvil alojan el diseño del pedal del acelerador. En una clase se pro- porcionan uno o más métodos, los cuales están diseñados para realizar las tareas de esa clase. Por ejemplo, una clase que representa a una cuenta bancaria podría contener un método para depositar dinero en una cuenta, otro para retirar dinero de una cuenta y un tercero para solicitar el saldo actual de la cuenta. Así como no podemos conducir un dibujo de ingeniería de un automóvil, tampoco podemos “conducir” una clase. De la misma forma que alguien tiene que construir un automóvil a partir de sus dibujos de ingeniería para poder conducirlo, también debemos construir un objeto de una clase para poder hacer que un programa realice las tareas que la clase le describe cómo realizar. Ésta es una de las razones por las cuales Java se conoce como un lenguaje de programación orientado a objetos. Cuando usted conduce un automóvil, si oprime el pedal del acelerador se envía un mensaje al automóvil para que realice una tarea-hacer que el automóvil vaya más rápido. De manera similar, se envían mensajes a un objeto; cada mensaje se conoce como la llamada a un método, e indica a un método del objeto que realice su tarea. Hasta ahora, hemos utilizado la analogía del automóvil para introducir las clases, los objetos y los métodos. Además de las capacidades con las que cuenta un automóvil, también tiene muchos atributos como su color, el número de puertas, la cantidad de gasolina en su tanque, su velocidad actual y el total de kilómetros recorridos (es decir, la lectura de su odómetro). Al igual que las capacidades del automóvil, estos atributos se representan como parte del diseño en sus diagramas de ingeniería. Cuando usted conduce un automóvil, estos atributos siempre están asociados con él. Cada uno mantiene sus propios atributos. Por ejemplo, cada conductor sabe cuánta gasolina tiene en su propio tanque, pero no cuánta hay en los tanques de otros automóviles. De manera similar, un objeto tiene atributos que lleva consigo cuando se utiliza en un programa. Éstos se especifican como parte de la clase del objeto. Por ejemplo, un objeto tipo cuenta bancaria tiene un atributo llamado saldo, el cual representa la cantidad de dine- ro en la cuenta. Cada objeto tipo cuenta bancaria conoce el saldo en la cuenta que representa, pero no los saldos de las otras cuentas en el banco. Los atributos se especifican mediante las variables de instancia de la clase. El resto de este capítulo presenta ejemplos que demuestran los conceptos que presentamos aquí, dentro del contexto de la analogía del automóvil. Los primeros cuatro ejemplos se encargan de construir en forma incremen- tal una clase llamada LibroCalificaciones para demostrar estos conceptos: 1. El primer ejemplo presenta una clase llamada LibroCalificaciones, con un método que simplemente muestra un mensaje de bienvenida cuando se le llama. Después le mostraremos cómo crear un objeto de esa clase y cómo llamarlo, para que muestre el mensaje de bienvenida. 2. El segundo ejemplo modifica el primero, al permitir que el método reciba el nombre de un curso como argumento, y al mostrar ese nombre como parte del mensaje de bienvenida. 3. El tercer ejemplo muestra cómo almacenar el nombre del curso en un objeto tipo LibroCalificacio- nes. Para esta versión de la clase, también le mostraremos cómo utilizar los métodos para establecer el nombre del curso y obtener este nombre. 4. El cuarto ejemplo demuestra cómo pueden inicializarse los datos en un objeto tipo LibroCalificaciones, a la hora de crear el objeto; el constructor de la clase se encarga de realizar el proceso de inicialización. El último ejemplo en el capítulo presenta una clase llamada Cuenta, la cual refuerza los conceptos presentados en los primeros cuatro ejemplos, e introduce los números de punto flotante. Para este fin, presentamos una clase llamada Cuenta, la cual representa una cuenta bancaria y mantiene su saldo como un número de punto flotante. La clase contiene dos métodos —uno para acreditar un depósito a la cuenta, con lo cual se incrementa el saldo, y otro para obtener el saldo. El constructor de la clase permite inicializar el saldo de cada objeto tipo Cuenta, a la hora de crear el objeto. Crearemos dos objetos tipo Cuenta y haremos depósitos en cada uno de ellos, para mostrar que cada objeto mantiene su propio saldo. El ejemplo también demuestra cómo recibir e imprimir en pantalla números de punto flotante. 3.3 Declaración de una clase con un método e instanciamiento de un objeto de una clase Comenzaremos con un ejemplo que consiste en las clases LibroCalificaciones (figura 3.1) y PruebaLibroCa- lificaciones (figura 3.2). La clase LibroCalificaciones (declarada en el archivo LibroCalificaciones.java) 3.3 Declaración de una clase con un método e instanciamiento de un objeto de una clase 77
  • 116. 78 Capítulo 3 Introducción a las clases y los objetos se utilizará para mostrar un mensaje en la pantalla (figura 3.2), para dar la bienvenida al instructor a la aplicación del libro de calificaciones. La clase PruebaLibroCalificaciones (declarada en el archivo PruebaLibroCalifi- caciones.java) es una clase de aplicación en la que el método main utilizará a la clase LibroCalificaciones. Cada declaración de clase que comienza con la palabra clave public debe almacenarse en un archivo que tenga el mismo nombre que la clase, y que termine con la extensión de archivo .java. Por lo tanto, las clases Libro- Calificaciones y PruebaLibroCalificaciones deben declararse en archivos separados, ya que cada clase se declara como public. Error común de programación 3.1 Declarar más de una clase public en el mismo archivo es un error de compilación. La clase LibroCalificaciones La declaración de la clase LibroCalificaciones (figura 3.1) contiene un método llamado mostrarMensaje (líneas 7-10), el cual muestra un mensaje en la pantalla. La línea 9 de la clase realiza el trabajo de mostrar el men- saje. Recuerde que una clase es como un plano de construcción; necesitamos crear un objeto de esta clase y llamar a su método para hacer que se ejecute la línea 9 y que muestre su mensaje. La declaración de la clase empieza en la línea 4. La palabra clave public es un modificador de acceso. Por ahora, simplemente declararemos cada clase como public. Toda declaración de clase contiene la palabra clave class, seguida inmediatamente por el nombre de la clase. El cuerpo de toda clase se encierra entre una llave izquierda y una derecha ({ y }), como en las líneas 5 y 12 de la clase LibroCalificaciones. En el capítulo 2, cada clase que declaramos tenía un método llamado main. La clase LibroCalificaciones también tiene un método: mostrarMensaje (líneas 7-10). Recuerde que main es un método especial, que siem- pre es llamado, automáticamente, por la Máquina Virtual de Java (JVM) a la hora de ejecutar una aplicación. La mayoría de los métodos no se llaman en forma automática. Como veremos en breve, es necesario llamar al método mostrarMensaje para decirle que haga su trabajo. La declaración del método comienza con la palabra clave public para indicar que el método está “disponible al público”; es decir, los métodos de otras clases pueden llamarlo desde el exterior del cuerpo de la declaración de la clase. La palabra clave void indica que este método realizará una tarea pero no devolverá (es decir, regre- sará) información al método que hizo la llamada cuando complete su tarea. Ya hemos utilizado métodos que devuelven información; por ejemplo, en el capítulo 2 utilizó el método nextInt de Scanner para recibir un entero escrito por el usuario desde el teclado. Cuando nextInt recibe un valor de entrada, devuelve ese valor para utilizarlo en el programa. El nombre del método, mostrarMensaje, va después del tipo de valor de retorno. Por convención, los nombres de los métodos comienzan con una letra minúscula, y el resto de las palabras en el nombre empiezan con letra mayúscula. Los paréntesis después del nombre del método indican que éste es un método. Un conjunto vacío de paréntesis, como se muestra en la línea 7, indica que este método no requiere información adicional para realizar su tarea. La línea 7 se conoce comúnmente como el encabezado del método. El cuerpo de cada método se delimita mediante una llave izquierda y una llave derecha ({ y }), como en las líneas 8 y 10. Figura 3.1 | Declaración de una clase con un método. 1 // Fig. 3.1: LibroCalificaciones.java 2 // Declaración de una clase con un método. 3 4 public class LibroCalificaciones 5 { 6 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones 7 public void mostrarMensaje() 8 { 9 System.out.println( “Bienvenido al Libro de calificaciones!” ); 10 } // fin del método mostrarMensaje 11 12 } // fin de la clase LibroCalificaciones
  • 117. El cuerpo de un método contiene una o varias instrucciones que realizan su trabajo. En este caso, el método contiene una instrucción (línea 9) que muestra el mensaje "Bienvenido al Libro de calificaciones!", seguido de una nueva línea en la ventana de comandos. Una vez que se ejecuta esta instrucción, el método ha completado su trabajo. A continuación, nos gustaría utilizar la clase LibroCalificaciones en una aplicación. Como aprendió en el capítulo 2, el método main empieza la ejecución de todas las aplicaciones. Una clase que contiene el método main es una aplicación de Java. Dicha clase es especial, ya que la JVM puede utilizar a main como un punto de entrada para empezar la ejecución. La clase LibroCalificaciones no es una aplicación, ya que no contiene a main. Por lo tanto, si trata de ejecutar LibroCalificaciones escribiendo java LibroCalificaciones en la ventana de comandos, recibirá un mensaje de error como este: Exception in thread "main" java.lang.NoSuchMethodError: main Esto no fue un problema en el capítulo 2, ya que cada clase que declaramos tenía un método main. Para corregir este problema con la clase LibroCalificaciones, debemos declarar una clase separada que contenga un método main, o colocar un método main en la clase LibroCalificaciones. Para ayudarlo a prepararse para los programas más extensos que encontrará más adelante en este libro y en la industria, utilizamos una clase separada (Prueba- LibroCalificaciones en este ejemplo) que contiene el método main para probar cada nueva clase que vayamos a crear en este capítulo. La clase PruebaLibroCalificaciones La declaración de la clase PruebaLibroCalificaciones (figura 3.2) contiene el método main que controlará la ejecución de nuestra aplicación. Cualquier clase que contiene el método main, declarado como se muestra en la línea 7, puede utilizarse para ejecutar una aplicación. La declaración de la clase PruebaLibroCalificaciones empieza en la línea 4 y termina en la línea 16. La clase sólo contiene un método main, algo común en muchas clases que empiezan la ejecución de una aplicación. Las líneas 7 a la 14 declaran el método main. En el capítulo 2 vimos que el encabezado main debe aparecer como se muestra en la línea 7; en caso contrario, no se ejecutará la aplicación. Una parte clave para permitir que la JVM localice y llame al método main para empezar la ejecución de la aplicación es la palabra clave static (línea 7), la cual indica que main es un método static. Un método static es especial, ya que puede llamarse sin tener que crear primero un objeto de la clase en la cual se declara ese método. En el capítulo 6, Métodos: un análisis más detallado, explicaremos a detalle los métodos static. Figura 3.2 | Cómo crear un objeto de la clase LibroCalificaciones y llamar a su método mostrarMensaje. 1 // Fig. 3.2: PruebaLibroCalificaciones.java 2 // Crea un objeto LibroCalificaciones y llama a su método mostrarMensaje. 3 4 public class PruebaLibroCalificaciones 5 { 6 // el método main empieza la ejecución del programa 7 public static void main( String args[] ) 8 { 9 // crea un objeto LibroCalificaciones y lo asigna a miLibroCalificaciones 10 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones(); 11 12 // llama al método mostrarMensaje de miLibroCalificaciones 13 miLibroCalificaciones.mostrarMensaje(); 14 } // fin de main 15 16 } // fin de la clase PruebaLibroCalificaciones Bienvenido al Libro Calificaciones! 3.3 Declaración de una clase con un método e instanciamiento de un objeto de una clase 79
  • 118. 80 Capítulo 3 Introducción a las clases y los objetos En esta aplicación nos gustaría llamar al método mostrarMensaje de la clase LibroCalificaciones para mostrar el mensaje de bienvenida en la ventana de comandos. Por lo general, no podemos llamar a un método que pertenece a otra clase, sino hasta crear un objeto de esa clase, como se muestra en la línea 10. Empezaremos por declarar la variable miLibroCalificaciones. Observe que el tipo de la variable es LibroCalificaciones; la clase que declaramos en la figura 3.1. Cada nueva clase que creamos se convierte en un nuevo tipo, que puede usarse para declarar variables y crear objetos. Los programadores pueden declarar nuevos tipos de clases según lo necesiten; ésta es una razón por la cual Java se conoce como un lenguaje extensible. La variable miLibroCalificaciones se inicializa con el resultado de la expresión de creación de instan- cia de clase new LibroCalificaciones(). La palabra clave new crea un nuevo objeto de la clase especificada a la derecha de la palabra clave (es decir, LibroCalificaciones). Los paréntesis a la derecha de LibroCalificaciones son obligatorios. Como veremos en la sección 3.7, esos paréntesis en combinación con el nombre de una clase representan una llamada a un constructor, que es similar a un método, pero se utiliza sólo cuando se crea un objeto, para inicializar los datos de éste. En esa sección verá que los datos pueden colocarse entre paréntesis para especificar los valores iniciales para los datos del objeto. Por ahora, sólo dejaremos los paréntesis vacíos. Así como podemos usar el objeto System.out para llamar a los métodos print, printf y println, tam- bién podemos usar el objeto miLibroCalificaciones para llamar al método mostrarMensaje. La línea 13 llama al método mostrarMensaje (líneas 7-10 de la figura 3.1), usando miLibroCalificaciones seguida de un sepa- rador punto (.), el nombre del método mostrarMensaje y un conjunto vacío de paréntesis. Esta llamada hace que el método mostrarMensaje realice su tarea. La llamada a este método difiere de las del capítulo 2 en las que se mostraba la información en una ventana de comandos; cada una de estas llamadas al método proporcionaban argumentos que especificaban los datos a mostrar. Al inicio de la línea 13, “miLibroCalificaciones”. Indica que main debe utilizar el objeto miLibroCalificaciones que se creó en la línea 10. La línea 7 de la figura 3.1 indica que el método mostrarMensaje tiene una lista de parámetros vacía; es decir, mostrarMensaje no requiere infor- mación adicional para realizar su tarea. Por esta razón, la llamada al método (línea 13 de la figura 3.2) especifica un conjunto vacío de paréntesis después del nombre del método, para indicar que no se van a pasar argumentos al método mostrarMensaje. Cuando el método mostrarMensaje completa su tarea, el método main continúa su ejecución en la línea 14. Éste es el final del método main, por lo que el programa termina. Compilación de una aplicación con varias clases Debe compilar las clases de las figuras 3.1 y 3.2 antes de poder ejecutar la aplicación. Primero, cambie al directorio que contiene los archivos de código fuente de la aplicación. Después, escriba el comando javac LibroCalificaciones.java PruebaLibroCalificaciones.java para compilar ambas clases a la vez. Si el directorio que contiene la aplicación sólo incluye los archivos de esta aplicación, puede compilar todas las clases que haya en el directorio con el comando javac *.java El asterisco (*) en *.java indica que deben compilarse todos los archivos en el directorio actual que terminen con la extensión de nombre de archivo “.java”. Diagrama de clases de UML para la clase LibroCalificaciones La figura 3.3 presenta un diagrama de clases de UML para la clase LibroCalificaciones de la figura 3.1. En la sección 1.16 vimos que UML es un lenguaje gráfico, utilizado por los programadores para representar sistemas orientados a objetos en forma estandarizada. En UML, cada clase se modela en un diagrama de clases en forma de un rectángulo con tres componentes. El compartimiento superior contiene el nombre de la clase, centrado en forma horizontal y en negrita. El compartimiento de en medio contiene los atributos de la clase, que en Java corresponden a las variables de instancia. En la figura 3.3, el compartimiento de en medio está vacío, ya que la versión de la clase LibroCalificaciones en la figura 3.1 no tiene atributos. El compartimiento inferior contiene las operaciones de la clase, que en Java corresponden a los métodos. Para modelar las operaciones, UML lista el nombre de la operación precedido por un modificador de acceso y seguido de un conjunto de paréntesis. La clase LibroCalificaciones tiene un solo método llamado mostrarMensaje, por lo que el compartimiento inferior de la figura 3.3 lista una operación con este nombre. El método mostrarMensaje no requiere información adicio- nal para realizar sus tareas, por lo que los paréntesis que van después del nombre del método en el diagrama de
  • 119. clases están vacíos, de igual forma que como aparecieron en la declaración del método, en la línea 7 de la figura 3.1. El signo más (+) que va antes del nombre de la operación indica que mostrarMensaje es una operación public en UML (es decir, un método public en Java). Utilizaremos los diagramas de clases de UML a menudo para sintetizar los atributos y las operaciones de una clase. 3.4 Declaración de un método con un parámetro En nuestra analogía del automóvil de la sección 3.2, hablamos sobre el hecho de que al oprimir el pedal del acele- rador se envía un mensaje al automóvil para que realice una tarea: hacer que vaya más rápido. Pero ¿qué tan rápido debería acelerar el automóvil? Como sabe, entre más oprima el pedal, mayor será la aceleración del automóvil. Por lo tanto, el mensaje para el automóvil en realidad incluye tanto la tarea a realizar como información adicional que ayuda al automóvil a ejecutar su tarea. A la información adicional se le conoce como parámetro; el valor del parámetro ayuda al automóvil a determinar qué tan rápido debe acelerar. De manera similar, un método puede requerir uno o más parámetros que representan la información adicional que necesita para realizar su tarea. La llamada a un método proporciona valores (llamados argumentos) para cada uno de los parámetros de ese méto- do. Por ejemplo, el método System.out.println requiere un argumento que especifica los datos a mostrar en una ventana de comandos. De manera similar, para realizar un depósito en una cuenta bancaria, un método llamado deposito especifica un parámetro que representa el monto a depositar. Cuando se hace una llamada al método deposito, se asigna al parámetro del método un valor como argumento, que representa el monto a depositar. Entonces, el método realiza un depósito por ese monto. Nuestro siguiente ejemplo declara la clase LibroCalificaciones (figura 3.4), con un método mostrar- Mensaje que muestra el nombre del curso como parte del mensaje de bienvenida (en la figura 3.5 podrá ver la ejecución de ejemplo). El nuevo método mostrarMensaje requiere un parámetro que representa el nombre del curso a imprimir en pantalla. Antes de hablar sobre las nuevas características de la clase LibroCalificaciones, veamos cómo se utiliza la nueva clase desde el método main de la clase PruebaLibroCalificaciones (figura 3.5). La línea 12 crea un obje- to Scanner llamado entrada, para recibir el nombre del curso escrito por el usuario. La línea 15 crea un objeto de la clase LibroCalificaciones y lo asigna a la variable miLibroCalificaciones. La línea 18 pide al usuario que escriba el nombre de un curso. La línea 19 lee el nombre que introduce el usuario y lo asigna a la variable nombreDelCurso, mediante el uso del método nextLine de Scanner para realizar la operación de entrada. El Figura 3.3 | Diagrama de clases de UML, el cual indica que la clase LibroCalificaciones tiene una operación public llamada mostrarMensaje. LibroCalificaciones + mostrarMensaje( ) Figura 3.4 | Declaración de una clase con un método que tiene un parámetro. 1 // Fig. 3.4: LibroCalificaciones.java 2 // Declaración de una clase con un método que tiene un parámetro. 3 4 public class LibroCalificaciones 5 { 6 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones 7 public void mostrarMensaje( String nombreDelCurso ) 8 { 9 System.out.printf( “Bienvenido al libro de calificaciones paran%s!n”, 10 nombreDelCurso ); 11 } // fin del método mostrarMensaje 12 13 } // fin de la clase LibroCalificaciones 3.4 Declaración de un método con un parámetro 81
  • 120. 82 Capítulo 3 Introducción a las clases y los objetos usuario escribe el nombre del curso y oprime Intro para enviarlo al programa. Observe que al oprimir Intro se inserta un carácter de nueva línea al final de los caracteres escritos por el usuario. El método nextLine lee los caracteres que escribió el usuario hasta encontrar el carácter de nueva línea, y después devuelve un objeto String que contiene los caracteres hasta, pero sin incluir, la nueva línea. El carácter de nueva línea se descarta. La clase Scanner también cuenta con un método similar (next) para leer palabras individuales. Cuando el usuario oprime Intro después de escribir la entrada, el método next lee caracteres hasta encontrar un carácter de espacio en blanco (espacio, tabulador o nueva línea), y después devuelve un objeto String que contiene los caracteres hasta, pero sin incluir, el carácter de espacio en blanco (que se descarta). No se pierde toda la información que va después del primer carácter de espacio en blanco; estará disponible para que la lean otras instrucciones que llamen a los métodos de Scanner, más adelante en el programa. La línea 24 llama al método mostrarMensaje de miLibroCalificaciones. La variable nombreDelCurso entre paréntesis es el argumento que se pasa al método mostrarMensaje, para que éste pueda realizar su tarea. El valor de la variable nombreDelCurso en main se convierte en el valor del parámetro nombreDelCurso del método mostrarMensaje, en la línea 7 de la figura 3.4. Al ejecutar esta aplicación, observe que el método mostrarMen- saje imprime en pantalla el nombre que usted escribió como parte del mensaje de bienvenida (figura 3.5). Observación de ingeniería de software 3.1 Por lo general, los objetos se crean mediante el uso de new. Una excepción es la literal de cadena que está encerrada entre comillas, como “hola”. Las literales de cadena son referencias a objetos String que Java crea de manera implícita. 1 // Fig. 3.5: PruebaLibroCalificaciones.java 2 // Crea un objeto LibroCalificaciones y pasa un objeto String 3 // a su método mostrarMensaje. 4 import java.util.Scanner; // el programa usa la clase Scanner 5 6 public class PruebaLibroCalificaciones 7 { 8 // el método main empieza la ejecución del programa 9 public static void main( String args[] ) 10 { 11 // crea un objeto Scanner para obtener la entrada de la ventana de comandos 12 Scanner entrada = new Scanner( System.in ); 13 14 // crea un objeto LibroCalificaciones y lo asigna a miLibroCalificaciones 15 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones(); 16 17 // pide y recibe el nombre del curso como entrada 18 System.out.println( “Escriba el nombre del curso:” ); 19 String nombreDelCurso = entrada.nextLine(); // lee una línea de texto 20 System.out.println(); // imprime una línea en blanco 21 22 // llama al método mostrarMensaje de miLibroCalificaciones 23 // y pasa nombreDelCurso como argumento 24 miLibroCalificaciones.mostrarMensaje( nombreDelCurso ); 25 } // fin de main 26 27 } // fin de la clase PruebaLibroCalificaciones Escriba el nombre del curso: CS101 Introduccion a la programacion en Java Bienvenido al libro de calificaciones para CS101 Introduccion a la programacion en Java! Figura 3.5 | Cómo crear un objeto LibroCalificaciones y pasar un objeto String a su método mostrarMensaje.
  • 121. Más sobre los argumentos y los parámetros Al declarar un método, debe especificar si el método requiere datos para realizar su tarea. Para ello es necesario colocar información adicional en la lista de parámetros del método, la cual se encuentra en los paréntesis que van después del nombre del método. La lista de parámetros puede contener cualquier número de parámetros, incluso ninguno. Los paréntesis vacíos después del nombre del método (como en la figura 3.1, línea 7) indican que un método no requiere parámetros. En la figura 3.4, la lista de parámetros de mostrarMensaje (línea 7) declara que el método requiere un parámetro. Cada parámetro debe especificar un tipo y un identificador. En este caso, el tipo String y el identificador nombreDelCurso indican que el método mostrarMensaje requiere un objeto String para realizar su tarea. En el instante en que se llama al método, el valor del argumento en la llamada se asigna al parámetro correspondiente (en este caso, nombreDelCurso) en el encabezado del método. Después, el cuerpo del método utiliza el parámetro nombreDelCurso para acceder al valor. Las líneas 9 y 10 de la figura 3.4 muestran el valor del parámetro nombreDelCurso, mediante el uso del especificador de formato %s en la cadena de formato de printf. Observe que el nombre de la variable de parámetro (figura 3.4, línea 7) puede ser igual o distinto al nombre de la variable de argumento (figura 3.5, línea 24). Un método puede especificar múltiples parámetros; sólo hay que separar un parámetro de otro mediante una coma (en el capítulo 6 veremos un ejemplo de esto). El número de argumentos en la llamada a un método debe coincidir con el número de parámetros en la lista de parámetros de la declaración del método que se llamó. Ade- más, los tipos de los argumentos en la llamada al método deben ser “consistentes con” los tipos de los parámetros correspondientes en la declaración del método (como veremos en capítulos posteriores, no siempre se requiere que el tipo de un argumento y el tipo de su correspondiente parámetro sean idénticos). En nuestro ejemplo, la llamada al método pasa un argumento de tipo String (nombreDelCurso se declara como String en la línea 19 de la figura 3.5) y la declaración del método especifica un parámetro de tipo String (línea 7 en la figura 3.4). Por lo tanto, en este ejemplo, el tipo del argumento en la llamada al método coincide exactamente con el tipo del parámetro en el encabezado del método. Error común de programación 3.2 Si el número de argumentos en la llamada a un método no coincide con el número de parámetros en la declaración del método, se produce un error de compilación. Error común de programación 3.3 Si los tipos de los argumentos en la llamada a un método no son consistentes con los tipos de los parámetros corres- pondientes en la declaración del método, se produce un error de compilación. Diagrama de clases de UML actualizado para la clase LibroCalificaciones El diagrama de clases de UML de la figura 3.6 modela la clase LibroCalificaciones de la figura 3.4. Al igual que la Figura 3.1, esta clase LibroCalificaciones contiene la operación public llamada mostrarMensaje. Sin embargo, esta versión de mostrarMensaje tiene un parámetro. La forma en que UML modela un parámetro es un poco distinta a la de Java, ya que lista el nombre del parámetro, seguido de dos puntos y del tipo del parámetro entre paréntesis, después del nombre de la operación. UML tiene sus propios tipos de datos, que son similares a los de Java (pero como veremos, no todos los tipos de datos de UML tienen los mismos nombres que los tipos correspondientes en Java). El tipo String de UML corresponde al tipo String de Java. El método mostrarMen- saje de LibroCalificaciones (figura 3.4) tiene un parámetro String llamado nombreDelCurso, por lo que en la figura 3.6 se lista a nombreDelCurso : String entre los paréntesis que van después de mostrarMensaje. Figura 3.6 | Diagrama de clases de UML, que indica que la clase LibroCalificaciones tiene una operación llamada mostrarMensaje, con un parámetro llamado nombreDelCurso de tipo String de UML. LibroCalificaciones + mostrarMensaje( nombreDelCurso : String ) 3.4 Declaración de un método con un parámetro 83
  • 122. 84 Capítulo 3 Introducción a las clases y los objetos Observaciones acerca del uso de las declaraciones import Observe la declaración import en la figura 3.5 (línea 4). Esto indica al compilador que el programa utiliza la clase Scanner. ¿Por qué necesitamos importar la clase Scanner, pero no las clases System, String o LibroCalifica- ciones? La mayoría de las clases que utilizará en los programas de Java deben importarse. Las clases System y String están en el paquete java.lang, que se importa de manera implícita en todo programa de Java, por lo que todos los programas pueden usar las clases del paquete java.lang sin tener que importarlas de manera explícita. Hay una relación especial entre las clases que se compilan en el mismo directorio en el disco, como las cla- ses LibroCalificaciones y PruebaLibroCalificaciones. De manera predeterminada, se considera que dichas clases se encuentran en el mismo paquete; a éste se le conoce como el paquete predeterminado. Las clases en el mismo paquete se importan implícitamente en los archivos de código fuente de las otras clases en el mismo paquete. Por ende, no se requiere una declaración import cuando la clase en un paquete utiliza a otra en el mis- mo paquete; como cuando PruebaLibroCalificaciones utiliza a la clase LibroCalificaciones. La declaración import en la línea 4 no es obligatoria si siempre hacemos referencia a la clase Scanner como java.util.Scanner, que incluye el nombre completo del paquete y de la clase. Esto se conoce como el nombre de clase completamente calificado. Por ejemplo, la línea 12 podría escribirse como java.util.Scanner entrada = new java.util.Scanner( System.in ); Observación de ingeniería de software 3.2 El compilador de Java no requiere declaraciones import en un archivo de código fuente de Java, si se especifica el nombre de clase completamente calificado cada vez que se utilice el nombre de una clase en el código fuente. Pero la mayoría de los programadores de Java consideran que el uso de nombres completamente calificados es incómodo, por lo cual prefieren usar declaraciones import. 3.5 Variables de instancia, métodos establecer y métodos obtener En el capítulo 2 declaramos todas las variables de una aplicación en el método main. Las variables que se declaran en el cuerpo de un método específico se conocen como variables locales, y sólo se pueden utilizar en ese método. Cuando termina ese método, se pierden los valores de sus variables locales. En la sección 3.2 vimos que un objeto tiene atributos que lleva consigo cuando se utiliza en un programa. Dichos atributos existen antes de que un objeto llame a un método, y después de que el método completa su ejecución. Por lo general, una clase consiste en uno o más métodos que manipulan los atributos pertenecientes a un objeto específico de la clase. Los atributos se representan como variables en la declaración de la clase. Dichas variables se llaman campos, y se declaran dentro de la declaración de una clase, pero fuera de los cuerpos de las declaraciones de los métodos de la clase. Cuando cada objeto de una clase mantiene su propia copia de un atribu- to, el campo que representa a ese atributo se conoce también como variable de instancia; cada objeto (instancia) de la clase tiene una instancia separada de la variable en memoria. El ejemplo en esta sección demuestra una clase LibroCalificaciones, que contiene una variable de instancia llamada nombreDelCurso para representar el nombre del curso de un objeto LibroCalificaciones específico. La clase LibroCalificaciones con una variable de instancia, un método establecer y un método obtener En nuestra siguiente aplicación (figuras 3.7 y 3.8), la clase LibroCalificaciones (figura 3.7) mantiene el nom- bre del curso como una variable de instancia, para que pueda usarse o modificarse en cualquier momento, durante la ejecución de una aplicación. Esta clase contiene tres métodos: establecerNombreDelCurso, obtenerNom- breDelCurso y mostrarMensaje. El método establecerNombreDelCurso almacena el nombre de un curso en un LibroCalificaciones. El método obtenerNombreDelCurso obtiene el nombre del curso de un LibroCali- ficaciones. El método mostrarMensaje, que en este caso no especifica parámetros, sigue mostrando un mensaje de bienvenida que incluye el nombre del curso; como veremos más adelante, el método ahora obtiene el nombre del curso mediante una llamada a otro método en la misma clase: obtenerNombreDelCurso. Por lo general, un instructor enseña más de un curso, cada uno con su propio nombre. La línea 7 declara que nombreDelCurso es una variable de tipo String. Como la variable se declara en el cuerpo de la clase, pero fuera de los cuerpos de los métodos de la misma (líneas 10 a la 13, 16 a la 19 y 22 a la 28), la línea 7 es una declaración para una variable de instancia. Cada instancia (es decir, objeto) de la clase LibroCalificaciones contiene una
  • 123. copia de cada variable de instancia. Por ejemplo, si hay dos objetos LibroCalificaciones, cada objeto tiene su propia copia de nombreDelCurso (una por cada objeto). Un beneficio de hacer de nombreDelCurso una variable de instancia es que todos los métodos de la clase (en este caso, LibroCalificaciones) pueden manipular cual- quier variable de instancia que aparezca en la clase (en este caso, nombreDelCurso). Los modificadores de acceso public y private La mayoría de las declaraciones de variables de instancia van precedidas por la palabra clave private (como en la línea 7). Al igual que public, la palabra clave private es un modificador de acceso. Las variables o los méto- dos declarados con el modificador de acceso private son accesibles sólo para los métodos de la clase en la que se declaran. Así, la variable nombreDelCurso sólo puede utilizarse en los métodos establecerNombreDelCurso, obtenerNombreDelCurso y mostrarMensaje de (cada objeto de) la clase LibroCalificaciones. Observación de ingeniería de software 3.3 Es necesario colocar un modificador de acceso antes de cada declaración de un campo y de un método. Como regla empí- rica, las variables de instancia deben declararse como private y los métodos, como public. (Más adelante veremos que es apropiado declarar ciertos métodos como private, si sólo van a estar accesibles por otros métodos de la clase). Buena práctica de programación 3.1 Preferimos listar los campos de una clase primero, para que, a medida que usted lea el código, pueda ver los nom- bres y tipos de las variables antes de ver su uso en los métodos de la clase. Es posible listar los campos de la clase en cualquier parte de la misma, fuera de las declaraciones de sus métodos, pero si se esparcen por todo el código, éste será más difícil de leer. 1 // Fig. 3.7: LibroCalificaciones.java 2 // Clase LibroCalificaciones que contiene una variable de instancia nombreDelCurso 3 // y métodos para establecer y obtener su valor. 4 5 public class LibroCalificaciones 6 { 7 private String nombreDelCurso; // nombre del curso para este LibroCalificaciones 8 9 // método para establecer el nombre del curso 10 public void establecerNombreDelCurso( String nombre ) 11 { 12 nombreDelCurso = nombre; // almacena el nombre del curso 13 } // fin del método establecerNombreDelCurso 14 15 // método para obtener el nombre del curso 16 public String obtenerNombreDelCurso() 17 { 18 return nombreDelCurso; 19 } // fin del método obtenerNombreDelCurso 20 21 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones 22 public void mostrarMensaje() 23 { 24 // esta instrucción llama a obtenerNombreDelCurso para obtener el 25 // nombre del curso que representa este LibroCalificaciones 26 System.out.printf( “Bienvenido al libro de calificaciones paran%s!n”, 27 obtenerNombreDelCurso() ); 28 } // fin del método mostrarMensaje 29 30 } // fin de la clase LibroCalificaciones Figura 3.7 | Clase LibroCalificaciones que contiene una variable de instancia nombreDelCurso y métodos para establecer y obtener su valor. 3.5 Variables de instancia, métodos establecer y métodos obtener 85
  • 124. 86 Capítulo 3 Introducción a las clases y los objetos Buena práctica de programación 3.2 Coloque una línea en blanco entre las declaraciones de los métodos, para separarlos y mejorar la legibilidad del programa. El proceso de declarar variables de instancia con el modificador de acceso private se conoce como oculta- miento de datos. Cuando un programa crea (instancia) un objeto de la clase LibroCalificaciones, la variable nombreDelCurso se encapsula (oculta) en el objeto, y sólo está accesible para los métodos de la clase de ese objeto. En la clase LibroCalificaciones, los métodos establecerNombreDelCurso y obtenerNombreDelCurso mani- pulan a la variable de instancia nombreDelCurso. El método establecerNombreDelCurso (líneas 10 a la 13) no devuelve datos cuando completa su tarea, por lo que su tipo de valor de retorno es void. El método recibe un parámetro (nombre), el cual representa el nom- bre del curso que se pasará al método como un argumento. La línea 12 asigna nombre a la variable de instancia nombreDelCurso. El método obtenerNombreDelCurso (líneas 16 a la 19) devuelve un nombreDelCurso de un objeto Libro- Calificaciones específico. El método tiene una lista de parámetros vacía, por lo que no requiere información adicional para realizar su tarea. El método especifica que devuelve un objeto String; a éste se le conoce como el tipo de valor de retorno del método. Cuando se hace una llamada a un método que especifica un tipo de valor de retorno, y éste completa su tarea, devuelve un resultado al método que lo llamó. Por ejemplo, cuando usted va a un cajero automático (ATM) y solicita el saldo de su cuenta, espera que el ATM le devuelva un valor que representa su saldo. De manera similar, cuando una instrucción llama al método obtenerNombreDelCurso en un objeto LibroCalificaciones, la instrucción espera recibir el nombre del curso de LibroCalificaciones (en este caso, un objeto String, como se especifica en el tipo de valor de retorno de la declaración del método). Si tiene un método cuadrado que devuelve el cuadrado de su argumento, es de esperarse que la instrucción int resultado = cuadrado( 2 ); devuelva 4 del método cuadrado y asigne 4 a la variable resultado. Si tiene un método maximo que devuelve el mayor de tres argumentos enteros, es de esperarse que la siguiente instrucción int mayor = maximo( 27, 114, 51 ); devuelva 114 del método maximo y asigne 114 a la variable mayor. Observe que cada una de las instrucciones en las líneas 12 y 18 utilizan nombreDelCurso, aun cuando esta variable no se declaró en ninguno de los métodos. Podemos utilizar nombreDelCurso en los métodos de la clase LibroCalificaciones, ya que nombreDelCurso es un campo de la clase. Observe además que el orden en el que se declaran los métodos en una clase no determina cuándo se van a llamar en tiempo de ejecución. Por lo tanto, el método obtenerNombreDelCurso podría declararse antes del método establecerNombreDelCurso. El método mostrarMensaje (líneas 22 a la 28) no devuelve datos cuando completa su tarea, por lo que su tipo de valor de retorno es void. El método no recibe parámetros, por lo que la lista de parámetros está vacía. Las líneas 26 y 27 imprimen un mensaje de bienvenida, que incluye el valor de la variable de instancia nombreDel- Curso. Una vez más, necesitamos crear un objeto de la clase LibroCalificaciones y llamar a sus métodos para poder mostrar en pantalla el mensaje de bienvenida. La clase PruebaLibroCalificaciones que demuestra a la clase LibroCalificaciones La clase PruebaLibroCalificaciones (figura 3.8) crea un objeto de la clase LibroCalificaciones y demuestra el uso de sus métodos. La línea 11 crea un objeto Scanner, que se utilizará para obtener el nombre de un curso del usuario. La línea 14 crea un objeto LibroCalificaciones y lo asigna a la variable local miLibroCalificacio- nes, de tipo LibroCalificaciones. Las líneas 17-18 muestran el nombre inicial del curso mediante una llamada al método obtenerNombreDelCurso del objeto. Observe que la primera línea de la salida muestra el nombre “null”. A diferencia de las variables locales, que no se inicializan de manera automática, cada campo tiene un valor inicial predeterminado: un valor que Java proporciona cuando el programador no especifica el valor inicial del campo. Por ende, no se requiere que los campos se inicialicen explícitamente antes de usarlos en un programa, a menos que deban inicializarse con valores distintos de los predeterminados. El valor predeterminado para un campo de tipo String (como nombreDelCurso en este ejemplo) es null, de lo cual hablaremos con más detalle en la sección 3.6.
  • 125. La línea 21 pide al usuario que escriba el nombre para el curso. La variable String local elNombre (decla- rada en la línea 22) se inicializa con el nombre del curso que escribió el usuario, el cual se devuelve mediante la llamada al método nextLine del objeto Scanner llamado entrada. La línea 23 llama al método estable- cerNombreDelCurso del objeto miLibroCalificaciones y provee elNombre como argumento para el método. Cuando se hace la llamada al método, el valor del argumento se asigna al parámetro nombre (línea 10, figura 3.7) del método establecerNombreDelCurso (líneas 10 a la 13, figura 3.7). Después, el valor del parámetro se asigna a la variable de instancia nombreDelCurso (línea 12, figura 3.7). La línea 24 (figura 3.8) salta una línea en la salida, y después la línea 27 llama al método mostrarMensaje del objeto miLibroCalificaciones para mostrar en pantalla el mensaje de bienvenida, que contiene el nombre del curso. Los métodos establecer y obtener Los campos private de una clase pueden manipularse sólo mediante los métodos de esa clase. Por lo tanto, un cliente de un objeto (es decir, cualquier clase que llame a los métodos del objeto) llama a los métodos public de la clase para manipular los campos private de un objeto de esa clase. Esto explica por qué las instrucciones en el método main (figura 3.8) llaman a los métodos establecerNombreDelCurso, obtenerNombreDelCurso y mostrarMensaje en un objeto LibroCalificaciones. A menudo, las clases proporcionan métodos public para permitir a los clientes de la clase establecer (es decir, asignar valores a) u obtener (es decir, obtener los valores de) variables de instancia private. Los nombres de estos métodos no necesitan empezar con establecer u obtener, pero esta convención de nomenclatura es muy recomendada en Java, y es requerida para ciertos componentes de software especiales de Java, conocidos como JavaBeans, que pueden simplificar la programación en muchos entornos de desarrollo integrados (IDEs). El método que establece la variable nombreDelCurso en este ejemplo se llama establecerNombreDelCurso, y el método que obtiene el valor de la variable de instancia nombreDelCurso se llama obtenerNombreDelCurso. Figura 3.8 | Creación y manipulación de un objeto LibroCalificaciones. (Parte 1 de 2). 1 // Fig. 3.8: PruebaLibroCalificaciones.java 2 // Crea y manipula un objeto LibroCalificaciones. 3 import java.util.Scanner; // el programa usa la clase Scanner 4 5 public class PruebaLibroCalificaciones 6 { 7 // el método main empieza la ejecución del programa 8 public static void main( String args[] ) 9 { 10 // crea un objeto Scanner para obtener la entrada de la ventana de comandos 11 Scanner entrada = new Scanner( System.in ); 12 13 // crea un objeto LibroCalificaciones y lo asigna a miLibroCalificaciones 14 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones(); 15 16 // muestra el valor inicial de nombreDelCurso 17 System.out.printf( “El nombre inicial del curso es: %snn”, 18 miLibroCalificaciones.obtenerNombreDelCurso() ); 19 20 // pide y lee el nombre del curso 21 System.out.println( “Escriba el nombre del curso:” ); 22 String elNombre = entrada.nextLine(); // lee una línea de texto 23 miLibroCalificaciones.establecerNombreDelCurso( elNombre ); // establece el nombre del curso 24 System.out.println(); // imprime una línea en blanco 25 26 // muestra el mensaje de bienvenida después de especificar el nombre del curso 27 miLibroCalificaciones.mostrarMensaje(); 28 } // fin de main 29 30 } // fin de la clase PruebaLibroCalificaciones 3.5 Variables de instancia, métodos establecer y métodos obtener 87
  • 126. 88 Capítulo 3 Introducción a las clases y los objetos Diagrama de clases de UML para la clase LibroCalificaciones con una variable de instancia, y métodos establecer y obtener La figura 3.9 contiene un diagrama de clases de UML actualizado para la versión de la clase LibroCalifica- ciones de la figura 3.7. Este diagrama modela la variable de instancia nombreDelCurso de la clase Libro- Calificaciones como un atributo en el compartimiento intermedio de la clase. UML representa a las variables de instancia como atributos, listando el nombre del atributo, seguido de dos puntos y del tipo del atributo. El tipo de UML del atributo nombreDelCurso es String. La variable de instancia nombreDelCurso es private en Java, por lo que el diagrama de clases lista un signo menos (-) en frente del nombre del atributo correspondiente. La clase LibroCalificaciones contiene tres métodos public, por lo que el diagrama de clases lista tres operaciones en el tercer compartimiento. Recuerde que el signo más (+) antes de cada nombre de operación indica que ésta es public. La operación establecerNombreDelCurso tiene un parámetro String llamado nombre. UML indica el tipo de valor de retorno de una operación colocando dos puntos y el tipo de valor de retorno después de los paréntesis que le siguen al nombre de la operación. El método obtenerNombreDelCurso de la clase LibroCali- ficaciones (figura 3.7) tiene un tipo de valor de retorno String en Java, por lo que el diagrama de clases muestra un tipo de valor de retorno String en UML. Observe que las operaciones establecerNombreDelCurso y mos- trarMensaje no devuelven valores (es decir, devuelven void en Java), por lo que el diagrama de clases de UML no especifica un tipo de valor de retorno después de los paréntesis de estas operaciones. El nombre inicial del curso es: null Escriba el nombre del curso: CS101 Introduccion a la programacion en Java Bienvenido al libro de calificaciones para CS101 Introduccion a la programacion en Java! Figura 3.8 | Creación y manipulación de un objeto LibroCalificaciones. (Parte 2 de 2). Figura 3.9 | Diagrama de clases de UML, en el que se indica que la clase LibroCalificaciones tiene un atributo nombreDelCurso de tipo String en UML, y tres operaciones: establecerNombreDelCurso (con un pará- metro nombre de tipo String de UML), obtenerNombreDelCurso (que devuelve el tipo String de UML) y mostrarMensaje. LibroCalificaciones – nombreDelCurso : String + establecerNombreDelCurso( nombre : String ) + obtenerNombreDelCurso( ) : String + mostrarMensaje( ) 3.6 Comparación entre tipos primitivos y tipos por referencia Los tipos de datos en Java se dividen en dos categorías: tipos primitivos y tipos por referencia (algunas veces conocidos como tipos no primitivos). Los tipos primitivos son boolean, byte, char, short, int, long, float y double. Todos los tipos no primitivos son tipos por referencia, por lo cual las clases, que especifican los tipos de objetos, son tipos por referencia. Una variable de tipo primitivo puede almacenar sólo un valor de su tipo declarado a la vez. Por ejemplo, una variable int puede almacenar un número completo (como 7) a la vez. Cuando se asigna otro valor a esa variable, se sustituye su valor inicial. Las variables de instancia de tipo primitivo se inicializan de manera predeterminada; las variables de los tipos byte, char, short, int, long, float y double se inicializan con 0, y las variables de tipo boolean se inicializan con false. Usted puede especificar sus propios valores iniciales para las variables de tipo primitivo. Recuerde que las variables locales no se inicializan de manera predeterminada.
  • 127. Tip para prevenir errores 3.1 Cualquier intento de utilizar una variable local que no se haya inicializado produce un error de compilación. Los programas utilizan variables de tipo por referencia (que por lo general se llaman referencias) para alma- cenar las ubicaciones de los objetos en la memoria de la computadora. Se dice que dicha variable hace referencia a un objeto en el programa. Cada uno de los objetos a los que se hace referencia pueden contener muchas variables de instancia y métodos. La línea 14 de la figura 3.8 crea un objeto de la clase LibroCalificaciones, y la variable miLibroCalificaciones contiene una referencia a ese objeto LibroCalificaciones. Las variables de instancia de tipo por referencia se inicializan de manera predeterminada con el valor null: una palabra reservada que repre- senta una “referencia a nada”. Esto explica por qué la primera llamada a obtenerNombreDelCurso en la línea 18 de la figura 3.8 devolvía null; no se había establecido el valor de nombreDelCurso, por lo que se devolvía el valor inicial predeterminado null. En el apéndice C, Palabras clave y palabras reservadas, se muestra una lista completa de las palabras reservadas y las palabras clave. Es obligatorio que una referencia a un objeto invoque (es decir, llame) a los métodos de un objeto. En la aplicación de la figura 3.8, las instrucciones en el método main utilizan la variable miLibroCalificaciones para enviar mensajes al objeto LibroCalificaciones. Estos mensajes son llamadas a métodos (como establecer- NombreDelCurso y obtenerNombreDelCurso) que permiten al programa interactuar con el objeto LibroCali- ficaciones. Por ejemplo, la instrucción en la línea 23 utiliza a miLibroCalificaciones para enviar el mensaje establecerNombreDelCurso al objeto LibroCalificaciones. El mensaje incluye el argumento que requiere es- tablecerNombreDelCurso para realizar su tarea. El objeto LibroCalificaciones utiliza esta información para establecer la variable de instancia nombreDelCurso. Tenga en cuenta que las variables de tipo primitivo no hacen referencias a objetos, por lo que dichas variables no pueden utilizarse para invocar métodos. Observación de ingeniería de software 3.4 El tipo declarado de una variable (por ejemplo, int, double o LibroCalificaciones) indica si la variable es de tipo primitivo o por referencia. Si el tipo de una variable no es uno de los ocho tipos primitivos, entonces es un tipo por referencia. (Por ejemplo, Cuenta cuenta1 indica que cuenta1 es una referencia a un objeto Cuenta). 3.7 Inicialización de objetos mediante constructores Como mencionamos en la sección 3.5, cuando se crea un objeto de la clase LibroCalificaciones (figura 3.7), su variable de instancia nombreCurso se inicializa con null de manera predeterminada. ¿Qué pasa si usted desea proporcionar el nombre de un curso a la hora de crear un objeto LibroCalificaciones? Cada clase que usted declare puede proporcionar un constructor, el cual puede utilizarse para inicializar un objeto de una clase al momento de crear ese objeto. De hecho, Java requiere una llamada al constructor para cada objeto que se crea. La palabra clave new llama al constructor de la clase para realizar la inicialización. La llamada al constructor se indica mediante el nombre de la clase, seguido de paréntesis; el constructor debe tener el mismo nombre que la clase. Por ejemplo, la línea 14 de la figura 3.8 primero utiliza new para crear un objeto LibroCalificaciones. Los paréntesis vacíos después de "new LibroCalificaciones" indican una llamada sin argumentos al constructor de la clase. De manera predeterminada, el compilador proporciona un constructor predeterminado sin parámetros, en cualquier clase que no incluya un constructor en forma explícita. Cuando una clase sólo tiene el constructor predeterminado, sus variables de instancia se inicializan con sus valores predeterminados. Las variables de los tipos char, byte, short, int, long, float y double se inicializan con 0, las variables de tipo boolean se inicializan con false, y las variables de tipo por referencia se inicializan con null. Cuando usted declara una clase, puede proporcionar su propio constructor para especificar una inicialización personalizada para los objetos de su clase. Por ejemplo, tal vez un programador quiera especificar el nombre de un curso para un objeto LibroCalificaciones cuando se crea este objeto, como en LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones( "CS101 Introduccion a la programacion en Java" ); En este caso, el argumento "CS101 Introduccion a la programacion en Java" se pasa al constructor del objeto LibroCalificaciones y se utiliza para inicializar el nombreDelCurso. La instrucción anterior requiere que la clase proporcione un constructor con un parámetro String. La figura 3.10 contiene una clase LibroCalifi- caciones modificada con dicho constructor. 3.7 Inicialización de objetos mediante constructores 89
  • 128. 90 Capítulo 3 Introducción a las clases y los objetos Las líneas 9 a la 12 declaran el constructor para la clase LibroCalificaciones. Un constructor debe tener el mismo nombre que su clase. Al igual que un método, un constructor especifica en su lista de parámetros los da- tos que requiere para realizar su tarea. Cuando usted crea un nuevo objeto (como haremos en la figura 3.11), estos datos se colocan en los paréntesis que van después del nombre de la clase. La línea 9 indica que el constructor de la clase LibroCalificaciones tiene un parámetro String llamado nombre. El nombre que se pasa al constructor se asigna a la variable de instancia nombreCurso en la línea 11 del cuerpo del constructor. La figura 3.11 demuestra la inicialización de objetos LibroCalificaciones mediante el uso de este cons- tructor. Las líneas 11 y 12 crean e inicializan el objeto libroCalificaciones1 de LibroCalificaciones. El constructor de la clase LibroCalificaciones se llama con el argumento "CS101 Introduccion a la pro- gramacion en Java" para inicializar el nombre del curso. La expresión de creación de la instancia de la clase a la derecha del signo = en las líneas 11 y 12 devuelve una referencia al nuevo objeto, el cual se asigna a la variable libroCalificaciones1. Las líneas 13 y 14 repiten este proceso para otro objeto LibroCalificaciones llama- do libroCalificaciones2, pero esta vez se le pasa el argumento "CS102 Estructuras de datos en Java" para inicializar el nombre del curso para libroCalificaciones2. Las líneas 17 a la 20 utilizan el método obtenerNom- breDelCurso de cada objeto para obtener los nombres de los cursos y mostrar que, sin duda, se inicializaron en el momento en el que se crearon los objetos. En la introducción a la sección 3.5, aprendió que cada instancia (es decir, objeto) de una clase contiene su propia copia de las variables de instancia de la clase. La salida confirma que cada objeto LibroCalificaciones mantiene su propia copia de la variable de instancia nombreCurso. Figura 3.10 | La clase LibroCalificaciones con un constructor para inicializar el nombre del curso. 1 // Fig. 3.10: LibroCalificaciones.java 2 // La clase LibroCalificaciones con un constructor para inicializar el nombre del curso. 3 4 public class LibroCalificaciones 5 { 6 private String nombreDelCurso; // nombre del curso para este LibroCalificaciones 7 8 // el constructor inicializa nombreDelCurso con el objeto String que se provee como argumento 9 public LibroCalificaciones( String nombre ) 10 { 11 nombreDelCurso = nombre; // inicializa nombreDelCurso 12 } // fin del constructor 13 14 // método para establecer el nombre del curso 15 public void establecerNombreDelCurso( String nombre ) 16 { 17 nombreDelCurso = nombre; // almacena el nombre del curso 18 } // fin del método establecerNombreDelCurso 19 20 // método para obtener el nombre del curso 21 public String obtenerNombreDelCurso() 22 { 23 return nombreDelCurso; 24 } // fin del método obtenerNombreDelCurso 25 26 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones 27 public void mostrarMensaje() 28 { 29 // esta instrucción llama a obtenerNombreDelCurso para obtener el 30 // nombre del curso que este LibroCalificaciones representa 31 System.out.printf( “Bienvenido al Libro de calificaciones paran%s!n”, 32 obtenerNombreDelCurso() ); 33 } // fin del método mostrarMensaje 34 35 } // fin de la clase LibroCalificaciones
  • 129. Al igual que los métodos, los constructores también pueden recibir argumentos. No obstante, una importan- te diferencia entre los constructores y los métodos es que los constructores no pueden devolver valores, por lo cual no pueden especificar un tipo de valor de retorno (ni siquiera void). Por lo general, los constructores se declaran como public. Si una clase no incluye un constructor, las variables de instancia de esa clase se inicializan con sus valores predeterminados. Si un programador declara uno o más constructores para una clase, el compilador de Java no creará un constructor predeterminado para esa clase. Tip para prevenir errores 3.2 A menos que sea aceptable la inicialización predeterminada de las variables de instancia de su clase, deberá propor- cionar un constructor para asegurarse que las variables de instancia de su clase se inicialicen en forma apropiada con valores significativos, a la hora de crear cada nuevo objeto de su clase. Agregar el constructor al diagrama de clases de UML de la clase LibroCalificaciones El diagrama de clases de UML de la figura 3.12 modela la clase LibroCalificaciones de la figura 3.10, la cual tiene un constructor con un parámetro llamado nombre, de tipo String. Al igual que las operaciones, en un diagrama de clases, UML modela a los constructores en el tercer compartimiento de una clase. Para diferenciar a un constructor de las operaciones de una clase, UML requiere que se coloque la palabra “constructor” entre los signos « y » antes del nombre del constructor. Es costumbre listar los constructores antes de otras operaciones en el tercer compartimiento. 3.8 Números de punto flotante y el tipo double En nuestra siguiente aplicación, dejaremos por un momento nuestro ejemplo práctico con la clase LibroCalifi- caciones para declarar una clase llamada Cuenta, la cual mantiene el saldo de una cuenta bancaria. La mayoría de los saldos de las cuentas no son números enteros (por ejemplo, 0, –22 y 1024). Por esta razón, la clase Cuenta Figura 3.11 | El constructor de LibroCalificaciones se utiliza para especificar el nombre del curso cada vez que se crea un objeto LibroCalificaciones. 1 // Fig. 3.11: PruebaLibroCalificaciones.java 2 // El constructor de LibroCalificaciones se utiliza para especificar el 3 // nombre del curso cada vez que se crea cada objeto LibroCalificaciones. 4 5 public class PruebaLibroCalificaciones 6 { 7 // el método main empieza la ejecución del programa 8 public static void main( String args[] ) 9 { 10 // crea objeto LibroCalificaciones 11 LibroCalificaciones libroCalificaciones1 = new LibroCalificaciones( 12 “CS101 Introduccion a la programacion en Java” ); 13 LibroCalificaciones libroCalificaciones2 = new LibroCalificaciones( 14 “CS102 Estructuras de datos en Java” ); 15 16 // muestra el valor inicial de nombreDelCurso para cada LibroCalificaciones 17 System.out.printf( “El nombre del curso de libroCalificaciones1 es: %sn”, 18 libroCalificaciones1.obtenerNombreDelCurso() ); 19 System.out.printf( “El nombre del curso de libroCalificaciones2 es: %sn”, 20 libroCalificaciones2.obtenerNombreDelCurso() ); 21 } // fin de main 22 23 } // fin de la clase PruebaLibroCalificaciones El nombre del curso de libroCalificaciones1 es: CS101 Introduccion a la programacion en Java El nombre del curso de libroCalificaciones2 es: CS102 Estructuras de datos en Java 3.8 Números de punto flotante y el tipo double 91
  • 130. 92 Capítulo 3 Introducción a las clases y los objetos representa el saldo de las cuentas como un número de punto flotante (es decir, un número con un punto deci- mal, como 7.33, 0.0975 o 1000.12345). Java cuenta con dos tipos primitivos para almacenar números de punto flotante en la memoria: float y double. La principal diferencia entre ellos es que las variables tipo double pueden almacenar números con mayor magnitud y detalle (es decir, más dígitos a la derecha del punto decimal; lo que también se conoce como precisión del número) que las variables float. Precisión de los números de punto flotante y requerimientos de memoria Las variables de tipo float representan números de punto flotante de precisión simple y tienen siete dígitos significativos. Las variables de tipo double representan números de punto flotante de precisión doble. Éstos requieren el doble de memoria que las variables float y proporcionan 15 dígitos significativos; aproximadamente el doble de precisión de las variables float. Para el rango de valores requeridos por la mayoría de los programas, debe bastar con las variables de tipo float, pero podemos utilizar variables tipo double para “ir a la segura”. En algunas aplicaciones, incluso hasta las variables de tipo double serán inadecuadas; dichas aplicaciones se encuen- tran más allá del alcance de este libro. La mayoría de los programadores representan los números de punto flotante con el tipo double. De hecho, Java trata a todos los números de punto flotante que escribimos en el código fuente de un programa (como 7.33 y 0.0975) como valores double de manera predeterminada. Dichos valores en el código fuente se conocen como literales de punto flotante. En el apéndice D, Tipos primitivos, podrá consultar los rangos de los valores para los tipos float y double. Aunque los números de punto flotante no son siempre 100% precisos, tienen numerosas aplicaciones. Por ejemplo, cuando hablamos de una temperatura corporal “normal” de 36.8, no necesitamos una precisión con un número extenso de dígitos. Cuando leemos la temperatura en un termómetro como 36.8, en realidad podría ser 36.7999473210643. Si consideramos a este número simplemente como 36.8, está bien para la mayoría de las apli- caciones en las que se trabaja con las temperaturas corporales. Debido a la naturaleza imprecisa de los números de punto flotante, se prefiere el tipo double al tipo float ya que las variables double pueden representar números de punto flotante con más precisión. Por esta razón, utilizaremos el tipo double a lo largo de este libro. Los números de punto flotante también surgen como resultado de la división. En la aritmética convencional, cuando dividimos 10 entre 3 el resultado es 3.3333333…, y la secuencia de números 3 se repite en forma inde- finida. La computadora asigna sólo una cantidad fija de espacio para almacenar un valor de este tipo, por lo que, sin duda, el valor de punto flotante almacenado sólo puede ser una aproximación. Error común de programación 3.4 El uso de números de punto flotante en una forma en la que se asuma que se representan con precisión puede producir errores lógicos. La clase Cuenta con una variable de instancia de tipo double Nuestra siguiente aplicación (figuras 3.13 y 3.14) contiene una clase llamada Cuenta (figura 3.13), la cual man- tiene el saldo de una cuenta bancaria. Un banco ordinario da servicio a muchas cuentas, cada una con su propio saldo, por lo que la línea 7 declara una variable de instancia, de tipo double, llamada saldo . La variable saldo es una variable de instancia, ya que está declarada en el cuerpo de la clase pero fuera de las declaraciones de los métodos de la misma (líneas 10 a la 16, 19 a la 22 y 25 a la 28). Cada instancia (es decir, objeto) de la clase Cuenta contiene su propia copia de saldo. Figura 3.12 | Diagrama de clases de UML, en el cual se indica que la clase LibroCalificaciones tiene un constructor con un parámetro nombre del tipo String de UML. LibroCalificaciones – nombreDelCurso : String «constructor» LibroCalificaciones( nombre : String ) + establecerNombreDelCurso( nombre : String ) + obtenerNombreDelCurso( ) : String + mostrarMensaje( )
  • 131. La clase Cuenta contiene un constructor y dos métodos. Debido a que es común que alguien abra una cuenta para depositar dinero de inmediato, el constructor (líneas 10 a la 16) recibe un parámetro llamado saldoInicial de tipo double, el cual representa el saldo inicial de la cuenta. Las líneas 14 y 15 aseguran que saldoInicial sea mayor que 0.0. De ser así, el valor de saldoInicial se asigna a la variable de instancia saldo. En caso contrario, saldo permanece en 0.0, su valor inicial predeterminado. El método abonar (líneas 19 a la 22) no devuelve datos cuando completa su tarea, por lo que su tipo de valor de retorno es void. El método recibe un parámetro llamado monto: un valor double que se sumará al saldo. La línea 21 suma monto al valor actual de saldo, y después asigna el resultado a saldo (con lo cual se sustituye el monto del saldo anterior). El método obtenerSaldo (líneas 25 a la 28) permite a los clientes de la clase (es decir, otras clases que utili- cen esta clase) obtener el valor del saldo de un objeto Cuenta específico. El método especifica el tipo de valor de retorno double y una lista de parámetros vacía. Observe una vez más que las instrucciones en las líneas 15, 21 y 27 utilizan la variable de instancia saldo, aun y cuando no se declaró en ninguno de los métodos. Podemos usar saldo en estos métodos, ya que es una variable de instancia de la clase. La clase PruebaCuenta que utiliza a la clase Cuenta La clase PruebaCuenta (figura 3.14) crea dos objetos Cuenta (líneas 10 y 11) y los inicializa con 50.00 y -7.53, respectivamente. Las líneas 14 a la 17 imprimen el saldo en cada objeto Cuenta mediante una llamada al método obtenerSaldo de Cuenta. Cuando se hace una llamada al método obtenerSaldo para cuenta1 en la línea 15, se devuelve el valor del saldo de cuenta1 de la línea 27 en la figura 3.13, y se imprime en pantalla mediante la instrucción System.out.printf (figura 3.14, líneas 14 y 15). De manera similar, cuando se hace la llamada al método obtenerSaldo para cuenta2 en la línea 17, se devuelve el valor del saldo de cuenta2 de la línea 27 en la Figura 3.13 | La clase Cuenta con una variable de instancia de tipo double. 1 // Fig. 3.13: Cuenta.java 2 // La clase Cuenta con un constructor para 3 // inicializar la variable de instancia saldo. 4 5 public class Cuenta 6 { 7 private double saldo; // variable de instancia que almacena el saldo 8 9 // constructor 10 public Cuenta( double saldoInicial ) 11 { 12 // valida que saldoInicial sea mayor que 0.0; 13 // si no lo es, saldo se inicializa con el valor predeterminado 0.0 14 if ( saldoInicial > 0.0 ) 15 saldo = saldoInicial; 16 } // fin del constructor de Cuenta 17 18 // abona (suma) un monto a la cuenta 19 public void abonar( double monto ) 20 { 21 saldo = saldo + monto; // suma el monto al saldo 22 } // fin del método abonar 23 24 // devuelve el saldo de la cuenta 25 public double obtenerSaldo() 26 { 27 return saldo; // proporciona el valor de saldo al método que hizo la llamada 28 } // fin del método obtenerSaldo 29 30 } // fin de la clase Cuenta 3.8 Números de punto flotante y el tipo double 93
  • 132. 94 Capítulo 3 Introducción a las clases y los objetos figura 3.13, y se imprime en pantalla mediante la instrucción System.out.printf (figura 3.14, líneas 16 y 17). Observe que el saldo de cuenta2 es 0.00, ya que el constructor se aseguró de que la cuenta no pudiera empezar con un saldo negativo. El valor se imprime en pantalla mediante printf, con el especificador de formato %.2f. El especificador de formato %f se utiliza para imprimir valores de tipo float o double. El .2 entre % y f repre- senta el número de lugares decimales (2) que deben imprimirse a la derecha del punto decimal en el número de punto flotante; a esto también se le conoce como la precisión del número. Cualquier valor de punto flotante que se imprima con %.2f se redondeará a la posición de las centenas; por ejemplo, 123.457 se redondearía a 123.46, y 27.333 se redondearía a 27.33. Figura 3.14 | Entrada y salida de números de punto flotante con objetos Cuenta. (Parte 1 de 2). 1 // Fig. 3.14: PruebaCuenta.java 2 // Entrada y salida de números de punto flotante con objetos Cuenta. 3 import java.util.Scanner; 4 5 public class PruebaCuenta 6 { 7 // el método main empieza la ejecución de la aplicación de Java 8 public static void main( String args[] ) 9 { 10 Cuenta cuenta1 = new Cuenta( 50.00 ); // crea objeto Cuenta 11 Cuenta cuenta2 = new Cuenta( -7.53 ); // crea objeto Cuenta 12 13 // muestra el saldo inicial de cada objeto 14 System.out.printf( “Saldo de cuenta1: $%.2fn”, 15 cuenta1.obtenerSaldo() ); 16 System.out.printf( “Saldo de cuenta2: $%.2fnn”, 17 cuenta2.obtenerSaldo() ); 18 19 // crea objeto Scanner para obtener la entrada de la ventana de comandos 20 Scanner entrada = new Scanner( System.in ); 21 double montoDeposito; // deposita el monto escrito por el usuario 22 23 System.out.print( “Escriba el monto a depositar para cuenta1: “ ); // indicador 24 montoDeposito = entrada.nextDouble(); // obtiene entrada del usuario 25 System.out.printf( “nsumando %.2f al saldo de cuenta1nn”, 26 montoDeposito ); 27 cuenta1.abonar( montoDeposito ); // suma al saldo de cuenta1 28 29 // muestra los saldos 30 System.out.printf( “Saldo de cuenta1: $%.2fn”, 31 cuenta1.obtenerSaldo() ); 32 System.out.printf( “Saldo de cuenta2: $%.2fnn”, 33 cuenta2.obtenerSaldo() ); 34 35 System.out.print( “Escriba el monto a depositar para cuenta2: “ ); // indicador 36 montoDeposito = entrada.nextDouble(); // obtiene entrada del usuario 37 System.out.printf( “nsumando %.2f al saldo de cuenta2nn”, 38 montoDeposito ); 39 cuenta2.abonar( montoDeposito ); // suma al saldo de cuenta2 40 41 // muestra los saldos 42 System.out.printf( “Saldo de cuenta1: $%.2fn”, 43 cuenta1.obtenerSaldo() ); 44 System.out.printf( “Saldo de cuenta2: $%.2fn”, 45 cuenta2.obtenerSaldo() ); 46 } // fin de main 47 48 } // fin de la clase PruebaCuenta
  • 133. La línea 20 crea un objeto Scanner, el cual se utilizará para obtener montos de depósito de un usuario. La línea 21 declara la variable local montoDeposito para almacenar cada monto de depósito introducido por el usua- rio. A diferencia de la variable de instancia saldo en la clase Cuenta, la variable local montoDeposito en main no se inicializa con 0.0 de manera predeterminada. Sin embargo, esta variable no necesita inicializarse aquí, ya que su valor se determinará con base a la entrada del usuario. La línea 23 pide al usuario que escriba un monto a depositar para cuenta1. La línea 24 obtiene la entrada del usuario, llamando al método nextDouble del objeto Scanner llamado entrada, el cual devuelve un valor double introducido por el usuario. Las líneas 25 y 26 muestran el monto del depósito. La línea 27 llama al méto- do abonar del objeto cuenta1 y le suministra montoDeposito como argumento. Cuando se hace la llamada al método, el valor del argumento se asigna al parámetro monto (línea 19 de la figura 3.13) del método abonar (líneas 19 a la 22 de la figura 3.13), y después el método abonar suma ese valor al saldo (línea 21 de la figura 3.13). Las líneas 30 a la 33 (figura 3.14) imprimen en pantalla los saldos de ambos objetos Cuenta otra vez, para mostrar que sólo se modificó el saldo de cuenta1. La línea 35 pide al usuario que escriba un monto a depositar para cuenta2. La línea 36 obtiene la entrada del usuario, llamando al método nextDouble del objeto Scanner llamado entrada. Las líneas 37 y 38 muestran el monto del depósito. La línea 39 llama al método abonar del objeto cuenta2 y le suministra montoDeposito como argumento; después, el método abonar suma ese valor al saldo. Por último, las líneas 42 a la 45 imprimen en pantalla los saldos de ambos objetos Cuenta otra vez, para mostrar que sólo se modificó el saldo de cuenta2. Diagrama de clases de UML para la clase Cuenta El diagrama de clases de UML en la figura 3.15 modela la clase Cuenta de la figura 3.13. El diagrama modela la propiedad private llamada saldo con el tipo Double de UML, para que corresponda a la variable de instancia saldo de la clase, que tiene el tipo double de Java. El diagrama modela el constructor de la clase Cuenta con un parámetro saldoInicial del tipo Double de UML en el tercer compartimiento de la clase. Los dos métodos public de la clase se modelan como operaciones en el tercer compartimiento también. El diagrama modela la operación abonar con un parámetro monto de tipo Double de UML (ya que el método correspondiente tiene un parámetro monto de tipo double en Java) y la operación obtenerSaldo con un tipo de valor de retorno Double (ya que el método correspondiente en Java devuelve un valor double). 3.9 (Opcional) Ejemplo práctico de GUI y gráficos: uso de cuadros de diálogo Este ejemplo práctico opcional está diseñado para aquellos quienes desean empezar a conocer las poderosas herra- mientas de Java para crear interfaces gráficas de usuario (GUIs) y gráficos antes de las principales discusiones de estos temas en el capítulo 11, Componentes de la GUI: parte 1, el capítulo 12, Gráficos y Java 2D™, y el capítulo 22, Componentes de la GUI: parte 2. Figura 3.14 | Entrada y salida de números de punto flotante con objetos Cuenta. (Parte 2 de 2). Saldo de cuenta1: $50.00 Saldo de cuenta2: $0.00 Escriba el monto a depositar para cuenta1: 25.53 sumando 25.53 al saldo de cuenta1 Saldo de cuenta1: $75.53 Saldo de cuenta2: $0.00 Escriba el monto a depositar para cuenta2: 123.45 sumando 123.45 al saldo de cuenta2 Saldo de cuenta1: $75.53 Saldo de cuenta2: $123.45 3.9 (Opcional) Ejemplo práctico de GUI y gráficos: uso de cuadros de diálogo 95
  • 134. 96 Capítulo 3 Introducción a las clases y los objetos El ejemplo práctico de GUI y gráficos aparece en 10 secciones breves (figura 3.16). Cada sección introduce unos cuantos conceptos básicos y proporciona ejemplos visuales y gráficos. En las primeras secciones, creará sus primeras aplicaciones gráficas; en las secciones posteriores, utilizará los conceptos de programación orientada a objetos que se presentan a lo largo del capítulo 10 para crear una aplicación de dibujo, la cual dibuja una varie- dad de figuras. Cuando presentemos formalmente a las GUIs en el capítulo 11, utilizaremos el ratón para elegir exactamente qué figuras dibujar y en dónde dibujarlas. En el capítulo 12, agregaremos las herramientas de la API de gráficos en 2D de Java para dibujar las figuras con distintos grosores de línea y rellenos. Esperamos que este ejemplo práctico le sea informativo y divertido. Ubicación Título – Ejercicio(s) Sección 3.9 Sección 4.14 Sección 5.10 Sección 6.13 Sección 7.13 Sección 8.18 Sección 9.8 Sección 10.8 Ejercicio 11.18 Ejercicio 12.31 Uso de cuadros de diálogo: entrada y salida básica con cuadros de diálogo. Creación de dibujos simples: mostrar y dibujar líneas en la pantalla. Dibujo de rectángulos y óvalos: uso de figuras para representar datos. Colores y figuras rellenas: dibujar un tiro al blanco y gráficos aleatorios. Dibujo de arcos: dibujar espirales con arcos. Uso de objetos con gráficos: almacenar figuras como objetos. Mostrar texto e imágenes mediante el uso de etiquetas: proporcionar información de estado. Dibujo con polimorfismo: identificar las similitudes entre figuras. Expansión de la interfaz: uso de componentes de la GUI y manejo de eventos. Agregar Java 2D: uso de la API 2D de Java para mejorar los dibujos. Figura 3.16 | Glosario de GUI ejemplo práctico en cada capítulo. Cómo mostrar texto en un cuadro de diálogo Los programas que hemos presentado hasta ahora muestran su salida en la ventana de comandos. Muchas aplica- ciones utilizan ventanas, o cuadros de diálogo (también llamados diálogos) para mostrar la salida. Por ejemplo, los navegadores Web como Firefox o Microsoft Internet Explorer muestran las páginas Web en sus propias venta- nas. Los programas de correo electrónico le permiten escribir y leer mensajes en una ventana. Por lo general, los cuadros de diálogo son ventanas en las que los programas muestran mensajes importantes a los usuarios. La clase JOptionPane cuenta con cuadros de diálogo previamente empaquetados, los cuales permiten a los programas mostrar ventanas que contengan mensajes; a dichas ventanas se les conoce como diálogos de mensaje. La figura 3.17 muestra el objeto String “BienvenidonanJava” en un diálogo de mensaje. La línea 3 indica que el programa utiliza la clase JOptionPane del paquete javax.swing. Este paquete contiene muchas clases que le ayudan a crear interfaces gráficas de usuario (GUIs) para las aplicaciones. Los componentes de la GUI facilitan la entrada de datos al usuario del programa, y la presentación de los datos de salida. La línea 10 llama al método showMessageDialog de JOptionPane para mostrar un cuadro de diálogo que contiene un mensaje. El método requiere dos argumentos. El primero ayuda a Java a determinar en dónde Cuenta – balance : Double «constructor» Cuenta( saldoInicial : Double ) + abonar( monto : Double ) + obtenerSaldo( ) : Double Figura 3.15 | Diagrama de clases de UML, el cual indica que la clase Cuenta tiene un atributo private llamado saldo, con el tipo Double de UML, un constructor (con un parámetro de tipo Double de UML) y dos operaciones public: abonar (con un parámetro monto de tipo Double de UML) y obtenerSaldo (devuelve el tipo Double de UML).
  • 135. colocar el cuadro de diálogo. Cuando el primer argumento es null, el cuadro de diálogo aparece en el centro de la pantalla de la computadora. El segundo argumento es el objeto String a mostrar en el cuadro de diálogo. El método showMessageDialog es un método static de la clase JOptionPane. A menudo, los métodos static definen las tareas utilizadas con frecuencia, y no se requiere crear explícitamente un objeto. Por ejemplo, muchos programas muestran cuadros de diálogo. En vez de que usted tenga que crear código para realizar esta tarea, los diseñadores de la clase JOptionPane de Java declaran un método static que realiza esta tarea por usted. Por lo general, la llamada a un método static se realiza mediante el uso del nombre de su clase, seguido de un punto (.) y del nombre del método, como en NombreClase.nombreMétodo( argumentos ) El capítulo 6, Métodos: un análisis más detallado, habla sobre los métodos static con detalle. Introducir texto en un cuadro de diálogo La aplicación de la figura 3.18 utiliza otro cuadro de diálogo JOptionPane predefinido, conocido como diálogo de entrada, el cual permite al usuario introducir datos en el programa. El programa pide el nombre del usuario, y responde con un diálogo de mensaje que contiene un saludo y el nombre introducido por el usuario. Figura 3.17 | Uso de JOptionPane para mostrar varias líneas en un cuadro de diálogo. 1 // Fig. 3.17: Dialogo1.java 2 // Imprimir varias líneas en un cuadro de diálogo. 3 import javax.swing.JOptionPane; // importa la clase JOptionPane 4 5 public class Dialogo1 6 { 7 public static void main( String args[] ) 8 { 9 // muestra un cuadro de diálogo con un mensaje 10 JOptionPane.showMessageDialog( null, “BienvenidonanJava” ); 11 } // fin de main 12 } // fin de la clase Dialogo1 1 // Fig. 3.18: DialogoNombre.java 2 // Entrada básica con un cuadro de diálogo. 3 import javax.swing.JOptionPane; 4 5 public class DialogoNombre 6 { 7 public static void main( String args[] ) 8 { 9 // pide al usuario que escriba su nombre 10 String nombre = 11 JOptionPane.showInputDialog( “Cual es su nombre?” ); 12 13 // crea el mensaje 14 String mensaje = Figura 3.18 | Cómo obtener la entrada del usuario mediante un cuadro de diálogo. (Parte 1 de 2). 3.9 Ejemplo práctico de GUI y gráficos: uso de cuadros de diálogo 97
  • 136. 98 Capítulo 3 Introducción a las clases y los objetos 15 String.format( “Bienvenido, %s, a la programacion en Java!”, nombre ); 16 17 // muestra el mensaje para dar la bienvenida al usuario por su nombre 18 JOptionPane.showMessageDialog( null, mensaje ); 19 } // fin de main 20 } // fin de la clase DialogoNombre Las líneas 10 y 11 utilizan el método showInputDialog de JOptionPane para mostrar un diálogo de entrada que contiene un indicador y un campo (conocido como campo de texto), en el cual el usuario puede escribir texto. El argumento de showInputDialog es el indicador que muestra lo que el usuario debe escribir. El usuario escribe caracteres en el campo de texto, y después hace clic en el botón Aceptar u oprime la tecla Intro para devolver el objeto String al programa. El método showInputDialog (línea 11) devuelve un objeto String que contiene los caracteres escritos por el usuario. Almacenamos el objeto String en la variable nombre (línea 10). [Nota: si oprime el botón Cancelar en el cuadro de diálogo, el método devuelve null y el programa muestra la palabra clave “null” como el nombre]. Las líneas 14 y 15 utilizan el método static String llamado format para devolver un objeto String que contiene un saludo con el nombre del usuario. El método format es similar al método System.out.printf, excepto que format devuelve el objeto String con formato, en vez de mostrarlo en una ventana de comandos. La línea 18 muestra el saludo en un cuadro de diálogo de mensaje. Ejercicio del ejemplo práctico de GUI y gráficos 3.1 Modifique el programa de suma en la figura 2.7 para usar la entrada y salida basadas en cuadro de diálogo con los métodos de la clase JOptionPane. Como el método showInputDialog devuelve un objeto String, debe convertir el objeto String que introduce el usuario a un int para usarlo en los cálculos. El método Integer.parseInt( String s ) toma un argumento String que representa a un entero (por ejemplo, el resultado de JOptionPane.showInputDialog) y devuelve el valor como un int. El método parseInt es un método static de la clase Integer (del paquete java.lang). Observe que si el objeto String no contiene un entero válido, el programa terminará con un error. 3.10 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un documento de requerimientos Ahora empezaremos a diseñar el sistema ATM que presentamos en el capítulo 2. En esta sección identificaremos las clases necesarias para crear el sistema ATM, analizando los sustantivos y las frases nominales que aparecen en el documento de requerimientos. Presentaremos los diagramas de clases de UML para modelar las relaciones entre estas clases. Este primer paso es importante para definir la estructura de nuestro sistema. Identificación de las clases en un sistema Para comenzar nuestro proceso de DOO, identificaremos las clases requeridas para crear el sistema ATM. Más adelante describiremos estas clases mediante el uso de los diagramas de clases de UML y las implementaremos en Java. Primero debemos revisar el documento de requerimientos de la sección 2.9, para identificar los sustantivos y frases nominales clave que nos ayuden a identificar las clases que conformarán el sistema ATM. Tal vez decidamos que algunos de estos sustantivos y frases nominales sean atributos de otras clases en el sistema. Tal vez también concluyamos que algunos de los sustantivos no corresponden a ciertas partes del sistema y, por ende, no deben modelarse. A medida que avancemos por el proceso de diseño podemos ir descubriendo clases adicionales. Figura 3.18 | Cómo obtener la entrada del usuario mediante un cuadro de diálogo. (Parte 2 de 2).
  • 137. La figura 3.19 lista los sustantivos y frases nominales que se encontraron en el documento de requerimientos de la sección 2.9. Los listaremos de izquierda a derecha, en el orden en el que los encontramos por primera vez en el documento de requerimientos. Sólo listaremos la forma singular de cada sustantivo o frase nominal. Sustantivos y frases nominales en el documento de requerimientos del ATM banco ATM usuario cliente transacción cuenta saldo dinero / fondos pantalla teclado numérico dispensador de efectivo billete de $20 / efectivo ranura de depósito sobre de depósito número de cuenta NIP base de datos del banco solicitud de saldo retiro depósito Figura 3.19 | Sustantivos y frases nominales en el documento de requerimientos del ATM. Crearemos clases sólo para los sustantivos y frases nominales que tengan importancia en el sistema ATM. No modelamos “banco” como una clase, ya que el banco no es una parte del sistema ATM; el banco sólo quiere que nosotros construyamos el ATM. “Cliente” y “usuario” también representan entidades fuera del sistema; son importantes debido a que interactúan con nuestro sistema ATM, pero no necesitamos modelarlos como clases en el software del ATM. Recuerde que modelamos un usuario del ATM (es decir, un cliente del banco) como el actor en el diagrama de casos de uso de la figura 2.20. No necesitamos modelar “billete de $20” ni “sobre de depósito” como clases. Éstos son objetos físicos en el mundo real, pero no forman parte de lo que se va a automatizar. Podemos representar en forma adecuada la pre- sencia de billetes en el sistema, mediante el uso de un atributo de la clase que modela el dispensador de efectivo (en la sección 4.15 asignaremos atributos a las clases del sistema ATM). Por ejemplo, el dispensador de efec- tivo mantiene un conteo del número de billetes que contiene. El documento de requerimientos no dice nada acerca de lo que debe hacer el sistema con los sobres de depósito después de recibirlos. Podemos suponer que con sólo admitir la recepción de un sobre (una operación que realiza la clase que modela la ranura de depósito) es suficiente para representar la presencia de un sobre en el sistema (en la sección 6.14 asignaremos operaciones a las clases del sistema ATM). En nuestro sistema ATM simplificado, lo más apropiado sería representar varios montos de “dinero”, inclu- yendo el “saldo” de una cuenta, como atributos de clases. De igual forma, los sustantivos “número de cuenta” y “NIP” representan piezas importantes de información en el sistema ATM. Son atributos importantes de una cuenta bancaria. Sin embargo, no exhiben comportamientos. Por ende, podemos modelarlos de la manera más apropiada como atributos de una clase de cuenta. Aunque, con frecuencia, el documento de requerimientos describe una “transacción” en un sentido general, no modelaremos la amplia noción de una transacción financiera en este momento. En vez de ello, modelaremos los tres tipos de transacciones (es decir, “solicitud de saldo”, “retiro” y “depósito”) como clases individuales. Estas clases poseen los atributos específicos necesarios para ejecutar las transacciones que representan. Por ejemplo, para un retiro se necesita conocer el monto de dinero que el usuario desea retirar. Sin embargo, una solicitud de saldo no requiere datos adicionales. Lo que es más, las tres clases de transacciones exhiben comportamientos únicos. Para un retiro se requiere entregar efectivo al usuario, mientras que para un depósito se requiere recibir un sobre de depósito del usuario. [Nota: en la sección 10.9, “factorizaremos” las características comunes de todas las tran- sacciones en una clase de “transacción” general, mediante el uso del concepto orientado a objetos de herencia]. Determinaremos las clases para nuestro sistema con base en los sustantivos y frases nominales restantes de la figura 3.19. Cada una de ellas se refiere a uno o varios de los siguientes elementos: ATM pantalla teclado numérico dispensador de efectivo ranura de depósito • • • • • cuenta base de datos del banco solicitud de saldo retiro depósito • • • • • 3.10 (Opción) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un... 99
  • 138. 100 Capítulo 3 Introducción a las clases y los objetos Es probable que los elementos de esta lista sean clases que necesitaremos implementar en nuestro sistema. Ahora podemos modelar las clases en nuestro sistema, con base en la lista que hemos creado. En el proceso de diseño escribimos los nombres de las clases con la primera letra en mayúscula (una convención de UML), como lo haremos cuando escribamos el código de Java para implementar nuestro diseño. Si el nombre de una clase contiene más de una palabra, juntaremos todas las palabras y escribiremos la primera letra de cada una de ellas en mayúscula (por ejemplo, NombreConVariasPalabras). Utilizando esta convención, crearemos las clases ATM, Pantalla, Teclado, DispensadorEfectivo, RanuraDeposito, Cuenta, BaseDatosBanco, Soli- citudSaldo, Retiro y Deposito. Construiremos nuestro sistema mediante el uso de todas estas clases como bloques de construcción. Sin embargo, antes de empezar a construir el sistema, debemos comprender mejor la forma en que las clases se relacionan entre sí. Modelado de las clases UML nos permite modelar, a través de los diagramas de clases, las clases en el sistema ATM y sus interrelaciones. La figura 3.20 representa a la clase ATM. En UML, cada clase se modela como un rectángulo con tres comparti- mientos. El compartimiento superior contiene el nombre de la clase, centrado horizontalmente y en negrita. El compartimiento intermedio contiene los atributos de la clase (en las secciones 4.15 y 5.11 hablaremos sobre los atributos). El compartimiento inferior contiene las operaciones de la clase (que veremos en la sección 6.14). En la figura 3.20, los compartimientos intermedio e inferior están vacíos, ya que no hemos determinado los atributos y operaciones de esta clase todavía. Los diagramas de clases también muestran las relaciones entre las clases del sistema. La figura 3.21 muestra cómo nuestras clases ATM y Retiro se relacionan una con la otra. Por el momento modelaremos sólo este subcon- junto de las clases del ATM, por cuestión de simpleza. Más adelante en esta sección, presentaremos un diagrama de clases más completo. Observe que los rectángulos que representan a las clases en este diagrama no están sub- divididos en compartimientos. UML permite suprimir los atributos y las operaciones de una clase de esta forma, cuando sea apropiado, para crear diagramas más legibles. Un diagrama de este tipo se denomina diagrama con elementos omitidos (elided diagram): su información, como el contenido de los compartimientos segundo y tercero, no se modela. En las secciones 4.15 y 6.14 colocaremos información en estos compartimientos. En la figura 3.21, la línea sólida que conecta a las dos clases representa una asociación: una relación entre clases. Los números cerca de cada extremo de la línea son valores de multiplicidad; éstos indican cuántos objetos de cada clase participan en la asociación. En este caso, al seguir la línea de un extremo al otro se revela que, en un momento dado, un objeto ATM participa en una asociación con cero o con un objeto Retiro; cero si el usuario actual no está realizando una transacción o si ha solicitado un tipo distinto de transacción, y uno si el usuario ha solicitado un retiro. UML puede modelar muchos tipos de multiplicidad. La figura 3.22 lista y explica los tipos de multiplicidad. Una asociación puede tener nombre. Por ejemplo, la palabra Ejecuta por encima de la línea que conecta a las clases ATM y Retiro en la figura 3.21 indica el nombre de esa asociación. Esta parte del diagrama se lee así: “un objeto de la clase ATM ejecuta cero o un objeto de la clase Retiro”. Los nombres de las asociaciones son direc- cionales, como lo indica la punta de flecha rellena; por lo tanto, sería inapropiado, por ejemplo, leer la anterior asociación de derecha a izquierda como “cero o un objeto de la clase Retiro ejecuta un objeto de la clase ATM”. Figura 3.20 | Representación de una clase en UML mediante un diagrama de clases. ATM Figura 3.21 | Diagrama de clases que muestra una asociación entre clases. Ejecuta 1 transaccionActual 0..1 Retiro ATM
  • 139. La palabra transaccionActual en el extremo de Retiro de la línea de asociación en la figura 3.21 es un nombre de rol, el cual identifica el rol que desempeña el objeto Retiro en su relación con el ATM. Un nombre de rol agrega significado a una asociación entre clases, ya que identifica el rol que desempeña una clase dentro del contexto de una asociación. Una clase puede desempeñar varios roles en el mismo sistema. Por ejemplo, en un sistema de personal de una universidad, una persona puede desempeñar el rol de “profesor” con respecto a los estudiantes. La misma persona puede desempeñar el rol de “colega” cuando participa en una asociación con otro profesor, y de “entrenador” cuando entrena a los atletas estudiantes. En la figura 3.21, el nombre de rol transac- cionActual indica que el objeto Retiro que participa en la asociación Ejecuta con un objeto de la clase ATM representa a la transacción que está procesando el ATM en ese momento. En otros contextos, un objeto Retiro puede desempeñar otros roles (por ejemplo, la transacción anterior). Observe que no especificamos un nombre de rol para el extremo del ATM de la asociación Ejecuta. A menudo, los nombres de los roles se omiten en los diagramas de clases, cuando el significado de una asociación está claro sin ellos. Además de indicar relaciones simples, las asociaciones pueden especificar relaciones más complejas, como cuando los objetos de una clase están compuestos de objetos de otras clases. Considere un cajero automático real. ¿Qué “piezas” reúne un fabricante para construir un ATM funcional? Nuestro documento de requerimientos nos indica que el ATM está compuesto de una pantalla, un teclado, un dispensador de efectivo y una ranura de depósito. En la figura 3.23, los diamantes sólidos que se adjuntan a las líneas de asociación de la clase ATM indican que esta clase tiene una relación de composición con las clases Pantalla, Teclado, DispensadorEfectivo y RanuraDeposito. La composición implica una relación todo/parte. La clase que tiene el símbolo de composición (el diamante sólido) en su extremo de la línea de asociación es el todo (en este caso, ATM), y las clases en el otro extremo de las líneas de asociación son las partes; en este caso, las clases Pantalla, Teclado, Dispensador- Efectivo y RanuraDeposito. Las composiciones en la figura 3.23 indican que un objeto de la clase ATM está formado por un objeto de la clase Pantalla, un objeto de la clase DispensadorEfectivo, un objeto de la clase Teclado y un objeto de la clase RanuraDeposito. El ATM “tiene una” pantalla, un teclado, un dispensador de efectivo y una ranura de depósito. La relación tiene un define la composición (en la sección del Ejemplo práctico de Ingeniería de Software del capítulo 10 veremos que la relación “es un” define la herencia). De acuerdo con la especificación del UML (www.uml.org), las relaciones de composición tienen las siguien- tes propiedades: 1. Sólo una clase en la relación puede representar el todo (es decir, el diamante puede colocarse sólo en un extremo de la línea de asociación). Por ejemplo, la pantalla es parte del ATM o el ATM es parte de la pantalla, pero la pantalla y el ATM no pueden representar ambos el “todo” dentro de la relación. 2. Las partes en la relación de composición existen sólo mientras exista el todo, y el todo es responsable de la creación y destrucción de sus partes. Por ejemplo, el acto de construir un ATM incluye la manufactura de sus partes. Lo que es más, si el ATM se destruye, también se destruyen su pantalla, teclado, dispen- sador de efectivo y ranura de depósito. 3. Una parte puede pertenecer sólo a un todo a la vez, aunque esa parte puede quitarse y unirse a otro todo, el cual entonces asumirá la responsabilidad de esa parte. 3.10 (Opción) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un... 101 Símbolo Significado 0 1 m 0..1 m, n m..n * 0..* 1..* Ninguno. Uno. Un valor entero. Cero o uno. m o n. Cuando menos m, pero no más que n. Cualquier entero no negativo (cero o más). Cero o más (idéntico a *). Uno o más. Figura 3.22 | Tipos de multiplicidad.
  • 140. 102 Capítulo 3 Introducción a las clases y los objetos Los diamantes sólidos en nuestros diagramas de clases indican las relaciones de composición que cumplen con estas tres propiedades. Si una relación “es un” no satisface uno o más de estos criterios, UML especifica que se deben adjuntar diamantes sin relleno a los extremos de las líneas de asociación para indicar una agregación: una forma más débil de la composición. Por ejemplo, una computadora personal y un monitor de computadora participan en una relación de agregación: la computadora “tiene un” monitor, pero las dos partes pueden existir en forma independiente, y el mismo monitor puede conectarse a varias computadoras a la vez, con lo cual se violan las propiedades segunda y tercera de la composición. La figura 3.24 muestra un diagrama de clases para el sistema ATM. Este diagrama modela la mayoría de las clases que identificamos antes en esta sección, así como las asociaciones entre ellas que podemos inferir del docu- mento de requerimientos. [Nota: las clases SolicitudSaldo y Deposito participan en asociaciones similares a las de la clase Retiro, por lo que preferimos omitirlas en este diagrama por cuestión de simpleza. En el capítulo 10 expandiremos nuestro diagrama de clases para incluir todas las clases en el sistema ATM]. La figura 3.24 presenta un modelo gráfico de la estructura del sistema ATM. Este diagrama de clases incluye a las clases BaseDatosBanco y Cuenta, junto con varias asociaciones que no presentamos en las figuras 3.21 o 3.23. El diagrama de clases muestra que la clase ATM tiene una relación de uno a uno con la clase BaseDatos- Banco: un objeto ATM autentica a los usuarios en base a un objeto BaseDatosBanco. En la figura 3.24 también modelamos el hecho de que la base de datos del banco contiene información sobre muchas cuentas; un objeto de la clase BaseDatosBanco participa en una relación de composición con cero o más objetos de la clase Cuenta. Recuerde que en la figura 3.22 se muestra que el valor de multiplicidad 0..* en el extremo de la clase Cuenta, de la asociación entre las clases BaseDatosBanco y Cuenta, indica que cero o más objetos de la clase Cuenta par- ticipan en la asociación. La clase BaseDatosBanco tiene una relación de uno a varios con la clase Cuenta; BaseDatosBanco puede contener muchos objetos Cuenta. De manera similar, la clase Cuenta tiene una relación de varios a uno con la clase BaseDatosBanco; puede haber muchos objetos Cuenta en BaseDatosBanco. [Nota: si recuerda la figura 3.22, el valor de multiplicidad * es idéntico a 0..*. Incluimos 0..* en nuestros diagramas de clases por cuestión de claridad]. La figura 3.24 también indica que si el usuario va a realizar un retiro, “un objeto de la clase Retiro accede a/modifica el saldo de una cuenta a través de un objeto de la clase BaseDatosBanco”. Podríamos haber creado una asociación directamente entre la clase Retiro y la clase Cuenta. No obstante, el documento de requerimientos indica que el “ATM debe interactuar con la base de datos de información de las cuentas del banco” para realizar transacciones. Una cuenta de banco contiene información delicada, por lo que los ingenieros de sistemas deben considerar siempre la seguridad de los datos personales al diseñar un sistema. Por ello, sólo BaseDatosBanco pue- de acceder a una cuenta y manipularla en forma directa. Todas las demás partes del sistema deben interactuar con la base de datos para recuperar o actualizar la información de las cuentas (por ejemplo, el saldo de una cuenta). El diagrama de clases de la figura 3.24 también modela las asociaciones entre la clase Retiro y las clases Pantalla, DispensadorEfectivo y Teclado. Una transacción de retiro implica pedir al usuario que seleccione el monto a retirar; también implica recibir entrada numérica. Estas acciones requieren el uso de la pantalla y del teclado, respectivamente. Además, para entregar efectivo al usuario se requiere acceso al dispensador de efectivo. 1 1 1 1 1 1 1 1 Pantalla ATM Teclado RanuraDeposito DispensadorEfectivo Figura 3.23 | Diagrama de clases que muestra las relaciones de composición.
  • 141. Aunque no se muestran en la figura 3.24, las clases SolicitudSaldo y Deposito participan en varias asocia- ciones con las otras clases del sistema ATM. Al igual que la clase Retiro, cada una de estas clases se asocia con las clases ATM y BaseDatosBanco. Un objeto de la clase SolicitudSaldo también se asocia con un objeto de la clase Pantalla para mostrar al usuario el saldo de una cuenta. La clase Deposito se asocia con las clases Pantalla, Teclado y RanuraDeposito. Al igual que los retiros, las transacciones de depósito requieren el uso de la pantalla y el teclado para mostrar mensajes y recibir datos de entrada, respectivamente. Para recibir sobres de depósito, un objeto de la clase Deposito accede a la ranura de depósitos. Ya hemos identificado las clases en nuestro sistema ATM (aunque tal vez descubramos otras, a medida que avancemos con el diseño y la implementación). En la sección 4.15 determinaremos los atributos para cada una de estas clases, y en la sección 5.11 utilizaremos estos atributos para examinar la forma en que cambia el sistema con el tiempo. Figura 3.24 | Diagrama de clases para el modelo del sistema ATM. Accede a/modifica el saldo de una cuenta a través de Ejecuta 1 1 1 1 1 1 1 1 1 1 1 1 1 0..* 0..1 0..1 0..1 0..1 0..1 1 Contiene Autentica al usuario en base a Teclado Retiro RanuraDeposito ATM DispensadorEfectivo Pantalla Cuenta BaseDatosBanco Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 3.1 Suponga que tenemos una clase llamada Auto, la cual representa a un automóvil. Piense en algunas de las distin- tas piezas que podría reunir un fabricante para producir un automóvil completo. Cree un diagrama de clases (similar a la figura 3.23) que modele algunas de las relaciones de composición de la clase Auto. 3.2 Suponga que tenemos una clase llamada Archivo, la cual representa un documento electrónico en una computadora independiente, sin conexión de red, representada por la clase Computadora. ¿Qué tipo de asociación existe entre la clase Computadora y la clase Archivo? a) La clase Computadora tiene una relación de uno a uno con la clase Archivo. b) La clase Computadora tiene una relación de varios a uno con la clase Archivo. c) La clase Computadora tiene una relación de uno a varios con la clase Archivo. d) La clase Computadora tiene una relación de varios a varios con la clase Archivo. 3.3 Indique si la siguiente aseveración es verdadera o falsa. Si es falsa, explique por qué: un diagrama de clases de UML, en el que no se modelan los compartimientos segundo y tercero, se denomina diagrama con elementos omitidos (elided diagram). 3.4 Modifique el diagrama de clases de la figura 3.24 para incluir la clase Deposito, en vez de la clase Retiro. 3.10 (Opción) Ejemplo práctico de Ingeniería de Software: identificación de las clases en un... 103
  • 142. 104 Capítulo 3 Introducción a las clases y los objetos Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 3.1 [Nota: las respuestas de los estudiantes pueden variar]. La figura 3.25 presenta un diagrama de clases que muestra algunas de las relaciones de composición de una clase Auto. 3.2 c. [Nota: en una computadora con conexión de red, esta relación podría ser de varios a varios]. 3.3 Verdadera. 3.4 La figura 3.26 presenta un diagrama de clases para el ATM, en el cual se incluye la clase Deposito en vez de la clase Retiro (como en la figura 3.24). Observe que la clase Deposito no se asocia con la clase DispensadorEfectivo, sino que se asocia con la clase RanuraDeposito. Auto Rueda Parabrisas CinturonSeguridad Volante 1 1 5 2 1 1 4 1 Accede a/modifica el saldo de una cuenta a través de Ejecuta 1 1 1 1 1 1 1 1 1 1 1 1 1 0..* 0..1 0..1 0..1 0..1 0..1 1 Contiene Autentica el usuario en base a Teclado Deposito RanuraDeposito ATM DispensadorEfectivo Pantalla Cuenta BaseDatosBanco Figura 3.25 | Diagrama de clases que muestra algunas relaciones de composición de una clase Auto. Figura 3.26 | Diagrama de clases para el modelo del sistema ATM, incluyendo la clase Deposito.
  • 143. 3.11 Conclusión En este capítulo aprendió los conceptos básicos de las clases, los objetos, los métodos y las variables de instancia; utilizará estos conceptos en la mayoría de las aplicaciones de Java que vaya a crear. En especial, aprendió a declarar variables de instancia de una clase para mantener los datos de cada objeto de la clase, y cómo declarar métodos que operen sobre esos datos. Aprendió cómo llamar a un método para decirle que realice su tarea y cómo pasar información a los métodos en forma de argumentos. Vio la diferencia entre una variable local de un método y una variable de instancia de una clase, y que sólo las variables de instancia se inicializan en forma automática. También aprendió a utilizar el constructor de una clase para especificar los valores iniciales para las variables de instancia de un objeto. A lo largo del capítulo, vio cómo puede usarse UML para crear diagramas de clases que modelen los constructores, métodos y atributos de las clases. Por último, aprendió acerca de los números de punto flotante: cómo almacenarlos con variables del tipo primitivo double, cómo recibirlos en forma de datos de entra- da mediante un objeto Scanner y cómo darles formato con printf y el especificador de formato %f para fines de visualización. En el siguiente capítulo empezaremos nuestra introducción a las instrucciones de control, las cuales especifican el orden en el que se realizan las acciones de un programa. Utilizará estas instrucciones en sus métodos para especificar cómo deben realizar sus tareas. Resumen Sección 3.2 Clases, objetos, métodos y variables de instancia • Para realizar una tarea en un programa se requiere un método. Dentro del método se colocan los mecanismos que hacen que éste realice sus tareas; es decir, el método oculta los detalles de implementación de las tareas que realiza. • La unidad de programa que aloja a un método se llama clase. Una clase puede contener uno o más métodos, que están diseñados para realizar las tareas de esa clase. • Un método puede realizar una tarea y devolver un resultado. • Puede utilizarse una clase para crear una instancia de la clase, a la cual se le llama objeto. Ésta es una de las razones por las que Java se conoce como lenguaje de programación orientado a objetos. • Cada mensaje que se envía a un objeto se conoce como llamada a un método, y ésta le indica a un método del objeto que realice su tarea. • Cada método puede especificar parámetros que representan la información adicional requerida por el método para realizar su tarea correctamente. La llamada a un método suministra valores (llamados argumentos) para los paráme- tros del método. • Un objeto tiene atributos que se acarrean con el objeto, a medida que éste se utiliza en un programa. Estos atributos se especifican como parte de la clase del objeto. Los atributos se especifican en las clases mediante campos. Sección 3.3 Declaración de una clase con un método, e instanciamiento de un objeto de una clase • Cada declaración de clase que empieza con la palabra clave public debe almacenarse en un archivo que tenga exac- tamente el mismo nombre que la clase, y que termine con la extensión de nombre de archivo .java. • La palabra clave public es un modificador de acceso. • Cada declaración de clase contiene la palabra clave class, seguida inmediatamente por el nombre de la clase. • La declaración de un método que empieza con la palabra clave public indica que el método está “disponible para el público”; es decir, lo pueden llamar otras clases declaradas fuera de la declaración de esa clase. • La palabra clave void indica que un método realizará una tarea, pero no devolverá información cuando la termine. • Por convención, los nombres de los métodos empiezan con la primera letra en minúscula, y todas las palabras sub- siguientes en el nombre empiezan con la primera letra en mayúscula. • Los paréntesis vacíos después del nombre de un método indican que éste no requiere parámetros para realizar su tarea. • El cuerpo de todos los métodos está delimitado por llaves izquierda y derecha ({ y }). • El cuerpo de un método contiene instrucciones que realizan la tarea de éste. Una vez que se ejecutan las instruccio- nes, el método ha terminado su tarea. • Cuando intentamos ejecutar una clase, Java busca el método main de la clase para empezar la ejecución. • Cualquier clase que contenga public static void main( String args[] ) puede usarse para ejecutar una aplica- ción. • Por lo general, no podemos llamar a un método que pertenece a otra clase, sino hasta crear un objeto de esa clase. Resumen 105
  • 144. • Las expresiones de creación de instancias de clases que empiezan con la palabra clave new crean nuevos objetos. • Para llamar a un método de un objeto, se pone después del nombre de la variable un separador punto (.), el nombre del método y un conjunto de paréntesis, que contienen los argumentos del método. • En UML, cada clase se modela en un diagrama de clases en forma de rectángulo con tres compartimientos. El compartimiento superior contiene el nombre de la clase, centrado horizontalmente y en negrita. El compartimiento intermedio contiene los atributos de la clase, que corresponden a los campos en Java. El compartimiento inferior contiene las operaciones de la clase, que corresponden a los métodos y constructores en Java. • Para modelar las operaciones, UML lista el nombre de la operación, seguido de un conjunto de paréntesis. Un signo más (+) enfrente del nombre de la operación indica que ésta es una operación public en UML (es decir, un método public en Java). Sección 3.4 Declaración de un método con un parámetro • A menudo, los métodos requieren información adicional para realizar sus tareas. Dicha información adicional se proporciona mediante argumentos en las llamadas a los métodos. • El método nextLine de Scanner lee caracteres hasta encontrar una nueva línea, y después devuelve los caracteres que leyó en forma de un objeto String. • El método next de Scanner lee caracteres hasta encontrar cualquier carácter de espacio en blanco, y después devuelve los caracteres que leyó en forma de un objeto String. • Un método que requiere datos para realizar su tarea debe especificar esto en su declaración, para lo cual coloca infor- mación adicional en la lista de parámetros del método. • Cada parámetro debe especificar tanto un tipo como un identificador. • Cuando se hace la llamada a un método, sus argumentos se asignan a sus parámetros. Entonces, el cuerpo del méto- do utiliza las variables de los parámetros para acceder a los valores de los argumentos. • Un método puede especificar varios parámetros, separando un parámetro del otro mediante una coma. • El número de argumentos en la llamada a un método debe coincidir con el número de parámetros en la lista de parámetros de la declaración del método. Además, los tipos de los argumentos en la llamada al método deben ser consistentes con los tipos de los parámetros correspondientes en la declaración del método. • La clase String está en el paquete java.lang que, por lo general se importa de manera implícita en todos los archivos de código fuente. • Hay una relación especial entre las clases que se compilan en el mismo directorio en el disco. De manera predetermi- nada, se considera que dichas clases están en el mismo paquete, al cual se le conoce como paquete predeterminado. Las clases en el mismo paquete se importan implícitamente en los archivos de código fuente de las otras clases que están en el mismo paquete. Por ende, no se requiere una declaración import cuando una clase en un paquete utiliza a otra clase en el mismo paquete. • No se requiere una declaración import si siempre hacemos referencia a una clase con su nombre de clase completa- mente calificado. • Para modelar un parámetro de una operación, UML lista el nombre del parámetro, seguido de dos puntos y el tipo del parámetro entre los paréntesis que van después del nombre de la operación. • UML tiene sus propios tipos de datos, similares a los de Java. No todos los tipos de datos de UML tienen los mismos nombres que los tipos correspondientes en Java. • El tipo String de UML corresponde al tipo String de Java. Sección 3.5 Variables de instancia, métodos establecer y métodos obtener • Las variables que se declaran en el cuerpo de un método específico se conocen como variables locales, y pueden utilizarse sólo en ese método. • Por lo general, una clase consiste en uno o más métodos que manipulan los atributos (datos) pertenecientes a un objeto específico de esa clase. Los atributos se representan como campos en la declaración de una clase. Dichas variables se llaman campos, y se declaran dentro de la declaración de una clase, pero fuera de los cuerpos de las declaraciones de los métodos de esa clase. • Cuando cada objeto de una clase mantiene su propia copia de un atributo, el campo que representa a ese atributo también se conoce como variable de instancia. Cada objeto (instancia) de la clase tiene una instancia separada de la variable en la memoria. • La mayoría de las declaraciones de variables de instancia van precedidas por el modificador de acceso private. Las variables o métodos declarados con el modificador de acceso private sólo están accesibles para los métodos de la clase en la que están declarados. • Al proceso de declarar variables de instancia con el modificador de acceso private se le conoce como ocultamiento de datos. 106 Capítulo 3 Introducción a las clases y los objetos
  • 145. • Un beneficio de los campos es que todos los métodos de la clase pueden usarlos. Otra diferencia entre un campo y una variable local es que un campo tiene un valor inicial predeterminado, que Java proporciona cuando el progra- mador no especifica el valor inicial del campo, pero una variable local no hace esto. • El valor predeterminado para un campo de tipo String es null. • Cuando se llama a un método que especifica un tipo de valor de retorno y completa su tarea, el método devuelve un resultado al método que lo llamó. • A menudo, las clases proporcionan métodos public para permitir que los clientes de la clase establezcan u obtengan variables de instancia private. Los nombres de estos métodos no necesitan comenzar con establecer u obtener, pero esta convención de nomenclatura es muy recomendada en Java, y requerida para ciertos componentes de software de Java especiales, conocidos como JavaBeans. • UML representa a las variables de instancia como atributos, listando el nombre del atributo, seguido de dos puntos y el tipo del atributo. • En UML, los atributos privados van precedidos por un signo menos (-). • Para indicar el tipo de valor de retorno de una operación, UML coloca dos puntos y el tipo de valor de retorno después de los paréntesis que siguen del nombre de la operación. • Los diagramas de clases de UML no especifican tipos de valores de retorno para las operaciones que no devuelven valores. Sección 3.6 Comparación entre tipos primitivos y tipos por referencia • En Java, los tipos se dividen en dos categorías: tipos primitivos y tipos por referencia (algunas veces conocidos como tipos no primitivos). Los tipos primitivos son boolean, byte, char, short, int, long, float y double. Todos los demás tipos son por referencia, por lo cual, las clases que especifican los tipos de los objetos, son tipos por referencia. • Una variable de tipo primitivo puede almacenar exactamente un valor de su tipo declarado, en un momento dado. • Las variables de instancia de tipos primitivos se inicializan de manera predeterminada. Las variables de los tipos byte, char, short, int, long, float y double se inicializan con 0. Las variables de tipo boolean se inicializan con false. • Los programas utilizan variables de tipos por referencia (llamadas referencias) para almacenar la ubicación de un objeto en la memoria de la computadora. Dichas variables hacen referencia a los objetos en el programa. El objeto al que se hace referencia puede contener muchas variables de instancia y métodos. • Los campos de tipo por referencia se inicializan de manera predeterminada con el valor null. • Para invocar a los métodos de instancia de un objeto, se requiere una referencia a éste. Una variable de tipo primitivo no hace referencia a un objeto, por lo cual no puede usarse para invocar a un método. Sección 3.7 Inicialización de objetos con constructores • Un constructor puede usarse para inicializar un objeto de una clase, a la hora de crear este objeto. • Los constructores pueden especificar parámetros, pero no tipos de valores de retorno. • Si no se proporciona un constructor para una clase, el compilador proporciona un constructor predeterminado sin parámetros. • Cuando una clase sólo tiene el constructor predeterminado, sus variables de instancia se inicializan con sus valores predeterminados. Las variables de los tipos char, byte, short, int, long, float y double se inicializan con 0, las variables de tipo boolean se inicializan con false, y las variables de tipo por referencia se inicializan con null. • Al igual que las operaciones, UML modela a los constructores en el tercer compartimiento de un diagrama de clases. Para diferenciar a un constructor en base a las operaciones de una clase, UML coloca la palabra “constructor” entre los signos « y » antes del nombre del constructor. Sección 3.8 Números de punto flotante y el tipo double • Un número de punto flotante es un número con un punto decimal, como 7.33, 0.0975 o 1000.12345. Java propor- ciona dos tipos primitivos para almacenar números de punto flotante en la memoria –float y double. La principal diferencia entre estos tipos es que las variables double pueden almacenar números con mayor magnitud y detalle (a esto se le conoce como la precisión del número) que las variables float. • Las variables de tipo float representan números de punto flotante de precisión simple, y tienen siete dígitos signifi- cativos. Las variables de tipo double representan números de punto flotante de precisión doble. Éstos requieren el doble de memoria que las variables float y proporcionan 15 dígitos significativos; tienen aproximadamente el doble de precisión de las variables float. • Los valores de punto flotante que aparecen en código fuente se conocen como literales de punto flotante, y son de tipo double de manera predeterminada. Resumen 107
  • 146. • El método nextDouble de Scanner devuelve un valor double. • El especificador de formato %f se utiliza para mostrar valores de tipo float o double. Puede especificarse una precisión entre % y f para representar el número de posiciones decimales que deben mostrarse a la derecha del punto decimal, en el número de punto flotante. • El valor predeterminado para un campo de tipo double es 0.0, y el valor predeterminado para un campo de tipo int es 0. Terminología 108 Capítulo 3 Introducción a las clases y los objetos %f,especificador de formato « y » (UML) agregación (UML) atributo (UML) campo campo de texto (GUI) clase class, palabra clave cliente de un objeto de una clase compartimiento en un diagrama de clases (UML) componente de interfaz gráfica de usuario (GUI) composición (UML) constructor constructor predeterminado crear un objeto cuadro de diálogo (GUI) cuadro de diálogo de entrada (GUI) cuadro de diálogo de mensaje (GUI) declaración de clase declarar un método diagrama con elementos omitidos (UML) diagrama de clases de UML diálogo (GUI) diamante sólido (UML) double, tipo primitivo encabezado de un método enviar un mensaje establecer, método expresión de creación de instancia de clase float, tipo primitivo instancia de clase instancia de una clase (objeto) instanciar (o crear) un objeto interfaz gráfica de usuario (GUI) invocar a un método JOptionPane, clase (GUI) lenguaje extensible lista de parámetros literal de punto flotante llamada a un método mensaje método método que hace la llamada modificador de acceso multiplicidad (UML) new, palabra clave next, método de la clase Scanner nextDouble, método de la clase Scanner nextLine, método de la clase Scanner nombre de rol (UML) null, palabra reservada número de punto flotante número de punto flotante de precisión doble número de punto flotante de precisión simple objeto (o instancia) ocultamiento de datos operación (UML) paquete predeterminado parámetro precisión de un número de punto flotante con formato precisión de un valor de punto flotante private, modificador de acceso public, método public, modificador de acceso punto (.), separador referencia referirse a un objeto relación “tiene un” relación de uno a varios (UML) relación de varios a uno (UML) relación de varios a varios (UML) showInputDialog, método de la clase JOptionPane (GUI) showMessageDialog, método de la clase JOptionPane (GUI) tipo de valor de retorno de un método tipo por referencia tipos no primitivos valor inicial predeterminado valor predeterminado variable de instancia variable local void, palabra clave
  • 147. Ejercicios de autoevaluación 3.1 Complete las siguientes oraciones: a) Una casa es para un plano de construcción lo que un(a) ____________ para una clase. b) Cada declaración de clase que empieza con la palabra clave ____________ debe almacenarse en un archivo que tenga exactamente el mismo nombre de la clase, y que termine con la extensión de nombre de archi- vo .java. c) Cada declaración de clase contiene la palabra clave ____________, seguida inmediatamente por el nombre de la clase. d) La palabra clave ____________ crea un objeto de la clase especificada a la derecha de la palabra clave. e) Cada parámetro debe especificar un(a) ____________ y un(a) ____________. f) De manera predeterminada, se considera que las clases que se compilan en el mismo directorio están en el mismo paquete, conocido como ____________. g) Cuando cada objeto de una clase mantiene su propia copia de un atributo, el campo que representa a este atributo se conoce también como ____________. h) Java proporciona dos tipos primitivos para almacenar números de punto flotante en la memoria: _______ _____ y ____________. i) Las variables de tipo double representan a los números de punto flotante ____________. j) El método ____________ de la clase Scanner devuelve un valor double. k) La palabra clave public es un(a) ____________. l) El tipo de valor de retorno ____________ indica que un método realizará una tarea, pero no devolverá información cuando complete su tarea. m) El método ____________ de Scanner lee caracteres hasta encontrar una nueva línea, y después devuelve esos caracteres como un objeto String. n) La clase String está en el paquete ____________. o) No se requiere un(a) ____________ si siempre hacemos referencia a una clase con su nombre de clase completamente calificado. p) Un ____________ es un número con un punto decimal, como 7.33, 0.0975 o 1000.12345. q) Las variables de tipo float representan números de punto flotante ____________. r) El especificador de formato ____________ se utiliza para mostrar valores de tipo float o double. s) Los tipos en Java se dividen en dos categorías: tipos ____________ y tipos ____________. 3.2 Conteste con verdadero o falso a cada una de las siguientes proposiciones; en caso de ser falso, explique por qué. a) Por convención, los nombres de los métodos empiezan con la primera letra en mayúscula y todas las pala- bras subsiguientes en el nombre empiezan con la primera letra en mayúscula. b) Una declaración import no es obligatoria cuando una clase en un paquete utiliza a otra clase en el mismo paquete. c) Los paréntesis vacíos que van después del nombre de un método en la declaración de un método indican que éste no requiere parámetros para realizar su tarea. d) Las variables o los métodos declarados con el modificador de acceso private son accesibles sólo para los métodos de la clase en la que se declaran. e) Una variable de tipo primitivo puede usarse para invocar un método. f) Las variables que se declaran en el cuerpo de un método específico se conocen como variables de instancia, y pueden utilizarse en todos los métodos de la clase. g) El cuerpo de cada método está delimitado por llaves izquierda y derecha ({ y }). h) Las variables locales de tipo primitivo se inicializan de manera predeterminada. i) Las variables de instancia de tipo por referencia se inicializan de manera predeterminada con el valor null. j) Cualquier clase que contenga public static void main( String args[] ) puede usarse para ejecutar una aplicación. k) El número de argumentos en la llamada a un método debe coincidir con el número de parámetros en la lista de parámetros de la declaración del método. l) Los valores de punto flotante que aparecen en código fuente se conocen como literales de punto flotante, y son de tipo float de manera predeterminada. 3.3 ¿Cuál es la diferencia entre una variable local y un campo? 3.4 Explique el propósito de un parámetro de un método. ¿Cuál es la diferencia entre un parámetro y un argumento? Ejercicios de autoevaluación 109
  • 148. Respuestas a los ejercicios de autoevaluación 3.1 a) objeto. b) public. c) class. d) new. e) tipo, nombre. f) paquete predeterminado. g) varia- ble de instancia. h) float, double. i) de precisión doble. j) nextDouble. k) modificador de acceso. l) void. m) nextLine. n) java.lang. o) declaracion import. p) número de punto flotante. q) de precisión simple. r) %f. s) primitivo, por referencia. 3.2 a) Falso. Por convención, los nombres de los métodos empiezan con una primera letra en minúscula y todas las palabras subsiguientes en el nombre empiezan con una letra en mayúscula. b) Verdadero. c) Verdadero. d) Verdadero. e) Falso. Una variable de tipo primitivo no puede usarse para invocar a un método; se requiere una referencia a un objeto para invocar a los métodos de ese objeto. f) Falso. Dichas variables se llaman variables locales, y sólo se pueden utilizar en el método en el que están declaradas. g) Verdadero. h) Falso. Las variables de instancia de tipo primitivo se inicializan de manera predeterminada. A cada variable local se le debe asignar un valor de manera explícita. i) Verdadero. j) Verdadero. k) Verdadero. l) Falso. Dichas literales son de tipo double de manera pre- determinada. 3.3 Una variable local se declara en el cuerpo de un método, y sólo puede utilizarse desde el punto en el que se declaró, hasta el final de la declaración del método. Un campo se declara en una clase, pero no en el cuerpo de alguno de los métodos de la clase. Cada objeto (instancia) de una clase tiene una copia separada de los campos de la clase. Además, los campos están accesibles para todos los métodos de la clase. (En el capítulo 8, Clases y objetos: un análisis más detallado, veremos una excepción a esto). 3.4 Un parámetro representa la información adicional que requiere un método para realizar su tarea. Cada paráme- tro requerido por un método está especificado en la declaración del método. Un argumento es el valor actual para un parámetro del método. Cuando se llama a un método, los valores de los argumentos se pasan al método, para que éste pueda realizar su tarea. Ejercicios 3.5 ¿Cuál es el propósito de la palabra clave new? Explique lo que ocurre cuando se utiliza en una aplicación. 3.6 ¿Qué es un constructor predeterminado? ¿Cómo se inicializan las variables de instancia de un objeto, si una clase sólo tiene un constructor predeterminado? 3.7 Explique el propósito de una variable de instancia. 3.8 La mayoría de las clases necesitan importarse antes de poder utilizarlas en una aplicación ¿Por qué cualquier aplicación puede utilizar las clases System y String sin tener que importarlas primero? 3.9 Explique cómo utilizaría un programa la clase Scanner, sin importarla del paquete java.util. 3.10 Explique por qué una clase podría proporcionar un método establecer y un método obtener para una variable de instancia. 3.11 Modifique la clase LibroCalificaciones (figura 3.10) de la siguiente manera: a) Incluya una segunda variable de instancia String, que represente el nombre del instructor del curso. b) Proporcione un método establecer para modificar el nombre del instructor, y un método obtener para obte- ner el nombre. c) Modifique el constructor para especificar dos parámetros: uno para el nombre del curso y otro para el nom- bre del instructor. d) Modifique el método mostrarMensaje, de tal forma que primero imprima el mensaje de bienvenida y el nombre del curso, y que después imprima "Este curso es presentado por: ", seguido del nombre del instructor. Use su clase modificada en una aplicación de prueba que demuestre las nuevas capacidades de la clase. 3.12 Modifique la clase Cuenta (figura 3.13) para proporcionar un método llamado cargar, que retire dinero de un objeto Cuenta. Asegure que el monto a cargar no exceda el saldo de Cuenta. Si lo hace, el saldo debe permanecer sin cambio y el método debe imprimir un mensaje que indique "El monto a cargar excede el saldo de la cuenta". Modifique la clase PruebaCuenta (figura 3.14) para probar el método cargar. 3.13 Cree una clase llamada Factura, que una ferretería podría utilizar para representar una factura para un artículo vendido en la tienda. Una Factura debe incluir cuatro piezas de información como variables de instancia: un número de pieza (tipo String), la descripción de la pieza (tipo String), la cantidad de artículos de ese tipo que se van a comprar 110 Capítulo 3 Introducción a las clases y los objetos
  • 149. (tipo int) y el precio por artículo (double). Su clase debe tener un constructor que inicialice las cuatro variables de ins- tancia. Proporcione un método establecer y un método obtener para cada variable de instancia. Además, proporcione un método llamado obtenerMontoFactura, que calcule el monto de la factura (es decir, que multiplique la cantidad por el precio por artículo) y después devuelva ese monto como un valor double. Si la cantidad no es positiva, debe establecerse en 0. Si el precio por artículo no es positivo, debe establecerse a 0.0. Escriba una aplicación de prueba llamada Prueba- Factura, que demuestre las capacidades de la clase Factura. 3.14 Cree una clase llamada Empleado, que incluya tres piezas de información como variables de instancia: un primer nombre (tipo String), un apellido paterno (tipo String) y un salario mensual (double). Su clase debe tener un cons- tructor que inicialice las tres variables de instancia. Proporcione un método establecer y un método obtener para cada variable de instancia. Si el salario mensual no es positivo, establézcalo a 0.0. Escriba una aplicación de prueba llama- da PruebaEmpleado, que demuestre las capacidades de cada Empleado. Cree dos objetos Empleado y muestre el salario anual de cada objeto. Después, proporcione a cada Empleado un aumento del 10% y muestre el salario anual de cada Empleado otra vez. 3.15 Cree una clase llamada Fecha, que incluya tres piezas de información como variables de instancia —un mes (tipo int), un día (tipo int) y un año (tipo int). Su clase debe tener un constructor que inicialice las tres variables de instancia, y debe asumir que los valores que se proporcionan son correctos. Proporcione un método establecer y un método obtener para cada variable de instancia. Proporcione un método mostrarFecha, que muestre el mes, día y año, separados por barras diagonales (/). Escriba una aplicación de prueba llamada PruebaFecha, que demuestre las capaci- dades de la clase Fecha. Ejercicios 111
  • 150. Instrucciones de control: parte 1 OBJETIVO S En este capítulo aprenderá a: Comprender las técnicas básicas para solucionar problemas. Desarrollar algoritmos mediante el proceso de refinamiento de arriba a abajo, paso a paso, usando seudocódigo. Utilizar las estructuras de selección if e if...else para elegir entre distintas acciones alternativas. Utilizar la estructura de repetición while para ejecutar instrucciones de manera repetitiva dentro de un programa. Comprender la repetición controlada por un contador y la repetición controlada por un centinela. Utilizar los operadores de asignación compuestos, de incremento y decremento. Conocer los tipos de datos primitivos. Q Q Q Q Q Q Q Desplacémonos un lugar. —Lewis Carroll La rueda se convirtió en un círculo completo. —William Shakespeare ¡Cuántas manzanas tuvieron que caer en la cabeza de Newton antes de que entendiera el suceso! —Robert Frost Toda la evolución que conocemos procede de lo vago a lo definido. —Charles Sanders Peirce 4
  • 151. 4.2 Algoritmos 113 4.1 Introducción Antes de escribir un programa que dé solución a un problema, es imprescindible tener una comprensión detallada de todo el problema, además de una metodología cuidadosamente planeada para resolverlo. Al escribir un progra- ma, es igualmente esencial comprender los tipos de bloques de construcción disponibles, y emplear las técnicas comprobadas para construir programas. En este capítulo y en el 5, Instrucciones de control: parte 2, hablaremos sobre estas cuestiones cuando presentemos la teoría y los principios de la programación estructurada. Los concep- tos aquí presentados son imprescindibles para crear clases y manipular objetos. En este capítulo presentamos las instrucciones if...else y while de Java, tres de los bloques de construc- ción que permiten a los programadores especificar la lógica requerida para que los métodos realicen sus tareas. Dedicamos una parte de este capítulo (y de los capítulos 5 y 7) para desarrollar más la clase LibroCalificaciones que presentamos en el capítulo 3. En especial, agregamos un método a la clase LibroCalificaciones que utiliza instrucciones de control para calcular el promedio de un conjunto de calificaciones de estudiantes. Otro ejemplo demuestra formas adicionales de combinar instrucciones de control para resolver un problema similar. Presenta- mos los operadores de asignación compuestos de Java, y exploramos los operadores de incremento y decremento. Estos operadores adicionales abrevian y simplifican muchas instrucciones de los programas. Por último, presenta- mos las generalidades acerca de los tipos de datos primitivos que están disponibles para los programadores. 4.2 Algoritmos Cualquier problema de computación puede resolverse ejecutando una serie de acciones en un orden específico. Un procedimiento para resolver un problema en términos de: 1. las acciones a ejecutar y 2. el orden en el que se ejecutan estas acciones se conoce como un algoritmo. El siguiente ejemplo demuestra que es importante especificar de manera correcta el orden en el que se ejecutan las acciones. Considere el “algoritmo para levantarse y arreglarse” que sigue un ejecutivo para levantarse de la cama e ir a trabajar: (1) levantarse; (2) quitarse la pijama; (3) bañarse; (4) vestirse; (5) desayunar; (6) transportarse al tra- bajo. Esta rutina logra que el ejecutivo llegue al trabajo bien preparado para tomar decisiones críticas. Suponga 4.1 Introducción 4.2 Algoritmos 4.3 Seudocódigo 4.4 Estructuras de control 4.5 Instrucción de selección simple if 4.6 Instrucción de selección doble if...else 4.7 Instrucción de repetición while 4.8 Cómo formular algoritmos: repetición controlada por un contador 4.9 Cómo formular algoritmos: repetición controlada por un centinela 4.10 Cómo formular algoritmos: instrucciones de control anidadas 4.11 Operadores de asignación compuestos 4.12 Operadores de incremento y decremento 4.13 Tipos primitivos 4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples 4.15 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de los atributos de las clases 4.16 Conclusión Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios Pla n g e ne r a l
  • 152. 114 Capítulo 4 Instrucciones de control: parte 1 que los mismos pasos se realizan en un orden ligeramente distinto: (1) levantarse; (2) quitarse la pijama; (3) vestirse; (4) bañarse; (5) desayunar; (6) transportarse al trabajo. En este caso nuestro ejecutivo llegará al trabajo todo mojado. Al proceso de especificar el orden en el que se ejecutan las instrucciones (acciones) en un programa, se le llama control del programa. En este capítulo investigaremos el control de los programas mediante el uso de las instrucciones de control de Java. 4.3 Seudocódigo El seudocódigo es un lenguaje informal que ayuda a los programadores a desarrollar algoritmos sin tener que preocuparse por los estrictos detalles de la sintaxis del lenguaje Java. El seudocódigo que presentaremos es espe- cialmente útil para desarrollar algoritmos que se convertirán en porciones estructuradas de programas en Java. El seudocódigo es similar al lenguaje cotidiano; es conveniente y amigable con el usuario, aunque no es realmente un lenguaje de programación de computadoras. Empezaremos a utilizar el seudocódigo en la sección 4.5, y en la figura 4.5 aparece un programa de seudocódigo de ejemplo. El seudocódigo no se ejecuta en las computadoras. En vez de ello, ayuda al programador a “organizar” un programa antes de que intente escribirlo en un lenguaje de programación como Java. Este capítulo presenta varios ejemplos de cómo utilizar el seudocódigo para desarrollar programas en Java. El estilo de seudocódigo que presentaremos consiste solamente en caracteres, de manera que los programa- dores pueden escribir el seudocódigo, utilizando cualquier programa editor de texto. Un programa en seudocó- digo preparado de manera cuidadosa puede convertirse fácilmente en su correspondiente programa en Java. En muchos casos, esto requiere tan sólo reemplazar las instrucciones en seudocódigo con sus instrucciones equiva- lentes en Java. Por lo general, el seudocódigo describe sólo las instrucciones que representan las acciones que ocurren des- pués de que un programador convierte un programa de seudocódigo a Java, y el programa se ejecuta en una computadora. Dichas acciones podrían incluir la entrada, salida o un cálculo. Por lo general no incluimos las declaraciones de variables en nuestro seudocódigo, pero algunos programadores optan por listar las variables y mencionar sus propósitos al principio de su seudocódigo. 4.4 Estructuras de control Generalmente, en un programa las instrucciones se ejecutan una después de otra, en el orden en que están escritas. Este proceso se conoce como ejecución secuencial. Varias instrucciones en Java, que pronto veremos, permiten al programador especificar que la siguiente instrucción a ejecutarse tal vez no sea la siguiente en la secuencia. Esto se conoce como transferencia de control. Durante la década de los sesenta, se hizo evidente que el uso indiscriminado de las transferencias de control era el origen de muchas de las dificultades que experimentaban los grupos de desarrollo de software. A quien se señaló como culpable fue a la instrucción goto (utilizada en la mayoría de los lenguajes de programación de esa época), la cual permite al programador especificar la transferencia de control a uno de los muchos posibles desti- nos dentro de un programa. La noción de la llamada programación estructurada se hizo casi un sinónimo de la “eliminación del goto”. [Nota: Java no tiene una instrucción goto; sin embargo, la palabra goto está reservada para Java y no debe usarse como identificador en los programas]. Las investigaciones de Bohm y Jacopini1 demostraron que los programas podían escribirse sin instrucciones goto. El reto de la época para los programadores fue cambiar sus estilos a una “programación sin goto”. No fue sino hasta la década de los setenta cuando los programadores tomaron en serio la programación estructurada. Los resultados fueron impresionantes. Los grupos de desarrollo de software reportaron reducciones en los tiempos de desarrollo, mayor incidencia de entregas de sistemas a tiempo y más proyectos de software finalizados sin salirse del presupuesto. La clave para estos logros fue que los programas estructurados eran más claros, más fáciles de depurar y modificar, y había más probabilidad de que estuvieran libres de errores desde el principio. 1. Bohm, C. y G. Jacopini, “Flow Diagrams, Turing Machines and Languages with Only Two Formation Rules”, Communications of the ACM, vol. 9, núm. 5, mayo de 1966, páginas 336-371.
  • 153. 4.4 Estructuras de control 115 El trabajo de Bohm y Jacopini demostró que todos los programas podían escribirse en términos de tres estructuras de control solamente: la estructura de secuencia, la estructura de selección y la estructura de repetición. El término “estructuras de control” proviene del campo de las ciencias computacionales. Cuando presentemos las implementaciones de las estructuras de control en Java, nos referiremos a ellas en la terminología de la Especificación del lenguaje Java como “instrucciones de control”. Estructura de secuencia en Java La estructura de secuencia está integrada en Java. A menos que se le indique lo contrario, la computadora ejecuta las instrucciones en Java una después de otra, en el orden en que estén escritas; es decir, en secuencia. El diagrama de actividad de la figura 4.1 ilustra una estructura de secuencia típica, en la que se realizan dos cálculos en orden. Java permite tantas acciones como deseemos en una estructura de secuencia. Como veremos pronto, en donde quiera que se coloque una sola acción, podrán colocarse varias acciones en secuencia. Los diagramas de actividad son parte de UML. Un diagrama de actividad modela el flujo de trabajo (tam- bién conocido como la actividad) de una parte de un sistema de software. Dichos flujos de trabajo pueden incluir una porción de un algoritmo, como la estructura de secuencia de la figura 4.1. Los diagramas de actividad están compuestos por símbolos de propósito especial, como los símbolos de estado de acción (rectángulos cuyos lados izquierdo y derecho se reemplazan con arcos hacia fuera), rombos (diamantes) y pequeños círculos. Estos sím- bolos se conectan mediante flechas de transición, que representan el flujo de la actividad; es decir, el orden en el que deben ocurrir las acciones. Al igual que el seudocódigo, los diagramas de actividad ayudan a los programadores a desarrollar y represen- tar algoritmos; sin embargo, muchos de ellos aún prefieren el seudocódigo. Los diagramas de actividad muestran claramente cómo operan las estructuras de control. Considere el diagrama de actividad para la estructura de secuencia de la figura 4.1. Este diagrama contiene dos estados de acción que representan las acciones a realizar. Cada estado de acción contiene una expresión de acción (por ejemplo, “sumar calificación a total” o “sumar 1 al contador”), que especifica una acción particular a realizar. Otras acciones podrían incluir cálculos u operaciones de entrada/salida. Las flechas en el diagrama de actividad representan transiciones, las cuales indican el orden en el que ocurren las acciones representadas por los estados de acción. El programa que implementa las actividades ilustradas por el diagrama de la figura 4.1 primero suma calificacion a total, y después suma 1 a contador. El círculo relleno que se encuentra en la parte superior del diagrama de actividad representa el estado inicial de la actividad: el inicio del flujo de trabajo antes de que el programa realice las actividades modeladas. El círculo sólido rodeado por una circunferencia que aparece en la parte inferior del diagrama representa el estado final; es decir, el final del flujo de trabajo después de que el programa realiza sus acciones. La figura 4.1 también incluye rectángulos que tienen la esquina superior derecha doblada. En UML, a estos rectángulos se les llama notas (como los comentarios en Java): comentarios con explicaciones que describen el propósito de los símbolos en el diagrama. La figura 4.1 utiliza las notas de UML para mostrar el código en Java asociado con cada uno de los estados de acción en el diagrama de actividad. Una línea punteada conecta cada nota con el elemento que ésta describe. Los diagramas de actividad generalmente no muestran el código en Java que implementa la actividad. En este libro utilizamos las notas con este propósito, para mostrar cómo se rela- sumar 1 al contador sumar calificación al total Instrucción en Java correspondiente: total = total + calificacion; Instrucción en Java correspondiente: contador = contador + 1; Figura 4.1 | Diagrama de actividad de una estructura de secuencia.
  • 154. 116 Capítulo 4 Instrucciones de control: parte 1 ciona el diagrama con el código en Java. Para obtener más información sobre UML, vea nuestro ejemplo práctico opcional, que aparece en las secciones tituladas Ejemplo práctico de Ingeniería de Software al final de los capítulos 1 al 8 y 10, o visite www.uml.org. Instrucciones de selección en Java Java tiene tres tipos de instrucciones de selección (las cuales se describen en este capítulo y en el siguiente). La instrucción if realiza (selecciona) una acción si la condición es verdadera, o evita la acción si la condición es falsa. La instrucción if...else realiza una acción si la condición es verdadera, o realiza una acción distinta si la condición es falsa. La instrucción switch (capítulo 5) realiza una de entre varias acciones distintas, dependiendo del valor de una expresión. La instrucción if es una instrucción de selección simple, ya que selecciona o ignora una sola acción (o, como pronto veremos, un solo grupo de acciones). La instrucción if...else se conoce como instrucción de selección doble, ya que selecciona entre dos acciones distintas (o grupos de acciones). La instrucción switch es una estructura de selección múltiple, ya que selecciona entre diversas acciones (o grupos de acciones). Instrucciones de repetición en Java Java cuenta con tres instrucciones de repetición (también llamadas instrucciones de ciclo) que permiten a los programas ejecutar instrucciones en forma repetida, siempre y cuando una condición (llamada la condición de continuación del ciclo) siga siendo verdadera. Las instrucciones de repetición se implementan con las instruc- ciones while, do...while y for. (El capítulo 5 presenta las instrucciones do...while y for). Las instrucciones while y for realizan la acción (o grupo de acciones) en sus cuerpos, cero o más veces; si la condición de conti- nuación del ciclo es inicialmente falsa, no se ejecutará la acción (o grupo de acciones). La instrucción do...while realiza la acción (o grupo de acciones) en su cuerpo, una o más veces. Las palabras if, else, switch, while, do y for son palabras clave en Java; se utilizan para implementar varias características de Java, como las instrucciones de control. Las palabras clave no pueden usarse como identificadores, como los nombres de variables. En el apéndice C aparece una lista completa de las palabras clave en Java. Resumen de las instrucciones de control en Java Java sólo tiene tres tipos de estructuras de control, a las cuales nos referiremos de aquí en adelante como instruc- ciones de control: la instrucción de secuencia, las instrucciones de selección (tres tipos) y las instrucciones de repe- tición (tres tipos). Cada programa se forma combinando tantas instrucciones de secuencia, selección y repetición como sea apropiado para el algoritmo que implemente el programa. Al igual que con la instrucción de secuencia de la figura 4.1, podemos modelar cada una de las instrucciones de control como un diagrama de actividad. Cada diagrama contiene un estado inicial y final, los cuales representan el punto de entrada y salida de la instrucción de control, respectivamente. Las instrucciones de control de una sola entrada/una sola salida facilitan la creación de programas; las instrucciones de control están “unidas” entre sí mediante la conexión del punto de salida de una instrucción de control, al punto de entrada de la siguiente. Este procedimiento es similar a la manera en que un niño apila los bloques de construcción, así que a esto le llamamos apilamiento de instrucciones de control. En breve aprenderemos que sólo hay una manera alternativa de conectar las instrucciones de control: el anidamiento de instrucciones de control, en el cual una instrucción de control aparece dentro de otra. Por lo tanto, los algo- ritmos en los programas en Java se crean a partir de sólo tres principales tipos de instrucciones de control, que se combinan sólo de dos formas. Ésta es la esencia de la simpleza. 4.5 Instrucción de selección simple if Los programas utilizan instrucciones de selección para elegir entre los cursos alternativos de acción. Por ejemplo, suponga que la calificación para aprobar un examen es 60. La instrucción en seudocódigo Si la calificación del estudiante es mayor o igual a 60 Imprimir “Aprobado” determina si la condición “la calificación del estudiante es mayor o igual a 60” es verdadera o falsa. Si la condición es verdadera se imprime “Aprobado”, y se “ejecuta” en orden la siguiente instrucción en seudocódigo. (Recuerde que el seudocódigo no es un verdadero lenguaje de programación). Si la condición es falsa se ignora la instrucción
  • 155. Imprimir, y se ejecuta en orden la siguiente instrucción en seudocódigo. La sangría de la segunda línea de esta instrucción de selección es opcional, pero se recomienda ya que enfatiza la estructura inherente de los programas estructurados. La instrucción anterior if en seudocódigo puede escribirse en Java de la siguiente manera: if ( calificacionEstudiante >= 60 ) System.out.println( "Aprobado" ); Observe que el código en Java corresponde en gran medida con el seudocódigo. Ésta es una de las propiedades que hace del seudocódigo una herramienta de desarrollo de programas tan útil. La figura 4.2 muestra la instrucción if de selección simple. Esta figura contiene lo que quizá sea el símbolo más importante en un diagrama de actividad: el rombo o símbolo de decisión, el cual indica que se tomará una decisión. El flujo de trabajo continuará a lo largo de una ruta determinada por las condiciones de guardia asociadas de ese símbolo, que pueden ser verdaderas o falsas. Cada flecha de transición que sale de un símbolo de decisión tiene una condición de guardia (especificada entre corchetes, a un lado de la flecha de transición). Si una condición de guardia es verdadera, el flujo de trabajo entra al estado de acción al que apunta la flecha de transición. En la figura 4.2, si la calificación es mayor o igual a 60, el programa imprime “Aprobado” y luego se dirige al estado final de esta actividad. Si la calificación es menor a 60, el programa se dirige inmediatamente al estado final sin mostrar ningún mensaje. La instrucción if es una instrucción de control de una sola entrada/una sola salida. Pronto veremos que los diagramas de actividad para las instrucciones de control restantes también contienen estados iniciales, flechas de transición, estados de acción que indican las acciones a realizar, símbolos de decisión (con sus condiciones de guardia asociadas) que indican las decisiones a tomar, y estados finales. Esto es consistente con el modelo de programación acción/decisión que hemos estado enfatizando. Imagine siete cajones, en donde cada uno contiene sólo un tipo de instrucción de control de Java. Todas las instrucciones de control están vacías. Su tarea es ensamblar un programa a partir de tantas instrucciones de control de cada tipo como lo requiera el algoritmo, combinando esas instrucciones de control en sólo dos formas posibles (apilando o anidando), y después llenando los estados de acción y las decisiones con expresiones de acción y condiciones de guardia, en una manera que sea apropiada para el algoritmo. Hablaremos sobre la varie- dad de formas en que pueden escribirse las acciones y las decisiones. imprimir “Aprobado” [calificacion >= 60] [calificacion < 60] Figura 4.2 | Diagrama de actividad en UML de la instrucción if de selección simple. 4.6 Instrucción de selección doble if...else La instrucción if de selección simple realiza una acción indicada solamente cuando la condición es verdadera (true); de no ser así, se evita dicha acción. La instrucción if...else de selección doble permite al programador especificar una acción a realizar cuando la condición es verdadera, y otra distinta cuando la condición es falsa. Por ejemplo, la instrucción en seudocódigo: Si la calificación del estudiante es mayor o igual a 60 Imprimir “Aprobado” De lo contrario Imprimir “Reprobado” 4.6 Instrucción de selección doble if...else 117
  • 156. 118 Capítulo 4 Instrucciones de control: parte 1 imprime “Aprobado” si la calificación del estudiante es mayor o igual a 60, y, “Reprobado” si la calificación del estudiante es menor a 60. En cualquier caso, después de que ocurre la impresión se “ejecuta”, según la secuencia, la siguiente instrucción en seudocódigo. La instrucción anterior if...else en seudocódigo puede escribirse en Java como if ( calificacion >= 60 ) System.out.println( "Aprobado" ); else System.out.println( "Reprobado" ); Observe que el cuerpo de la instrucción else también tiene sangría. Cualquiera que sea la convención de sangría que usted elija, debe aplicarla consistentemente en todos sus programas. Es difícil leer programas que no obede- cen las convenciones de espaciado uniformes. Buena práctica de programación 4.1 Utilice sangría en ambos cuerpos de instrucciones de una estructura if...else. Buena práctica de programación 4.2 Si hay varios niveles de sangría, en cada nivel debe aplicarse la misma cantidad de espacio adicional. La figura 4.3 muestra el flujo de control en la instrucción if...else. Una vez más (además del estado inicial, las flechas de transición y el estado final), los símbolos en el diagrama de actividad de UML representan estados de acción y decisiones. Nosotros seguimos enfatizando este modelo de computación acción/decisión. Ima- gine de nuevo un cajón profundo que contiene tantas instrucciones if...else vacías como sea necesario para crear cualquier programa en Java. Su trabajo es ensamblar estas instrucciones if...else (apilando o anidando) con cualquier otra estructura de control requerida por el algoritmo. Usted debe llenar los estados de acción y los símbolos de decisión con expresiones de acción y condiciones de guardia que sean apropiadas para el algoritmo que esté desarrollando. Operador condicional (?:) Java cuenta con el operador condicional (?:), que en ocasiones puede utilizarse en lugar de una instrucción if...else. Éste es el único operador ternario en Java; es decir, que utiliza tres operandos. En conjunto, los operandos y el símbolo ?: forman una expresión condicional. El primer operando (a la izquierda del ?) es una expresión booleana (es decir, una condición que se evalúa a un valor booleano: true o false), el segundo operando (entre el ? y :) es el valor de la expresión condicional si la expresión booleana es verdadera, y el ter- cer operando (a la derecha de :) es el valor de la expresión condicional si la expresión booleana se evalúa como false. Por ejemplo, la instrucción System.out.println( calificacionEstudiante >= 60 ? "Aprobado" : "Reprobado" ); imprime el valor del argumento de println, que es una expresión condicional. La expresión condicional en esta instrucción produce como resultado la cadena "Aprobado" si la expresión booleana calificacionEstudiante >= 60 es verdadera, o produce como resultado la cadena "Reprobado" si la expresión booleana es falsa. Por lo tanto, esta instrucción con el operador condicional realiza en esencia la misma función que la instrucción if... else que se mostró anteriormente, en esta sección. La precedencia del operador condicional es baja, por lo que toda la expresión condicional se coloca normalmente entre paréntesis. Pronto veremos que las expresiones condi- cionales pueden usarse en algunas situaciones en las que no se pueden utilizar instrucciones if...else. Buena práctica de programación 4.3 Las expresiones condicionales son más difíciles de leer que las instrucciones if...else, por lo cual deben usarse para reemplazar sólo a las instrucciones if...else simples que seleccionan uno de dos valores. Instrucciones if...else anidadas Un programa puede evaluar varios casos colocando instrucciones if...else dentro de otras instrucciones if... else, para crear instrucciones if...else anidadas. Por ejemplo, el siguiente seudocódigo representa una ins-
  • 157. trucción if...else anidada que imprime A para las calificaciones de exámenes mayores o iguales a 90, B para las calificaciones en el rango de 80 a 89, C para las calificaciones en el rango de 70 a 79, D para las calificaciones en el rango de 60 a 69 y F para todas las demás calificaciones: Si la calificación del estudiante es mayor o igual a 90 Imprimir “A” de lo contrario Si la calificación del estudiante es mayor o igual a 80 Imprimir “B” de lo contrario Si la calificación del estudiante es mayor o igual a 70 Imprimir “C” de lo contrario Si la calificación del estudiante es mayor o igual a 60 Imprimir “D” de lo contrario Imprimir “F” Este seudocódigo puede escribirse en Java como if ( calificacionEstudiante >= 90 ) System.out.println( "A" ); else if ( calificacionEstudiante >= 80 ) System.out.println( "B" ); else if ( calificacionEstudiante >= 70 ) System.out.println( "C" ); else if ( calificacionEstudiante >= 60 ) System.out.println( "D" ); else System.out.println( "F" ); Si calificacionEstudiante es mayor o igual a 90, las primeras cuatro condiciones serán verdaderas, pero sólo se ejecutará la instrucción en la parte if de la primera instrucción if...else. Después de que se ejecute esa instrucción, se evita la parte else de la instrucción if...else más “externa”. La mayoría de los programadores en Java prefieren escribir la instrucción if...else anterior así: if ( calificacionEstudiante >= 90 ) System.out.println( "A" ); else if ( calificacionEstudiante >= 80 ) System.out.println( "B" ); else if ( calificacionEstudiante >= 70 ) Imprimir “Aprobado” imprimir “Reprobado” [calificacion >= 60] [calificacion < 60] Figura 4.3 | Diagrama de actividad de UML de la instrucción if...else de selección doble. 4.6 Instrucción de selección doble if...else 119
  • 158. 120 Capítulo 4 Instrucciones de control: parte 1 System.out.println( "C" ); else if ( calificacionEstudiante >= 60 ) System.out.println( "D" ); else System.out.println( "F" ); Las dos formas son idénticas, excepto por el espaciado y la sangría, que el compilador ignora. La segunda forma es más popular ya que evita usar mucha sangría hacia la derecha en el código. Dicha sangría a menudo deja poco espacio en una línea de código, forzando a que las líneas se dividan y empeorando la legibilidad del programa. Problema del else suelto El compilador de Java siempre asocia un else con el if que le precede inmediatamente, a menos que se le indique otra cosa mediante la colocación de llaves ({ y }). Este comportamiento puede ocasionar lo que se conoce como el problema del else suelto. Por ejemplo, if ( x > 5 ) if ( y > 5 ) System.out.println( "x e y son > 5" ); else System.out.println( "x es <= 5" ); parece indicar que si x es mayor que 5, la instrucción if anidada determina si y es también mayor que 5. De ser así, se produce como resultado la cadena "x e y son > 5". De lo contrario, parece ser que si x no es mayor que 5, la instrucción else que es parte del if...else produce como resultado la cadena "x es <= 5". ¡Cuidado! Esta instrucción if...else anidada no se ejecuta como parece ser. El compilador en realidad interpreta la instrucción así: if ( x > 5 ) if ( y > 5 ) System.out.println( "x e y son > 5" ); else System.out.println( "x es <= 5" ); en donde el cuerpo del primer if es un if...else anidado. La instrucción if más externa evalúa si x es mayor que 5. De ser así, la ejecución continúa evaluando si y es también mayor que 5. Si la segunda condición es verda- dera, se muestra la cadena apropiada ("x e y son > 5"). No obstante, si la segunda condición es falsa se muestra la cadena "x es <= 5", aun cuando sabemos que x es mayor que 5. Además, si la condición de la instrucción if exterior es falsa, se omite la instrucción if...else interior y no se muestra nada en pantalla. Para forzar a que la instrucción if...else anidada se ejecute como se tenía pensado originalmente, debe escribirse de la siguiente manera: if ( x > 5 ) { if ( y > 5 ) System.out.println( "x e y son > 5" ); } else System.out.println( "x es <= 5" ); Las llaves ({}) indican al compilador que la segunda instrucción if se encuentra en el cuerpo del primer if, y que el else está asociado con el primer if. Los ejercicios 4.27 y 4.28 analizan con más detalle el problema del else suelto. Bloques La instrucción if normalmente espera sólo una instrucción en su cuerpo. Para incluir varias instrucciones en el cuerpo de un if (o en el cuerpo del else en una instrucción if...else), encierre las instrucciones entre llaves ({ y }). A un conjunto de instrucciones contenidas dentro de un par de llaves se le llama bloque. Un bloque puede colocarse en cualquier parte de un programa en donde pueda colocarse una sola instrucción. El siguiente ejemplo incluye un bloque en la parte else de una instrucción if...else:
  • 159. if ( calificacion >= 60 ) System.out.println( "Aprobado" ); else { System.out.println( "Reprobado." ); System.out.println( "Debe tomar este curso otra vez." ); } En este caso, si calificacion es menor que 60, el programa ejecuta ambas instrucciones en el cuerpo del else e imprime Reprobado. Debe tomar este curso otra vez. Observe las llaves que rodean a las dos instrucciones en la cláusula else. Estas llaves son importantes. Sin ellas, la instrucción System.out.println ( "Debe tomar este curso otra vez." ); estaría fuera del cuerpo de la parte else de la instrucción if...else y se ejecutaría sin importar que la califica- ción fuera menor a 60. Los errores de sintaxis (como cuando se omite una llave en un bloque del programa) los atrapa el compilador. Un error lógico (como cuando se omiten ambas llaves en un bloque del programa) tiene su efecto en tiempo de ejecución. Un error lógico fatal hace que un programa falle y termine antes de tiempo. Un error lógico no fatal permite que un programa siga ejecutándose, pero éste produce resultados incorrectos. Error común de programación 4.1 Olvidar una o las dos llaves que delimitan un bloque puede provocar errores de sintaxis o errores lógicos en un programa. Buena práctica de programación 4.4 Colocar siempre las llaves en una instrucción if...else (o cualquier estructura de control) ayuda a evitar que se omitan de manera accidental, en especial, cuando posteriormente se agregan instrucciones a una cláusula if o else. Para evitar que esto suceda, algunos programadores prefieren escribir la llave inicial y la final de los bloques antes de escribir las instrucciones individuales dentro de ellas. Así como un bloque puede colocarse en cualquier parte en donde pueda colocarse una sola instrucción individual, también es posible no tener instrucción alguna. En la sección 2.8 vimos que la instrucción vacía se representa colocando un punto y coma (;) en donde normalmente iría una instrucción. Error común de programación 4.2 Colocar un punto y coma después de la condición en una instrucción if...else produce un error lógico en las instrucciones if de selección simple, y un error de sintaxis en las instrucciones if...else de selección doble (cuando la parte del if contiene una instrucción en el cuerpo). 4.7 Instrucción de repetición while Una instrucción de repetición (también llamada instrucción de ciclo, o un ciclo) permite al programador especificar que un programa debe repetir una acción mientras cierta condición sea verdadera. La instrucción en seudocódigo Mientras existan más artículos en mi lista de compras Comprar el siguiente artículo y quitarlo de mi lista describe la repetición que ocurre durante una salida de compras. La condición “existan más artículos en mi lista de compras” puede ser verdadera o falsa. Si es verdadera, entonces se realiza la acción “Comprar el siguiente artículo y quitarlo de mi lista”. Esta acción se realizará en forma repetida mientras la condición sea verdadera. La instrucción 4.7 Instrucción de repetición while 121
  • 160. 122 Capítulo 4 Instrucciones de control: parte 1 (o instrucciones) contenida en la instrucción de repetición while constituye el cuerpo de esta estructura, el cual puede ser una sola instrucción o un bloque. En algún momento, la condición será falsa (cuando el último artículo de la lista de compras sea adquirido y eliminado de la lista). En este punto la repetición terminará y se ejecutará la primera instrucción que esté después de la instrucción de repetición. Como ejemplo de la instrucción de repetición while en Java, considere un segmento de programa diseñado para encontrar la primera potencia de 3 que sea mayor a 100. Suponga que la variable producto de tipo int se inicializa en 3. Cuando la siguiente instrucción while termine de ejecutarse, producto contendrá el resultado: int producto = 3; while ( producto <= 100 ) producto = 3 * producto; Cuando esta instrucción while comienza a ejecutarse, el valor de la variable producto es 3. Cada iteración de la instrucción while multiplica a producto por 3, por lo que producto toma los valores de 9, 27, 81 y 243, sucesi- vamente. Cuando la variable producto se vuelve 243, la condición de la instrucción while (producto <= 1000) se torna falsa. Esto termina la repetición, por lo que el valor final de producto es 243. En este punto, la ejecución del programa continúa con la siguiente instrucción después de la instrucción while. Error común de programación 4.3 Si no se proporciona, en el cuerpo de una instrucción while, una acción que ocasione que en algún momento la condición de un while se torne falsa, por lo general, se producirá un error lógico conocido como ciclo infinito, en el que el ciclo nunca terminará. El diagrama de actividad de UML de la figura 4.4 muestra el flujo de control que corresponde a la instruc- ción while anterior. Una vez más (aparte del estado inicial, las flechas de transición, un estado final y tres notas), los símbolos en el diagrama representan un estado de acción y una decisión. Este diagrama también introduce el símbolo de fusión. UML representa tanto al símbolo de fusión como al símbolo de decisión como rombos. El símbolo de fusión une dos flujos de actividad en uno solo. En este diagrama, el símbolo de fusión une las transiciones del estado inicial y del estado de acción, de manera que ambas fluyan en la decisión que determina si el ciclo debe empezar a ejecutarse (o seguir ejecutándose). Los símbolos de decisión y de fusión pueden diferen- ciarse por el número de flechas de transición “entrantes” y “salientes”. Un símbolo de decisión tiene una flecha de transición que apunta hacia el rombo y dos o más flechas de transición que apuntan hacia fuera del rombo, para indicar las posibles transiciones desde ese punto. Además, cada flecha de transición que apunta hacia fuera de un símbolo de decisión tiene una condición de guardia junto a ella. Un símbolo de fusión tiene dos o más flechas de transición que apuntan hacia el rombo, y sólo una flecha de transición que apunta hacia fuera del rombo, para indicar múltiples flujos de actividad que se fusionan para continuar la actividad. Ninguna de las flechas de transición asociadas con un símbolo de fusión tiene una condición de guardia. triplicar valor de producto Instrucción correspondiente en Java: producto = 3 * producto; Decisión [producto <= 100] [producto > 100] Fusión Figura 4.4 | Diagrama de actividad de UML de la instrucción de repetición while.
  • 161. La figura 4.4 muestra claramente la repetición de la instrucción while que vimos antes en esta sección. La flecha de transición que emerge del estado de acción apunta de regreso a la fusión, desde la cual el flujo del programa regresa a la decisión que se evalúa al principio de cada iteración del ciclo. Éste ciclo sigue ejecutándose hasta que la condición de guardia producto > 100 se vuelva verdadera. Entonces, la instrucción while termina (llega a su estado final) y el control pasa a la siguiente instrucción en la secuencia del programa. 4.8 Cómo formular algoritmos: repetición controlada por un contador Para ilustrar la forma en que se desarrollan los algoritmos, modificamos la clase LibroCalificaciones del capítu- lo 3, para resolver dos variantes de un problema que promedia las calificaciones de unos estudiantes. Analicemos el siguiente enunciado del problema: A una clase de diez estudiantes se les aplicó un examen. Las calificaciones (enteros en el rango de 0 a 100) de este examen están disponibles para su análisis. Determine el promedio de la clase para este examen. El promedio de la clase es igual a la suma de las calificaciones, dividida entre el número de estudiantes. El algoritmo para resolver este problema en una computadora debe recibir como entrada cada una de las calificaciones, llevar el registro del total de las calificaciones introducidas, realizar el cálculo para promediar e imprimir el resultado. Algoritmo de seudocódigo con repetición controlada por un contador Emplearemos seudocódigo para enlistar las acciones a ejecutar y especificar el orden en que deben ejecutarse. Usa- remos una repetición controlada por contador para introducir las calificaciones, una por una. Esta técnica utiliza una variable llamada contador (o variable de control) para controlar el número de veces que debe ejecutarse un conjunto de instrucciones. A la repetición controlada por contador se le llama comúnmente repetición definida, ya que el número de repeticiones se conoce antes de que el ciclo comience a ejecutarse. En este ejemplo, la repeti- ción termina cuando el contador excede a 10. Esta sección presenta un algoritmo de seudocódigo (figura 4.5) com- pletamente desarrollado, y una versión de la clase LibroCalificaciones (figura 4.6) que implementa el algoritmo en un método de Java. Después presentamos una aplicación (figura 4.7) que demuestra el algoritmo en acción. En la sección 4.9 demostraremos cómo utilizar el seudocódigo para desarrollar dicho algoritmo desde cero. Observación de ingeniería de software 4.1 La experiencia ha demostrado que la parte más difícil para la resolución de un problema en una computadora es desarrollar el algoritmo para la solución. Por lo general, una vez que se ha especificado el algoritmo correcto, el pro- ceso de producir un programa funcional en Java a partir de dicho algoritmo es relativamente sencillo. Observe las referencias en el algoritmo de la figura 4.5 para un total y un contador. Un total es una variable que se utiliza para acumular la suma de varios valores. Un contador es una variable que se utiliza para contar; en este caso, el contador de calificaciones indica cuál de las 10 calificaciones está a punto de escribir el usuario. Por lo gene- ral, las variables que se utilizan para guardar totales deben inicializarse en cero antes de utilizarse en un programa. Figura 4.5 | Algoritmo en seudocódigo que utiliza la repetición controlada por contador para resolver el problema del promedio de una clase. 1 Asignar a total el valor de cero 2 Asignar al contador de calificaciones el valor de uno 3 4 Mientras que el contador de calificaciones sea menor o igual a diez 5 Pedir al usuario que introduzca la siguiente calificación 6 Obtener como entrada la siguiente calificación 7 Sumar la calificación al total 8 Sumar uno al contador de calificaciones 9 10 Asignar al promedio de la clase el total dividido entre diez 11 Imprimir el promedio de la clase 4.8 Cómo formular algoritmos: repetición controlada por un contador 123
  • 162. 124 Capítulo 4 Instrucciones de control: parte 1 Implementación de la repetición controlada por contador en la clase LibroCalificaciones La clase LibroCalificaciones (figura 4.6) contiene un constructor (líneas 11-14) que asigna un valor a la varia- ble de instancia nombreDelCurso (declarada en la línea 8) de la clase. Las líneas 17 a la 20, 23 a la 26 y 29 a la 34 declaran los métodos establecerNombreDelCurso, obtenerNombreDelCurso y mostrarMensaje, respecti- vamente. Las líneas 37 a la 66 declaran el método determinarPromedioClase, el cual implementa el algoritmo para sacar el promedio de la clase, descrito por el seudocódigo de la figura 4.5. La línea 40 declara e inicializa la variable entrada de tipo Scanner, que se utiliza para leer los valores intro- ducidos por el usuario. Las líneas 42 a 45 declaran las variables locales total, contadorCalif, calificacion y promedio de tipo int. La variable calificacion almacena la entrada del usuario. Observe que las declaraciones (en las líneas 42 a la 45) aparecen en el cuerpo del método determinar PromedioClase. Recuerde que las variables declaradas en el cuerpo de un método son variables locales, y sólo pueden utilizarse desde la línea de su declaración en el método, hasta la llave derecha de cierre (}) de la declaración del método. La declaración de una variable local debe aparecer antes de que la variable se utilice en ese método. Una variable local no puede utilizarse fuera del método en el que se declara. En las versiones de la clase LibroCalificaciones en este capítulo, simplemente leemos y procesamos un conjunto de calificaciones. El cálculo del promedio se realiza en el método determinarPromedioClase, usando variables locales; no preservamos información acerca de las calificaciones de los estudiantes en variables de ins- tancia de la clase. En versiones posteriores de la clase (en el capítulo 7, Arreglos), mantenemos las calificaciones en memoria utilizando una variable de instancia que hace referencia a una estructura de datos conocida como arreglo. Esto permite que un objeto LibroCalificaciones realice varios cálculos sobre el mismo conjunto de calificaciones, sin requerir que el usuario escriba las calificaciones varias veces. Buena práctica de programación 4.5 Separe las declaraciones de las otras instrucciones en los métodos con una línea en blanco, para mejorar la legibilidad. Figura 4.6 | Repetición controlada por contador: Problema del promedio de una clase. (Parte 1 de 2). 1 // Fig. 4.6: LibroCalificaciones.java 2 // La clase LibroCalificaciones que resuelve el problema del promedio de 3 // la clase, usando la repetición controlada por un contador. 4 import java.util.Scanner; // el programa utiliza la clase Scanner 5 6 public class LibroCalificaciones 7 { 8 private String nombreDelCurso; // el nombre del curso que representa este LibroCalificaciones 9 10 // el constructor inicializa a nombreDelCurso 11 public LibroCalificaciones( String nombre ) 12 { 13 nombreDelCurso = nombre; // inicializa a nombreDelCurso 14 } // fin del constructor 15 16 // método para establecer el nombre del curso 17 public void establecerNombreDelCurso( String nombre ) 18 { 19 nombreDelCurso = nombre; // almacena el nombre del curso 20 } // fin del método establecerNombreDelCurso 21 22 // método para obtener el nombre del curso 23 public String obtenerNombreDelCurso() 24 { 25 return nombreDelCurso; 26 } // fin del método obtenerNombreDelCurso 27 28 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones
  • 163. Las asignaciones (en las líneas 48 y 49) inicializan total a 0 y contadorCalif a 1. Observe que estas ini- cializaciones ocurren antes que se utilicen las variables en los cálculos. Las variables calificacion y promedio (para la entrada del usuario y el promedio calculado, respectivamente) no necesitan inicializarse aquí; sus valores se asignarán a medida que se introduzcan o calculen más adelante en el método. Error común de programación 4.4 Leer el valor de una variable local antes de inicializarla produce un error de compilación. Todas las variables locales deben inicializarse antes de leer sus valores en las expresiones. Tip para prevenir errores 4.1 Inicialice cada contador y total, ya sea en su declaración o en una instrucción de asignación. Por lo general, los totales se inicializan a 0. Los contadores comúnmente se inicializan a 0 o a 1, dependiendo de cómo se utilicen (más adelante veremos ejemplos de cuándo usar 0 y cuándo usar 1). Figura 4.6 | Repetición controlada por contador: Problema del promedio de una clase. (Parte 2 de 2). 29 public void mostrarMensaje() 30 { 31 // obtenerNombreDelCurso obtiene el nombre del curso 32 System.out.printf( "Bienvenido al libro de calificaciones paran%s!nn", 33 obtenerNombreDelCurso() ); 34 } // fin del método mostrarMensaje 35 36 // determina el promedio de la clase, con base en las 10 calificaciones introducidas por el usuario 37 public void determinarPromedioClase() 38 { 39 // crea objeto Scanner para obtener la entrada de la ventana de comandos 40 Scanner entrada = new Scanner( System.in ); 41 42 int total; // suma de las calificaciones escritas por el usuario 43 int contadorCalif; // número de la siguiente calificación a introducir 44 int calificacion; // valor de la calificación escrita por el usuario 45 int promedio; // el promedio de las calificaciones 46 47 // fase de inicialización 48 total = 0; // inicializa el total 49 contadorCalif = 1; // inicializa el contador del ciclo 50 51 // fase de procesamiento 52 while ( contadorCalif <= 10 ) // itera 10 veces 53 { 54 System.out.print( "Escriba la calificación: " ); // indicador 55 calificacion = entrada.nextInt(); // lee calificación del usuario 56 total = total + calificacion; // suma calificación a total 57 contadorCalif = contadorCalif + 1; // incrementa contador en 1 58 } // fin de while 59 60 // fase de terminación 61 promedio = total / 10; // la división entera produce un resultado entero 62 63 // muestra el total y el promedio de las calificaciones 64 System.out.printf( "nEl total de las 10 calificaciones es %dn", total ); 65 System.out.printf( "El promedio de la clase es %dn", promedio ); 66 } // fin del método determinarPromedioClase 67 68 } // fin de la clase LibroCalificaciones 4.8 Cómo formular algoritmos: repetición controlada por un contador 125
  • 164. 126 Capítulo 4 Instrucciones de control: parte 1 La línea 52 indica que la instrucción while debe continuar ejecutando el ciclo (lo que también se conoce como iterar), siempre y cuando el valor de contadorCalif sea menor o igual a 10. Mientras esta condición sea verdadera, la instrucción while ejecutará en forma repetida las instrucciones entre las llaves que delimitan su cuerpo (líneas 54 a la 57). La línea 54 muestra el indicador "Escriba la calificacion: ". La línea 55 lee el dato escrito por el usua- rio y lo asigna a la variable calificacion. Después, la línea 56 suma la nueva calificación escrita por el usuario al total, y asigna el resultado a total, que sustituye su valor anterior. La línea 57 suma 1 a contadorCalif para indicar que el programa ha procesado una calificación y está listo para recibir la siguiente calificación del usuario. Al incrementar a contadorCalif en cada iteración, en un momento dado su valor excederá a 10. En ese momento, el ciclo while termina debido a que su condición (línea 52) se vuelve falsa. Cuando el ciclo termina, la línea 61 realiza el cálculo del promedio y asigna su resultado a la variable prome- dio. La línea 64 utiliza el método printf de System.out para mostrar el texto "El total de las 10 cali- ficaciones es ", seguido del valor de la variable total. Después, la línea 65 utiliza a printf para mostrar el texto "El promedio de la clase es ", seguido del valor de la variable promedio. Después de llegar a la línea 66, el método determinarPromedioClase devuelve el control al método que hizo la llamada (es decir, a main en PruebaLibroCalificaciones de la figura 4.7). La clase PruebaLibroCalificaciones La clase PruebaLibroCalificaciones (figura 4.7) crea un objeto de la clase LibroCalificaciones (figura 4.6) y demuestra sus capacidades. Las líneas 10 y 11 de la figura 4.7 crean un nuevo objeto LibroCalificacio- nes y lo asignan a la variable miLibroCalificaciones. El objeto String en la línea 11 se pasa al constructor de LibroCalificaciones (líneas 11 a la 14 de la figura 4.6). La línea 13 llama al método mostrarMensaje de miLibroCalificaciones para mostrar un mensaje de bienvenida al usuario. Después, la línea 14 llama al método determinarPromedioClase de miLibroCalificaciones para permitir que el usuario introduzca 10 calificacio- nes, para las cuales el método posteriormente calcula e imprime el promedio; el método ejecuta el algoritmo que se muestra en la figura 4.5. Observaciones acerca de la división de enteros y el truncamiento El cálculo del promedio realizado por el método determinarPromedioClase, en respuesta a la llamada al méto- do en la línea 14 de la figura 4.7, produce un resultado entero. La salida del programa indica que la suma de los valores de las calificaciones en la ejecución de ejemplo es 846, que al dividirse entre 10, debe producir el número de punto flotante 84.6. Sin embargo, el resultado del cálculo total / 10 (línea 61 de la figura 4.6) es el entero 1 // Fig. 4.7: PruebaLibroCalificaciones.java 2 // Crea un objeto LibroCalificaciones e invoca a su método obtenerPromedioClase. 3 4 public class PruebaLibroCalificaciones 5 { 6 public static void main( String args[] ) 7 { 8 // crea objeto miLibroCalificaciones de la clase LibroCalificaciones y 9 // pasa el nombre del curso al constructor 10 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones( 11 "CS101 Introducción a la programación en Java" ); 12 13 miLibroCalificaciones.mostrarMensaje(); // muestra mensaje de bienvenida 14 miLibroCalificaciones.determinarPromedioClase(); // encuentra el promedio de 10 calificaciones 15 } // fin de main 16 17 } // fin de la clase PruebaLibroCalificaciones Figura 4.7 | La clase PruebaLibroCalificaciones crea un objeto de la clase LibroCalificaciones (figura 4.6) e invoca a su método determinarPromedioClase. (Parte 1 de 2).
  • 165. 84, ya que total y 10 son enteros. Al dividir dos enteros se produce una división entera: se pierde cualquier parte fraccionaria del cálculo (es decir, se trunca). En la siguiente sección veremos cómo obtener un resultado de punto flotante a partir del cálculo del promedio. Error común de programación 4.5 Suponer que la división entera redondea (en vez de truncar) puede producir resultados erróneos. Por ejemplo, 7 ÷ 4, que produce 1.75 en la aritmética convencional, se trunca a 1 en la aritmética entera, en vez de redondearse a 2. 4.9 Cómo formular algoritmos: repetición controlada por un centinela Generalicemos el problema, de la sección 4.8, para los promedios de una clase. Considere el siguiente problema: Desarrollar un programa que calcule el promedio de una clase y procese las calificaciones para un número arbitrario de estudiantes cada vez que se ejecute. En el ejemplo anterior del promedio de una clase, el enunciado del problema especificó el número de estudiantes (10). En este ejemplo no se indica cuántas calificaciones introducirá el usuario durante la ejecución del programa. El programa debe procesar un número arbitrario de calificaciones. ¿Cómo puede el programa determinar cuándo terminar de introducir calificaciones? ¿Cómo sabrá cuándo calcular e imprimir el promedio de la clase? Una manera de resolver este problema es utilizar un valor especial denominado valor centinela (también llamado valor de señal, valor de prueba o valor de bandera) para indicar el “fin de la introducción de datos”. El usuario escribe calificaciones hasta que se haya introducido el número correcto de ellas. Después, el usuario escribe el valor centinela para indicar que no se van a introducir más calificaciones. A la repetición controlada por centinela a menudo se le llama repetición indefinida, ya que el número de repeticiones no se conoce antes de que comience la ejecución del ciclo. Evidentemente, debe elegirse un valor centinela de tal forma que no pueda confundirse con un valor de entrada permitido. Las calificaciones de un examen son enteros positivos, por lo que –1 es un valor centinela aceptable para este problema. Por lo tanto, una ejecución del programa para promediar una clase podría procesar una cadena de entradas como 95, 96, 75, 74, 89 y –1. El programa entonces calcularía e imprimiría el prome- dio de la clase para las calificaciones 95, 96, 75, 74 y 89; como –1 es el valor centinela, no debe entrar en el cálculo del promedio. Error común de programación 4.6 Seleccionar un valor centinela que sea también un valor de datos permitido es un error lógico. Figura 4.7 | La clase PruebaLibroCalificaciones crea un objeto de la clase LibroCalificaciones (figura 4.6) e invoca a su método determinarPromedioClase. (Parte 2 de 2). Bienvenido al libro de calificaciones para CS101 Introduccion a la programacion en Java! Escriba la calificacion: 67 Escriba la calificacion: 78 Escriba la calificacion: 89 Escriba la calificacion: 67 Escriba la calificacion: 87 Escriba la calificacion: 98 Escriba la calificacion: 93 Escriba la calificacion: 85 Escriba la calificacion: 82 Escriba la calificacion: 100 El total de las 10 calificaciones es 846 El promedio de la clase es 84 4.9 Cómo formular algoritmos: repetición controlada por un centinela 127
  • 166. 128 Capítulo 4 Instrucciones de control: parte 1 Desarrollo del algoritmo en seudocódigo con el método de refinamiento de arriba a abajo, paso a paso: el primer refinamiento (cima) Desarrollamos el programa para promediar clases con una técnica llamada refinamiento de arriba a abajo, paso a paso, la cual es esencial para el desarrollo de programas bien estructurados. Comenzamos con una representa- ción en seudocódigo de la cima, una sola instrucción que transmite la función del programa en general: Determinar el promedio de la clase para el examen La cima es, en efecto, la representación completa de un programa. Desafortunadamente, la cima pocas veces trans- mite los detalles suficientes como para escribir un programa en Java. Por lo tanto, ahora comenzaremos el proceso de refinamiento. Dividiremos la cima en una serie de tareas más pequeñas y las enlistaremos en el orden en el que se van a realizar. Esto arroja como resultado el siguiente primer refinamiento: Inicializar variables Introducir, sumar y contar las calificaciones del examen Calcular e imprimir el promedio de la clase Esta mejora utiliza sólo la estructura de secuencia; los pasos aquí mostrados deben ejecutarse en orden, uno des- pués del otro. Observación de ingeniería de software 4.2 Cada mejora, así como la cima en sí, es una especificación completa del algoritmo; sólo varía el nivel del detalle. Observación de ingeniería de software 4.3 Muchos programas pueden dividirse lógicamente en tres fases: de inicialización, en donde se inicializan las variables; procesamiento, en donde se introducen los valores de los datos y se ajustan las variables del programa (como contado- res y totales) según sea necesario; y una fase de terminación, que calcula y produce los resultados finales. Cómo proceder al segundo refinamiento La anterior Observación de ingeniería de software es a menudo todo lo que usted necesita para el primer refi- namiento en el proceso de arriba a abajo. Para avanzar al siguiente nivel de refinamiento, es decir, el segundo refinamiento, nos comprometemos a usar variables específicas. En este ejemplo necesitamos el total actual de los números, una cuenta de cuántos números se han procesado, una variable para recibir el valor de cada calificación, a medida que el usuario las vaya introduciendo, y una variable para almacenar el promedio calculado. La instruc- ción en seudocódigo Inicializar las variables puede mejorarse como sigue: Inicializar total en cero Inicializar contador en cero Sólo las variables total y contador necesitan inicializase antes de que puedan utilizarse. Las variables promedio y calificacion (para el promedio calculado y la entrada del usuario, respectivamente) no necesitan inicializarse, ya que sus valores se reemplazarán a medida que se calculen o introduzcan. La instrucción en seudocódigo Introducir, sumar y contar las calificaciones del examen requiere una estructura de repetición (es decir, un ciclo) que introduzca cada calificación en forma sucesiva. No sabemos de antemano cuántas calificaciones van a procesarse, por lo que utilizaremos la repetición controlada por centinela. El usuario introduce las calificaciones una por una; después de introducir la última calificación, intro- duce el valor centinela. El programa evalúa el valor centinela después de la introducción de cada calificación, y ter- mina el ciclo cuando el usuario introduce el valor centinela. Entonces, la segunda mejora de la instrucción anterior en seudocódigo sería
  • 167. Pedir al usuario que introduzca la primera calificación Recibir como entrada la primera calificación (puede ser el centinela) Mientras el usuario no haya introducido aún el centinela Sumar esta calificación al total actual Sumar uno al contador de calificaciones Pedir al usuario que introduzca la siguiente calificación Recibir como entrada la siguiente calificación (puede ser el centinela) En seudocódigo no utilizamos llaves alrededor de las instrucciones que forman el cuerpo de la estructura Mientras. Simplemente aplicamos sangría a las instrucciones bajo el Mientras para mostrar que pertenecen a esta instruc- ción. De nuevo, el seudocódigo es solamente una herramienta informal para desarrollar programas. La instrucción en seudocódigo Calcular e imprimir el promedio de la clase puede mejorarse de la siguiente manera: Si el contador no es igual a cero Asignar al promedio el total dividido entre el contador Imprimir el promedio De lo contrario Imprimir “No se introdujeron calificaciones” Aquí tenemos cuidado de evaluar la posibilidad de una división entre cero; por lo general, esto es un error lógico que, si no se detecta, haría que el programa fallara o produjera resultados inválidos. El segundo refinamiento completo del seudocódigo para el problema del promedio de una clase se muestra en la figura 4.8. Tip para prevenir errores 4.2 Al realizar una división entre una expresión cuyo valor pudiera ser cero, debe evaluar explícitamente esta posibilidad y manejarla de manera apropiada en su programa (como imprimir un mensaje de error), en vez de permitir que ocurra el error. En las figuras 4.5 y 4.8 incluimos algunas líneas en blanco y sangría en el seudocódigo para facilitar su lec- tura. Las líneas en blanco separan los algoritmos en seudocódigo en sus diversas fases y accionan las instrucciones de control; la sangría enfatiza los cuerpos de las estructuras de control. 1 Inicializar total en cero 2 Inicializar contador en cero 3 4 Pedir al usuario que introduzca la primera calificación 5 Recibir como entrada la primera calificación (puede ser el centinela) 6 7 Mientras el usuario no haya introducido aún el centinela 8 Sumar esta calificación al total actual 9 Sumar uno al contador de calificaciones 10 Pedir al usuario que introduzca la siguiente calificación 11 Recibir como entrada la siguiente calificación (puede ser el centinela) 12 13 Si el contador no es igual a cero 14 Asignar al promedio el total dividido entre el contador 15 Imprimir el promedio 16 De lo contrario 17 Imprimir “No se introdujeron calificaciones” Figura 4.8 | Algoritmo en seudocódigo del problema para promediar una clase, con una repetición controlada por centinela. 4.9 Cómo formular algoritmos: repetición controlada por un centinela 129
  • 168. 130 Capítulo 4 Instrucciones de control: parte 1 El algoritmo en seudocódigo en la figura 4.8 resuelve el problema más general para promediar una clase. Este algoritmo se desarrolló después de aplicar dos niveles de refinamiento. En ocasiones se requieren más niveles de refinamiento. Observación de ingeniería de software 4.4 Termine el proceso de refinamiento de arriba a abajo, paso a paso, cuando haya especificado el algoritmo en seudo- código con el detalle suficiente como para poder convertir el seudocódigo en Java. Por lo general, la implementación del programa en Java después de esto es mucho más sencilla. Observación de ingeniería de software 4.5 Algunos programadores experimentados escriben programas sin utilizar herramientas de desarrollo de programas como el seudocódigo. Estos programadores sienten que su meta final es resolver el problema en una computadora y que el escribir seudocódigo simplemente retarda la producción de los resultados finales. Aunque este método pudiera funcionar para problemas sencillos y conocidos, tiende a ocasionar graves errores y retrasos en proyectos grandes y complejos. Implementación de la repetición controlada por centinela en la clase LibroCalificaciones La figura 4.9 muestra la clase de Java LibroCalificaciones que contiene el método determinarPromedio- Clase, el cual implementa el algoritmo, de la figura 4.8, en seudocódigo. Aunque cada calificación es un valor entero, existe la probabilidad de que el cálculo del promedio produzca un número con un punto decimal; en otras palabras, un número real (es decir, de punto flotante). El tipo int no puede representar un número de este tipo, por lo que esta clase utiliza el tipo double para ello. En este ejemplo vemos que las estructuras de control pueden apilarse una encima de otra (en secuencia), al igual que un niño apila bloques de construcción. La instrucción while (líneas 57 a 65) va seguida por una ins- trucción if...else (líneas 69 a 80) en secuencia. La mayor parte del código en este programa es igual al código de la figura 4.6, por lo que nos concentraremos en los nuevos conceptos. La línea 45 declara la variable promedio de tipo double, la cual nos permite guardar el promedio de la clase como un número de punto flotante. La línea 49 inicializa contadorCalif en 0, ya que todavía no se han intro- ducido calificaciones. Recuerde que este programa utiliza la repetición controlada por centinela para recibir las calificaciones que escribe el usuario. Para mantener un registro preciso del número de calificaciones introducidas, el programa incrementa contadorCalif sólo cuando el usuario introduce un valor permitido para la califica- ción. 1 // Fig. 4.9: LibroCalificaciones.java 2 // La clase LibroCalificaciones resuelve el problema del promedio de la clase 3 // usando la repetición controlada por un centinela. 4 import java.util.Scanner; // el programa usa la clase Scanner 5 6 public class LibroCalificaciones 7 { 8 private String nombreDelCurso; // el nombre del curso que representa este LibroCalificaciones 9 10 // el constructor inicializa a nombreDelCurso 11 public LibroCalificaciones( String nombre ) 12 { 13 nombreDelCurso = nombre; // inicializa a nombreDelCurso 14 } // fin del constructor 15 16 // método para establecer el nombre del curso 17 public void establecerNombreDelCurso( String nombre ) 18 { Figura 4.9 | Repetición controlada por centinela: problema del promedio de una clase. (Parte 1 de 3).
  • 169. Figura 4.9 | Repetición controlada por centinela: problema del promedio de una clase. (Parte 2 de 3). 19 nombreDelCurso = nombre; // almacena el nombre del curso 20 } // fin del método establecerNombreDelCurso 21 22 // método para obtener el nombre del curso 23 public String obtenerNombreDelCurso() 24 { 25 return nombreDelCurso; 26 } // fin del método obtenerNombreDelCurso 27 28 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones 29 public void mostrarMensaje() 30 { 31 // obtenerNombreDelCurso obtiene el nombre del curso 32 System.out.printf( "Bienvenido al libro de calificaciones paran%s!nn", 33 obtenerNombreDelCurso() ); 34 } // fin del método mostrarMensaje 35 36 // determina el promedio de un número arbitrario de calificaciones 37 public void determinarPromedioClase() 38 { 39 // crea objeto Scanner para obtener la entrada de la ventana de comandos 40 Scanner entrada = new Scanner( System.in ); 41 42 int total; // suma de las calificaciones 43 int contadorCalif; // número de calificaciones introducidas 44 int calificacion; // valor de calificación 45 double promedio; // número con punto decimal para el promedio 46 47 // fase de inicialización 48 total = 0; // inicializa el total 49 contadorCalif = 0; // inicializa el contador del ciclo 50 51 // fase de procesamiento 52 // pide entrada y lee calificación del usuario 53 System.out.print( "Escriba calificacion o -1 para terminar: " ); 54 calificacion = entrada.nextInt(); 55 56 // itera hasta leer el valor centinela del usuario 57 while ( calificacion != -1 ) 58 { 59 total = total + calificacion; // suma calificacion al total 60 contadorCalif = contadorCalif + 1; // incrementa el contador 61 62 // pide entrada y lee siguiente calificación del usuario 63 System.out.print( "Escriba calificacion o -1 para terminar: " ); 64 calificacion = entrada.nextInt(); 65 } // fin de while 66 67 // fase de terminación 68 // si el usuario introdujo por lo menos una calificación... 69 if ( contadorCalif != 0 ) 70 { 71 // calcula el promedio de todas las calificaciones introducidas 72 promedio = (double) total / contadorCalif; 73 74 // muestra el total y el promedio (con dos dígitos de precisión) 75 System.out.printf( "nEl total de las %d calificaciones introducidas es %dn", 76 contadorCalif, total ); 77 System.out.printf( "El promedio de la clase es %.2fn", promedio ); 4.9 Cómo formular algoritmos: repetición controlada por un centinela 131
  • 170. 132 Capítulo 4 Instrucciones de control: parte 1 Figura 4.9 | Repetición controlada por centinela: problema del promedio de una clase. (Parte 3 de 3). Comparación entre la lógica del programa para la repetición controlada por centinela, y la repetición controlada por contador Compare la lógica de esta aplicación para la repetición controlada por centinela con la repetición controlada por contador en la figura 4.6. En la repetición controlada por contador, cada iteración de la instrucción while (líneas 52 a 58 de la figura 4.6) lee un valor del usuario, para el número especificado de iteraciones. En la repetición controlada por centinela, el programa lee el primer valor (líneas 53 y 54 de la figura 4.9) antes de llegar al whi- le. Este valor determina si el flujo de control del programa debe entrar al cuerpo del while. Si la condición del while es falsa, el usuario introdujo el valor centinela, por lo que el cuerpo del while no se ejecuta (es decir, no se introdujeron calificaciones). Si, por otro lado, la condición es verdadera, el cuerpo comienza a ejecutarse y el ciclo suma el valor de calificacion al total (línea 59). Después, las líneas 63 y 64 en el cuerpo del ciclo reciben el siguiente valor escrito por el usuario. A continuación, el control del programa se acerca a la llave derecha de terminación (}) del cuerpo del ciclo en la línea 65, por lo que la ejecución continúa con la evaluación de la con- dición del while (línea 57). La condición utiliza el valor más reciente de calificacion que acaba de introducir el usuario, para determinar si el cuerpo de la instrucción while debe ejecutarse otra vez. Observe que el valor de la variable calificacion siempre lo introduce el usuario inmediatamente antes de que el programa evalúe la condición del while. Esto permite al programa determinar si el valor que acaba de introducir el usuario es el valor centinela, antes de que el programa procese ese valor (es decir, que lo sume al total). Si el valor introducido es el valor centinela, el ciclo termina y el programa no suma –1 al total. Buena práctica de programación 4.6 En un ciclo controlado por centinela, los indicadores que solicitan la introducción de datos deben recordar explícita- mente al usuario el valor que representa al centinela. Una vez que termina el ciclo se ejecuta la instrucción if...else en las líneas 69 a 80. La condición en la línea 69 determina si se introdujeron calificaciones o no. Si no se introdujo ninguna, se ejecuta la parte del else (líneas 79 y 80) de la instrucción if...else y muestra el mensaje "No se introdujeron calificaciones", y el método devuelve el control al método que lo llamó. Observe el bloque de la instrucción while en la figura 4.9 (líneas 58 a 65). Sin las llaves, el ciclo considera- ría que su cuerpo sólo consiste en la primera instrucción, que suma la calificacion al total. Las últimas tres instrucciones en el bloque quedarían fuera del cuerpo del ciclo, ocasionando que la computadora interpretara el código incorrectamente, como se muestra a continuación: while ( calificacion != -1 ) total = total + calificacion; // suma calificación al total contadorCalif = contadorCalif + 1; // incrementa el contador // obtiene como entrada la siguiente calificación del usuario System.out.print( "Escriba calificacion o –1 para terminar: " ); calificacion = entrada.nextInt(); El código anterior ocasionaría un ciclo infinito en el programa, si el usuario no introduce el centinela –1 como valor de entrada en la línea 54 (antes de la instrucción while). 78 } // fin de if 79 else // no se introdujeron calificaciones, por lo que se imprime el mensaje apropiado 80 System.out.println( "No se introdujeron calificaciones" ); 81 } // fin del método determinarPromedioClase 82 83 } // fin de la clase LibroCalificaciones
  • 171. Error común de programación 4.7 Omitir las llaves que delimitan a un bloque puede provocar errores lógicos, como ciclos infinitos. Para prevenir este problema, algunos programadores encierran el cuerpo de todas las instrucciones de control con llaves, aun si el cuerpo sólo contiene una instrucción. Conversión explícita e implícita entre los tipos primitivos Si se introdujo por lo menos una calificación, la línea 72 de la figura 4.9 calcula el promedio de las calificaciones. En la figura 4.6 vimos que la división entera produce un resultado entero. Aun y cuando la variable promedio se declara como double (línea 45), el cálculo promedio = total / contadorCalif; descarta la parte fraccionaria del cociente antes de asignar el resultado de la división a promedio. Esto ocurre de- bido a que total y contadorCalif son enteros, y la división entera produce un resultado entero. Para realizar un cálculo de punto flotante con valores enteros, debemos tratar temporalmente a estos valores como números de punto flotante, para usarlos en el cálculo. Java cuenta con el operador unario de conversión de tipo para llevar a cabo esta tarea. La línea 72 utiliza el operador de conversión de tipo (double) (un operador unario) para crear una copia de punto flotante temporal de su operando total (que aparece a la derecha del operador). Utilizar un operador de conversión de tipo de esta forma es un proceso que se denomina conversión explícita. El valor almacenado en total sigue siendo un entero. El cálculo ahora consiste de un valor de punto flotante (la versión temporal double de total) dividido entre el entero contadorCalif. Java sabe cómo evaluar sólo expresiones aritméticas en las que los tipos de los operandos sean idénticos. Para asegurar que los operandos sean del mismo tipo, Java realiza una operación lla- mada promoción (o conversión implícita) en los operandos seleccionados. Por ejemplo, en una expresión que contenga valores de los tipos int y double, los valores int son promovidos a valores double para utilizarlos en la expresión. En este ejemplo, Java promueve el valor de contadorCalif al tipo double, después el programa realiza la división de punto flotante y asigna el resultado del cálculo a promedio. Mientras que se aplique el operador de conversión de tipo (double) a cualquier variable en el cálculo, éste producirá un resultado double. Más adelante en el capítulo, hablaremos sobre todos los tipos primitivos. En la sección 6.7 aprenderá más acerca de las reglas de promoción. Error común de programación 4.8 El operador de conversión de tipo puede utilizarse para convertir entre los tipos numéricos primitivos, como int y double, y para convertir entre los tipos de referencia relacionados (como lo describiremos en el capítulo 10, Progra- mación orientada a objetos: polimorfismo). La conversión al tipo incorrecto puede ocasionar errores de compilación o errores en tiempo de ejecución. Los operadores de conversión de tipo están disponibles para cualquier tipo. El operador de conversión se forma colocando paréntesis alrededor del nombre de un tipo. Este operador es un operador unario (es decir, un operador que utiliza sólo un operando). En el capítulo 2 estudiamos los operadores aritméticos binarios. Java también soporta las versiones unarias de los operadores de suma (+) y resta (-), por lo que el programador puede escribir expresiones como –7 o +5. Los operadores de conversión de tipo se asocian de derecha a izquierda y tienen la misma precedencia que los demás operadores unarios, como + y –. Esta precedencia es un nivel mayor que la de los operadores de multiplicación *, / y %. (Consulte la tabla de precedencia de operadores en el apéndice A). En nuestras tablas de precedencia, indicamos el operador de conversión de tipos con la notación (tipo) para indicar que puede usarse cualquier nombre de tipo para formar un operador de conversión de tipo. La línea 77 imprime el promedio de la clase, usando el método printf de System.out. En este ejemplo mostramos el promedio de la clase redondeado a la centésima más cercana. El especificador de formato %.2f en la cadena de control de formato de printf (línea 77) indica que el valor de la variable promedio debe mostrarse con dos dígitos de precisión a la derecha del punto decimal; esto se indica mediante el .2 en el especificador de formato. Las tres calificaciones introducidas durante la ejecución de ejemplo de la clase PruebaLibroCalifica- ciones (figura 4.10) dan un total de 257, que produce el promedio de 85.666666…. El método printf utiliza la precisión en el especificador de formato para redondear el valor al número especificado de dígitos. En este programa, el promedio se redondea a la posición de las centésimas y se muestra como 85.67. 4.9 Cómo formular algoritmos: repetición controlada por un centinela 133
  • 172. 134 Capítulo 4 Instrucciones de control: parte 1 4.10 Cómo formular algoritmos: instrucciones de control anidadas En el siguiente ejemplo formularemos una vez más un algoritmo utilizando seudocódigo y el refinamiento de arriba a abajo, paso a paso, y después escribiremos el correspondiente programa en Java. Hemos visto que las ins- trucciones de control pueden apilarse una encima de otra (en secuencia). En este ejemplo práctico examinaremos la otra forma en la que pueden conectarse las instrucciones de control, a saber, mediante el anidamiento de una instrucción de control dentro de otra. Considere el siguiente enunciado de un problema: Una universidad ofrece un curso que prepara a los estudiantes para el examen estatal de certificación del estado como corre- dores de bienes raíces. El año pasado, diez de los estudiantes que completaron este curso tomaron el examen. La universidad desea saber qué tan bien se desempeñaron sus estudiantes en el examen. A usted se le ha pedido que escriba un programa para sintetizar los resultados. Se le dio una lista de estos 10 estudiantes. Junto a cada nombre hay un 1 escrito, si el estu- diante aprobó el examen, o un 2 si lo reprobó. Su programa debe analizar los resultados del examen de la siguiente manera: 1. Introducir cada resultado de la prueba (es decir, un 1 o un 2). Mostrar el mensaje “Escriba el resultado” en la pan- talla, cada vez que el programa solicite otro resultado de la prueba. 2. Contar el número de resultados de la prueba, de cada tipo. 3. Mostrar un resumen de los resultados de la prueba, indicando el número de estudiantes que aprobaron y el número de estudiantes que reprobaron. 4. Si más de ocho estudiantes aprobaron el examen, imprimir el mensaje “Aumentar la colegiatura”. 1 // Fig. 4.10: PruebaLibroCalificaciones.java 2 // Crea un objeto LibroCalificaciones e invoca a su método determinarPromedioClase. 3 4 public class PruebaLibroCalificaciones 5 { 6 public static void main( String args[] ) 7 { 8 // crea objeto miLibroCalificaciones de LibroCalificaciones y 9 // pasa el nombre del curso al constructor 10 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones( 11 "CS101 Introduccion a la programacion en Java" ); 12 13 miLibroCalificaciones.mostrarMensaje(); // muestra mensaje de bienvenida 14 miLibroCalificaciones.determinarPromedioClase(); // encuentra el promedio de las calificaciones 15 } // fin de main 16 17 } // fin de la clase PruebaLibroCalificaciones Figura 4.10 | La clase PruebaLibroCalificaciones crea un objeto de la clase LibroCalificaciones (figura 4.9) e invoca al método determinarPromedioClase. Bienvenido al libro de calificaciones para CS101 Introduccion a la programacion en Java! Escriba calificacion o -1 para terminar: 97 Escriba calificacion o -1 para terminar: 88 Escriba calificacion o -1 para terminar: 72 Escriba calificacion o -1 para terminar: -1 El total de las 3 calificaciones introducidas es 257 El promedio de la clase es 85.67
  • 173. Después de leer el enunciado del programa cuidadosamente, hacemos las siguientes observaciones: 1. El programa debe procesar los resultados de la prueba para 10 estudiantes. Puede usarse un ciclo contro- lado por contador, ya que el número de resultados de la prueba se conoce de antemano. 2. Cada resultado de la prueba tiene un valor numérico, ya sea 1 o 2. Cada vez que el programa lee un resultado de la prueba, debe determinar si el número es 1 o 2. Nosotros evaluamos un 1 en nuestro algoritmo. Si el número no es 1, suponemos que es un 2. (El ejercicio 4.24 considera las consecuencias de esta suposición). 3. Dos contadores se utilizan para llevar el registro de los resultados del examen: uno para contar el número de estudiantes que aprobaron el examen y uno para contar el número de estudiantes que reprobaron el examen. 4. Una vez que el programa ha procesado todos los resultados, debe decidir si más de ocho estudiantes aprobaron el examen. Veamos ahora el refinamiento de arriba a abajo, paso a paso. Comencemos con la representación del seudo- código de la cima: Analizar los resultados del examen y decidir si debe aumentarse la colegiatura o no. Una vez más, la cima es una representación completa del programa, pero es probable que se necesiten varios refi- namientos antes de que el seudocódigo pueda evolucionar de manera natural en un programa en Java. Nuestro primer refinamiento es Inicializar variables Introducir las 10 calificaciones del examen y contar los aprobados y reprobados Imprimir un resumen de los resultados del examen y decidir si debe aumentarse la colegiatura Aquí también, aun cuando tenemos una representación completa del programa, es necesario refinarla. Ahora nos comprometemos con variables específicas. Se necesitan contadores para registrar los aprobados y reprobados; uti- lizaremos un contador para controlar el proceso de los ciclos y necesitaremos una variable para guardar la entrada del usuario. La variable en la que se almacenará la entrada del usuario no se inicializa al principio del algoritmo, ya que su valor proviene del usuario durante cada iteración del ciclo. La instrucción en seudocódigo Inicializar variables puede mejorarse de la siguiente manera: Inicializar aprobados en cero Inicializar reprobados en cero Inicializar contador de estudiantes en cero Observe que sólo se inicializan los contadores al principio del algoritmo. La instrucción en seudocódigo Introducir las 10 calificaciones del examen, y contar los aprobados y reprobados requiere un ciclo en el que se introduzca sucesivamente el resultado de cada examen. Sabemos de antemano que hay precisamente 10 resultados del examen, por lo que es apropiado utilizar un ciclo controlado por contador. Dentro del ciclo (es decir, anidado dentro del ciclo), una estructura de selección doble determinará si cada resultado del examen es aprobado o reprobado, e incrementará el contador apropiado. Entonces, la mejora al seudocódigo anterior es Mientras el contador de estudiantes sea menor o igual a 10 Pedir al usuario que introduzca el siguiente resultado del examen Recibir como entrada el siguiente resultado del examen Si el estudiante aprobó Sumar uno a aprobados 4.10 Cómo formular algoritmos: instrucciones de control anidadas 135
  • 174. 136 Capítulo 4 Instrucciones de control: parte 1 De lo contrario Sumar uno a reprobados Sumar uno al contador de estudiantes Nosotros utilizamos líneas en blanco para aislar la estructura de control Si...De lo contrario, lo cual mejora la legibilidad. La instrucción en seudocódigo Imprimir un resumen de los resultados de los exámenes y decidir si debe aumentarse la colegiatura puede mejorarse de la siguiente manera: Imprimir el número de aprobados Imprimir el número de reprobados Si más de ocho estudiantes aprobaron Imprimir “Aumentar la colegiatura” Segundo refinamiento completo en seudocódigo y conversión a la clase Analisis El segundo refinamiento completo aparece en la figura 4.11. Observe que también se utilizan líneas en blanco para separar la estructura Mientras y mejorar la legibilidad del programa. Este seudocódigo está ahora lo suficien- temente mejorado para su conversión a Java. La clase de Java que implementa el algoritmo en seudocódigo se muestra en la figura 4.12, y en la figura 4.13 aparecen dos ejecuciones de ejemplo. Las líneas 13 a 16 de la figura 4.12 declaran las variables que utiliza el método procesarResultadosExamen de la clase Analisis para procesar los resultados del examen. Varias de estas declaraciones utilizan la habilidad de Java para incorporar la inicialización de variables en las declaraciones (a aprobados se le asigna 0, a reprobados se le asigna 0 y a contadorEstudiantes se le asigna 1). Los programas con ciclos pueden requerir de la iniciali- zación al principio de cada repetición; por lo general, dicha reinicialización se realiza mediante instrucciones de asignación, en vez de hacerlo en las declaraciones. La instrucción while (líneas 19 a 33) itera 10 veces. Durante cada iteración, el ciclo recibe y procesa un resultado del examen. Observe que la instrucción if...else (líneas 26 a 29) para procesar cada resultado se anida en la instrucción while. Si resultado es 1, la instrucción if...else incrementa a aprobados; en caso 1 Inicializar aprobados en cero 2 Inicializar reprobados en cero 3 Inicializar contador de estudiantes en uno 4 5 Mientras el contador de estudiantes sea menor o igual a 10 6 Pedir al usuario que introduzca el siguiente resultado del examen 7 Recibir como entrada el siguiente resultado del examen 8 9 Si el estudiante aprobó 10 Sumar uno a aprobados 11 De lo contrario 12 Sumar uno a reprobados 13 14 Sumar uno al contador de estudiantes 15 16 Imprimir el número de aprobados 17 Imprimir el número de reprobados 18 19 Si más de ocho estudiantes aprobaron 20 Imprimir “Aumentar colegiatura” Figura 4.11 | El seudocódigo para el problema de los resultados del examen.
  • 175. contrario, asume que resultado es 2 e incrementa reprobados. La línea 32 incrementa contadorEstudiantes antes de que se evalúe otra vez la condición del ciclo, en la línea 19. Después de introducir 10 valores, el ciclo termina y la línea 36 muestra el número de aprobados y de reprobados. La instrucción if de las líneas 39 a 40 determina si más de ocho estudiantes aprobaron el examen y, de ser así, imprime el mensaje "Aumentar cole- giatura". Tip para prevenir errores 4.3 Inicializar las variables locales cuando se declaran ayuda al programador a evitar cualquier error de compilación que pudiera surgir, debido a los intentos por utilizar datos sin inicializar. Aunque Java no requiere que se incorporen las inicializaciones de variables locales en las declaraciones, sí requiere que se inicialicen las variables locales antes de utilizar sus valores en una expresión. 1 // Fig. 4.12: Analisis.java 2 // Análisis de los resultados de un examen. 3 import java.util.Scanner; // esta clase utiliza la clase Scanner 4 5 public class Analisis 6 { 7 public void procesarResultadosExamen() 8 { 9 // crea objeto Scanner para obtener la entrada de la ventana de comandos 10 Scanner entrada = new Scanner( System.in ); 11 12 // inicialización de las variables en declaraciones 13 int aprobados = 0; // número de aprobados 14 int reprobados = 0; // número de reprobados 15 int contadorEstudiantes = 1; // contador de estudiantes 16 int resultado; // un resultado del examen (obtiene el valor del usuario) 17 18 // procesa 10 estudiantes, usando ciclo controlado por contador 19 while ( contadorEstudiantes <= 10 ) 20 { 21 // pide al usuario la entrada y obtiene el valor 22 System.out.print( "Escriba el resultado (1 = aprobado, 2 = reprobado): " ); 23 resultado = entrada.nextInt(); 24 25 // if...else anidado en while 26 if ( resultado == 1 ) // si resultado 1, 27 aprobados = aprobados + 1; // incrementa aprobados; 28 else // de lo contrario, resultado no es 1, por lo que 29 reprobados = reprobados + 1; // incrementa reprobados 30 31 // incrementa contadorEstudiantes, para que el ciclo termine en un momento dado 32 contadorEstudiantes = contadorEstudiantes + 1; 33 } // fin de while 34 35 // fase de terminación; prepara y muestra los resultados 36 System.out.printf( "Aprobados: %dnReprobados: %dn", aprobados, reprobados ); 37 38 // determina si más de 8 estudiantes aprobaron 39 if ( aprobados > 8 ) 40 System.out.println( "Aumentar colegiatura" ); 41 } // fin del método procesarResultadosExamen 42 43 } // fin de la clase Analisis Figura 4.12 | Estructuras de control anidadas: problema de los resultados del examen. 4.10 Cómo formular algoritmos: instrucciones de control anidadas 137
  • 176. 138 Capítulo 4 Instrucciones de control: parte 1 La clase PruebaAnalisis para demostrar la clase Analisis La clase PruebaAnalisis (figura 4.13) crea un objeto Analisis (línea 8) e invoca al método procesarResul- tadosExamen (línea 9) de ese objeto para procesar un conjunto de resultados de un examen, introducidos por el usuario. La figura 4.13 muestra la entrada y salida de dos ejecuciones de ejemplo del programa. Durante la primera ejecución de ejemplo, la condición en la línea 39 del método procesarResultadosExamen de la figura 4.12 es verdadera; más de ocho estudiantes aprobaron el examen, por lo que el programa imprime un mensaje indicando que se debe aumentar la colegiatura. 1 // Fig. 4.13: PruebaAnalisis.java 2 // Programa de prueba para la clase Analisis. 3 4 public class PruebaAnalisis 5 { 6 public static void main( String args[] ) 7 { 8 Analisis aplicacion = new Analisis(); // crea objeto Analisis 9 aplicacion.procesarResultadosExamen(); // llama al método para procesar los resultados 10 } // fin de main 11 12 } // fin de la clase PruebaAnalisis Figura 4.13 | Programa de prueba para la clase Analisis (figura 4.12). Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 2 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Aprobados: 9 Reprobados: 1 Aumentar colegiatura Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 2 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 2 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 2 Escriba el resultado (1 = aprobado, 2 = reprobado): 2 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Escriba el resultado (1 = aprobado, 2 = reprobado): 1 Aprobados: 6 Reprobados: 4 4.11 Operadores de asignación compuestos Java cuenta con varios operadores de asignación compuestos para abreviar las expresiones de asignación. Cual- quier instrucción de la forma variable = variable operador expresión;
  • 177. en donde operador es uno de los operadores binarios +, -, *, / o % (o alguno de los otros que veremos más adelan- te en el libro), puede escribirse de la siguiente forma: variable operador= expresión; Por ejemplo, puede abreviar la instrucción c = c + 3; mediante el operador de asignación compuesto de suma, +=, de la siguiente manera: c += 3; El operador += suma el valor de la expresión que está a la derecha del operador, al valor de la variable que está a la izquierda del operador, y almacena el resultado en la variable que está a la izquierda del operador. Por lo tanto, la expresión de asignación c += 3 suma 3 a c. La figura 4.14 muestra los operadores de asignación aritmé- ticos compuestos, algunas expresiones de ejemplo en las que se utilizan los operadores y las explicaciones de lo que estos operadores hacen. Operador de asignación Expresión de ejemplo Explicación Asigna Suponer que: int c = 3, d = 5, e = 4, f = 6, g = 12; += c += 7 c = c + 7 10 a c -= d -= 4 d = d – 4 1 a d *= e *= 5 e = e * 5 20 a e /= f /= 3 f = f / 3 2 a f %= g %= 9 g = g % 9 3 a g Figura 4.14 | Operadores de asignación aritméticos. 4.12 Operadores de incremento y decremento Java proporciona dos operadores unarios para sumar 1, o restar 1, al valor de una variable numérica. Estos operadores son el operador de incremento unario, ++, y el operador de decremento unario, --, los cuales se sintetizan en la figura 4.15. Un programa puede incrementar en 1 el valor de una variable llamada c, utilizando el operador de incremento, ++, en lugar de usar la expresión c = c + 1 o c += 1. A un operador de incremento o decremento que se coloca antes de una variable se le llama operador de preincremento o predecremento, respectivamente. A un operador de incremento o decremento que se coloca después de una variable se le llama operador de postincremento o postdecremento, respectivamente. Al proceso de utilizar el operador de preincremento (o postdecremento) para sumar (o restar) 1 a una varia- ble, se le conoce como preincrementar (o predecrementar) la variable. Al preincrementar (o predecrementar) una variable, ésta se incrementa (o decrementa) en 1, y después el nuevo valor de la variable se utiliza en la expre- sión en la que aparece. Al proceso de utilizar el operador de preincremento (o postdecremento) para sumar (o restar) 1 a una variable, se le conoce como postincrementar (o postdecrementar) la variable. Al postincrementar (o postdecrementar) una variable, el valor actual de la variable se utiliza en la expresión en la que aparece y después el valor de la variable se incrementa (o decrementa) en 1. Buena práctica de programación 4.7 A diferencia de los operadores binarios, los operadores unarios de incremento y decremento deben colocarse enseguida de sus operandos, sin espacios entre ellos. 4.12 Operadores de incremento y decremento 139
  • 178. 140 Capítulo 4 Instrucciones de control: parte 1 La figura 4.16 demuestra la diferencia entre la versión de preincremento y la versión de predecremento del operador de incremento ++. El operador de decremento (--) funciona de manera similar. Observe que este ejem- plo sólo contiene una clase, en donde el método main realiza todo el trabajo de ésta. En éste y en el capítulo 3, usted ha visto ejemplos que consisten en dos clases: una clase contiene los métodos que realizan tareas útiles, y la otra contiene el método main, que crea un objeto de la otra clase y hace llamadas a sus métodos. En este ejemplo simplemente queremos mostrarle la mecánica del operador ++, por lo que sólo usaremos una declaración de clase que contiene el método main. Algunas veces, cuando no tenga sentido tratar de crear una clase reutilizable para demostrar un concepto simple, utilizaremos un ejemplo “mecánico” contenido completamente dentro del méto- do main de una sola clase. La línea 11 inicializa la variable c con 5, y la línea 12 imprime el valor inicial de c. La línea 13 imprime el valor de la expresión c++. Esta expresión postincrementa la variable c, por lo que se imprime el valor original de c (5), y después el valor de c se incrementa (a 6). Por ende, la línea 13 imprime el valor inicial de c (5) otra vez. La línea 14 imprime el nuevo valor de c (6) para demostrar que, sin duda, se incrementó el valor de la variable en la línea 13. La línea 19 restablece el valor de c a 5, y la línea 20 imprime el valor de c. La línea 21 imprime el valor de la expresión ++c. Esta expresión preincrementa a c, por lo que su valor se incrementa y después se imprime el nuevo valor (6). La línea 22 imprime el valor de c otra vez, para mostrar que sigue siendo 6 después de que se ejecuta la línea 21. Los operadores de asignación compuestos aritméticos y los operadores de incremento y decremento pueden utilizarse para simplificar las instrucciones de los programas. Por ejemplo, las tres instrucciones de asignación de la figura 4.12 (líneas 27, 29 y 32) aprobados = aprobados + 1; reprobados = reprobados + 1; contadorEstudiantes = contadorEstudiantes + 1; pueden escribirse en forma más concisa con operadores de asignación compuestos, de la siguiente manera: aprobados += 1; reprobados += 1; contadorEstudiantes += 1; con operadores de preincremento de la siguiente forma: ++aprobados; ++reprobados; ++contadorEstudiantes; o con operadores de postincremento de la siguiente forma: aprobados++; reprobados++; contadorEstudiantes++; Operador Operador Llamado Expresión de ejemplo Explicación ++ Preincremento ++a Incrementar a en 1, después utilizar el nuevo valor de a en la expresión en que esta variable reside. ++ Postincremento a++ Usar el valor actual de a en la expresión en la que esta varia- ble reside, después incrementar a en 1. -- Predecremento --b Decrementar b en 1, después utilizar el nuevo valor de b en la expresión en que esta variable reside. -- Postdecremento b-- Usar el valor actual de b en la expresión en la que esta varia- ble reside, después decrementar b en 1. Figura 4.15 | Los operadores de incremento y decremento.
  • 179. Al incrementar o decrementar una variable que se encuentre en una instrucción por sí sola, las formas pre- incremento y postincremento tienen el mismo efecto, al igual que las formas predecremento y postdecremento. Solamente cuando una variable aparece en el contexto de una expresión más grande es cuando los operadores preincremento y postdecremento tienen distintos efectos (y lo mismo se aplica a los operadores de predecremento y postdecremento). Error común de programación 4.9 Tratar de usar el operador de incremento o decremento en una expresión a la que no se le pueda asignar un valor es un error de sintaxis. Por ejemplo, escribir ++(x + 1) es un error de sintaxis, ya que (x + 1) no es una variable. La figura 4.17 muestra la precedencia y la asociatividad de los operadores que se han presentado hasta este punto. Los operadores se muestran de arriba a abajo, en orden descendente de precedencia. La segunda columna describe la asociatividad de los operadores en cada nivel de precedencia. El operador condicional (?:), los opera- dores unarios de incremento (++), decremento (--), suma (+) y resta (-), los operadores de conversión de tipo y los operadores de asignación =, +=, -=, *=, /= y %= se asocian de derecha a izquierda. Todos los demás operado- res en la tabla de precedencia de operadores de la figura 4.17 se asocian de izquierda a derecha. La tercera columna enlista el tipo de cada grupo de operadores. 1 // Fig. 4.16: Incremento.java 2 // Operadores de preincremento y postincremento. 3 4 public class Incremento 5 { 6 public static void main( String args[] ) 7 { 8 int c; 9 10 // demuestra el operador de preincremento 11 c = 5; // asigna 5 a c 12 System.out.println( c ); // imprime 5 13 System.out.println( c++ ); // imprime 5, después postincrementa 14 System.out.println( c ); // imprime 6 15 16 System.out.println(); // omite una línea 17 18 // demuestra el operador de postincremento 19 c = 5; // asigna 5 a c 20 System.out.println( c ); // imprime 5 21 System.out.println( ++c ); // preincrementa y después imprime 6 22 System.out.println( c ); // imprime 6 23 24 } // fin de main 25 26 } // fin de la clase Incremento Figura 4.16 | Preincrementar y postincrementar. 5 5 6 5 6 6 4.12 Operadores de incremento y decremento 141
  • 180. 142 Capítulo 4 Instrucciones de control: parte 1 4.13 Tipos primitivos La tabla del apéndice D, Tipos primitivos, enlista los ocho tipos primitivos en Java. Al igual que sus lenguajes antecesores C y C++, Java requiere que todas las variables tengan un tipo. Es por esta razón que Java se conoce como un lenguaje fuertemente tipificado. En C y C++, los programadores frecuentemente tienen que escribir versiones independientes de los progra- mas, ya que no se garantiza que los tipos primitivos sean idénticos de computadora en computadora. Por ejemplo, un valor int en un equipo podría representarse mediante 16 bits (2 bytes) de memoria, mientras que un valor int en otro equipo podría representarse mediante 32 bits (4 bytes) de memoria. En Java, los valores int siempre son de 32 bits (4 bytes). Tip de portabilidad 4.1 A diferencia de C y C++, los tipos primitivos en Java son portables en todas las plataformas con soporte para Java. Cada uno de los tipos del apéndice D se enlista con su tamaño en bits (hay ocho bits en un byte) y su rango de valores. Como los diseñadores de Java desean que sea lo más portable posible, utilizan estándares reconocidos internacionalmente tanto para los formatos de caracteres (Unicode; para más información, visite www.unicode. org) como para los números de punto flotante (IEEE 754; para más información, visite grouper.ieee.org/ groups/754/). En la sección 3.5 vimos que a las variables de tipos primitivos que se declaran fuera de un método, como campos de una clase, se les asignan valores predeterminados, a menos que se inicialicen en forma explícita. Las variables de los tipos char, byte, short, int, long, float y double reciben el valor 0 de manera predeterminada. Las variables de tipo boolean reciben el valor false de manera predeterminada. Las variables de instancia de tipo por referencia se inicializan de manera predeterminada con el valor null. 4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples Una de las características interesantes de Java es su soporte para gráficos, el cual permite a los programadores mejorar visualmente sus aplicaciones. Esta sección presenta una de las capacidades gráficas de Java: dibujar líneas. También cubre los aspectos básicos acerca de cómo crear una ventana para mostrar un dibujo en la pantalla de la computadora. Para dibujar en Java, debe comprender su sistema de coordenadas (figura 4.18), un esquema para identificar cada uno de los puntos en la pantalla. De manera predeterminada, la esquina superior izquierda de un compo- nente de la GUI tiene las coordenadas (0, 0). Un par de coordenadas está compuesto por una coordenada x (la Operadores Asociatividad Tipo ++ -- derecha a izquierda postfijo unario ++ -- + - ( tipo ) derecha a izquierda prefijo unario * / % izquierda a derecha multiplicativo + - izquierda a derecha aditivo < <= > >= izquierda a derecha relacional == != izquierda a derecha igualdad ?: derecha a izquierda condicional = += -= *= /= %= derecha a izquierda asignación Figura 4.17 | Precedencia y asociatividad de los operadores vistos hasta ahora.
  • 181. coordenada horizontal) y una coordenada y (la coordenada vertical). La coordenada x es la ubicación horizon- tal que se desplaza de izquierda a derecha. La coordenada y es la ubicación vertical que se desplaza de arriba hacia abajo. El eje x describe cada una de las coordenadas horizontales, y el eje y describe cada una de las coordenadas verticales. Las coordenadas indican en dónde deben mostrarse los gráficos en una pantalla. Las unidades de las coorde- nadas se miden en píxeles. Un píxel es la unidad de resolución más pequeña de una pantalla. (El término píxel significa “elemento de imagen”). Nuestra primera aplicación de dibujo simplemente dibuja dos líneas. La clase PanelDibujo (figura 4.19) realiza el dibujo en sí, mientras que la clase PruebaPanelDibujo (figura 4.20) crea una ventana para mostrar el dibujo. En la clase PanelDibujo, las instrucciones import de las líneas 3 y 4 nos permiten utilizar la clase Graphics (del paquete java.awt), que proporciona varios métodos para dibujar texto y figuras en la pantalla, y la clase JPanel (del paquete javax.swing), que proporciona un área en la que podemos dibujar. La línea 6 utiliza la palabra clave extends para indicar que la clase PanelDibujo es un tipo mejorado de JPanel. La palabra clave extends representa algo que se denomina relación de herencia, en la cual nuestra nueva clase PanelDibujo empieza con los miembros existentes (datos y métodos) de la clase JPanel. La clase de la cual (0, 0) (x, y) +y +x eje y eje x Figura 4.18 | Sistema de coordenadas de Java. Las unidades se miden en píxeles. 1 // Fig. 4.19: PanelDibujo.java 2 // Uso de drawLine para conectar las esquinas de un panel. 3 import java.awt.Graphics; 4 import javax.swing.JPanel; 5 6 public class PanelDibujo extends JPanel 7 { 8 // dibuja una x desde las esquinas del panel 9 public void paintComponent( Graphics g ) 10 { 11 // llama a paintComponent para asegurar que el panel se muestre correctamente 12 super.paintComponent( g ); 13 14 int anchura = getWidth(); // anchura total 15 int altura = getHeight(); // altura total 16 17 // dibuja una línea de la esquina superior izquierda a la esquina inferior derecha 18 g.drawLine( 0, 0, anchura, altura ); 19 20 // dibuja una línea de la esquina inferior izquierda a la esquina superior derecha 21 g.drawLine( 0, altura, anchura, 0 ); 22 } // fin del método paintComponent 23 } // fin de la clase PanelDibujo Figura 4.19 | Uso de drawLine para conectar las esquinas de un panel. 4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples 143
  • 182. 144 Capítulo 4 Instrucciones de control: parte 1 PanelDibujo hereda, JPanel, aparece a la derecha de la palabra clave extends. En esta relación de herencia, a JPanel se le conoce como la superclase y PanelDibujo es la subclase. Esto produce una clase PanelDibujo que tiene los atributos (datos) y comportamientos (métodos) de la clase JPanel, así como las nuevas características que agregaremos en nuestra declaración de la clase PanelDibujo; específicamente, la habilidad de dibujar dos líneas a lo largo de las diagonales del panel. En el capítulo 9 explicaremos detalladamente el concepto de herencia. Todo JPanel, incluyendo nuestro PanelDibujo, tiene un método paintComponent (líneas 9 a 22), que el sistema llama automáticamente cada vez que necesita mostrar el objeto JPanel. El método paintComponent debe declararse como se muestra en la línea 9; de no ser así, el sistema no llamará al método. Este método se llama cuando se muestra un objeto JPanel por primera vez en la pantalla, cuando una ventana en la pantalla lo cubre y después lo descubre, y cuando la ventana en la que aparece cambia su tamaño. El método paint- Component requiere un argumento, un objeto Graphics, que el sistema proporciona por usted cuando llama a paintComponent. La primera instrucción en cualquier método paintComponent que cree debe ser siempre: super.paintComponent( g ); la cual asegura que el panel se despliegue apropiadamente en la pantalla, antes de empezar a dibujar en él. A con- tinuación, las líneas 14 y 15 llaman a dos métodos que la clase PanelDibujo hereda de la clase JPanel. Como PanelDibujo extiende a JPanel, PanelDibujo puede usar cualquier método public que esté declarado en JPa- nel. Los métodos getWidth y getHeight devuelven la anchura y la altura del objeto JPanel, respectivamente. Las líneas 14 y 15 almacenan estos valores en las variables locales anchura y altura. Por último, las líneas 18 y 21 utilizan la referencia g de la clase Graphics para llamar al método drawLine, y que dibuje las dos líneas. Los primeros dos argumentos son las coordenadas x y y para uno de los puntos finales de la línea, y los últimos dos argumentos son las coordenadas para el otro punto final. Si cambia de tamaño la ventana, las líneas se escalaran de manera acorde, ya que los argumentos se basan en la anchura y la altura del panel. Al cambiar el tamaño de la ven- tana en esta aplicación, el sistema llama a paintComponent para volver a dibujar el contenido de PanelDibujo. Para mostrar el PanelDibujo en la pantalla, debemos colocarlo en una ventana. Usted debe crear una ven- tana con un objeto de la clase JFrame. En PruebaPanelDibujo.java (figura 4.20), la línea 3 importa la clase JFrame del paquete javax.swing. La línea 10 en el método main de la clase PruebaPanelDibujo crea una instancia de la clase PanelDibujo, la cual contiene nuestro dibujo, y la línea 13 crea un nuevo objeto JFrame que puede contener y mostrar nuestro panel. La línea 16 llama al método setDefaultCloseOperation con el argumento JFrame.EXIT_ON_CLOSE, para indicar que la aplicación debe terminar cuando el usuario cierre la ventana. La línea 18 utiliza el método add de JFrame para adjuntar el objeto PanelDibujo, que contiene nues- tro dibujo, al objeto JFrame. La línea 19 establece el tamaño del objeto JFrame. El método setSize recibe dos parámetros: la anchura del objeto JFrame y la altura. Por último, la línea 20 muestra el objeto JFrame. Cuando se muestra este objeto, se hace la llamada al método paintComponent de PanelDibujo (líneas 9 a 22 de la figura 4.19) y se dibujan las dos líneas (vea los resultados de ejemplo de la figura 4.20). Cambie el tamaño de la ventana, para que vea que las líneas siempre se dibujan con base en la anchura y altura actuales de la ventana. Ejercicios del ejemplo práctico de GUI y gráficos 4.1 Utilizar ciclos e instrucciones de control para dibujar líneas puede producir muchos diseños interesantes. a) Cree el diseño que se muestra en la captura de pantalla izquierda de la figura 4.21. Este diseño dibuja líneas que parten desde la esquina superior izquierda, y se despliegan hasta cubrir la mitad superior izquierda del panel. Un método es dividir la anchura y la altura en un número equivalente de pasos (nosotros descubrimos que 15 pasos es una buena cantidad). El primer punto final de una línea siempre estará en la esquina superior izquierda (0,0). El segundo punto final puede encontrarse partiendo desde la esquina inferior izquierda, y avanzando un paso vertical hacia arriba, y un paso horizontal hacia la derecha. Dibuje una línea entre los dos puntos finales. Con- tinúe avanzando hacia arriba y a la derecha, para encontrar cada punto final sucesivo. La figura deberá escalarse apropiadamente, a medida que se cambie el tamaño de la ventana. b) Modifique su respuesta en la parte (a) para hacer que las líneas se desplieguen a partir de las cuatro esquinas, como se muestra en la captura de pantalla derecha de la figura 4.21. Las líneas de esquinas opuestas deberán intersecarse a lo largo de la parte media. 4.2 La figura 4.22 muestra dos diseños adicionales, creados mediante el uso de ciclos while y drawLine. a) Cree el diseño de la captura de pantalla izquierda de la figura 4.22. Empiece por dividir cada flanco en un núme- ro equivalente de incrementos (elegimos 15 de nuevo). La primera línea empieza en la esquina superior izquierda y termina un paso a la derecha, en el flanco inferior. Para cada línea sucesiva, avance hacia abajo un incremento
  • 183. en el flanco izquierdo, y un incremento a la derecha en el flanco inferior. Continúe dibujando líneas hasta llegar a la esquina inferior derecha. La figura deberá escalarse a medida que se cambie el tamaño de la ventana, de manera que los puntos finales siempre toquen los flancos. 1 // Fig. 4.20: PruebaPanelDibujo.java 2 // Aplicación que muestra un PanelDibujo. 3 import javax.swing.JFrame; 4 5 public class PruebaPanelDibujo 6 { 7 public static void main( String args[] ) 8 { 9 // crea un panel que contiene nuestro dibujo 10 PanelDibujo panel = new PanelDibujo(); 11 12 // crea un nuevo marco para contener el panel 13 JFrame aplicacion = new JFrame(); 14 15 // establece el marco para salir cuando se cierre 16 aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); 17 18 aplicacion.add( panel ); // agrega el panel al marco 19 aplicacion.setSize( 250, 250 ); // establece el tamaño del marco 20 aplicacion.setVisible( true ); // hace que el marco sea visible 21 } // fin de main 22 } // fin de la clase PruebaPanelDibujo Figura 4.20 | Creación de un objeto JFrame para mostrar el objeto PanelDibujo. Figura 4.21 | Despliegue de líneas desde una esquina. 4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples 145
  • 184. 146 Capítulo 4 Instrucciones de control: parte 1 b) Modifique su respuesta en la parte (a) para reflejar el diseño en las cuatro esquinas, como se muestra en la captura de pantalla derecha de la figura 4.22. 4.15 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de los atributos de las clases En la sección 3.10 empezamos la primera etapa de un diseño orientado a objetos (DOO) para nuestro sistema ATM: analizar el documento de requerimientos e identificar las clases necesarias para implementar el sistema. Enlistamos los sustantivos y las frases nominales en el documento de requerimientos e identificamos una clase separada para cada uno de ellos, que desempeña un papel importante en el sistema ATM. Después modelamos las clases y sus relaciones en un diagrama de clases de UML (figura 3.24). Las clases tienen atributos (datos) y ope- raciones (comportamientos). En los programas en Java, los atributos de las clases se implementan como campos, y las operaciones de las clases se implementan como métodos. En esta sección determinaremos muchos de los atributos necesarios en el sistema ATM. En el capítulo 5 examinaremos cómo esos atributos representan el estado de un objeto. En el capítulo 6 determinaremos las operaciones de las clases. Identificación de los atributos Considere los atributos de algunos objetos reales: los atributos de una persona incluyen su altura, peso y si es zurdo, diestro o ambidiestro. Los atributos de un radio incluyen la estación, el volumen y si está en AM o FM. Los atributos de un automóvil incluyen las lecturas de su velocímetro y odómetro, la cantidad de gasolina en su tanque y la velocidad de marcha en la que se encuentra. Los atributos de una computadora personal incluyen su fabricante (por ejemplo, Dell, Sun, Apple o IBM), el tipo de pantalla (por ejemplo, LCD o CRT), el tamaño de su memoria principal y el de su disco duro. Podemos identificar muchos atributos de las clases en nuestro sistema, analizando las palabras y frases des- criptivas en el documento de requerimientos. Para cada palabra o frase que descubramos desempeña un rol importante en el sistema ATM, creamos un atributo y lo asignamos a una o más de las clases identificadas en la sección 3.10. También creamos atributos para representar los datos adicionales que pueda necesitar una clase, ya que dichas necesidades se van aclarando a lo largo del proceso de diseño. La figura 4.23 lista las palabras o frases del documento de requerimientos que describen a cada una de las clases. Para formar esta lista, leemos el documento de requerimientos e identificamos cualquier palabra o frase que haga referencia a las características de las clases en el sistema. Por ejemplo, el documento de requerimien- tos describe los pasos que se llevan a cabo para obtener un “monto de retiro”, por lo que listamos “monto” enseguida de la clase Retiro. La figura 4.23 nos conduce a crear un atributo de la clase ATM. Esta clase mantiene información acerca del estado del ATM. La frase “el usuario es autenticado” describe un estado del ATM (en la sección 5.11 hablaremos con detalle sobre los estados), por lo que incluimos usuarioAutenticado como un atributo Boolean (es decir, un atributo que tiene un valor de true o false) en la clase ATM. Observe que el atributo tipo Boolean en UML Figura 4.22 | Arte lineal con ciclos y drawLine.
  • 185. es equivalente al tipo boolean en Java. Este atributo indica si el ATM autenticó con éxito al usuario actual o no; usuarioAutenticado debe ser true para que el sistema permita al usuario realizar transacciones y acceder a la información de la cuenta. Este atributo nos ayuda a cerciorarnos de la seguridad de los datos en el sistema. Las clases SolicitudSaldo, Retiro y Deposito comparten un atributo. Cada transacción requiere un “número de cuenta” que corresponde a la cuenta del usuario que realiza la transacción. Asignamos el atributo entero numeroCuenta a cada clase de transacción para identificar la cuenta a la que se aplica un objeto de la clase. Las palabras y frases descriptivas en el documento de requerimientos también sugieren ciertas diferencias en los atributos requeridos por cada clase de transacción. El documento de requerimientos indica que para retirar efectivo o depositar fondos, los usuarios deben introducir un “monto” específico de dinero para retirar o depositar, respectivamente. Por ende, asignamos a las clases Retiro y Deposito un atributo llamado monto para almacenar el valor suministrado por el usuario. Los montos de dinero relacionados con un retiro y un depósito son carac- terísticas que definen estas transacciones, que el sistema requiere para que se lleven a cabo. Sin embargo, la clase SolicitudSaldo no necesita datos adicionales para realizar su tarea; sólo requiere un número de cuenta para indicar la cuenta cuyo saldo hay que obtener. La clase Cuenta tiene varios atributos. El documento de requerimientos establece que cada cuenta de banco tiene un “número de cuenta” y un “NIP”, que el sistema utiliza para identificar las cuentas y autentificar a los usuarios. A la clase Cuenta le asignamos dos atributos enteros: numeroCuenta y nip. El documento de requeri- mientos también especifica que una cuenta debe mantener un “saldo” del monto de dinero que hay en la cuenta, y que el dinero que el usuario deposita no estará disponible para su retiro sino hasta que el banco verifique la can- tidad de efectivo en el sobre de depósito y cualquier cheque que contenga. Sin embargo, una cuenta debe registrar de todas formas el monto de dinero que deposita un usuario. Por lo tanto, decidimos que una cuenta debe repre- sentar un saldo utilizando dos atributos: saldoDisponible y saldoTotal. El atributo saldoDisponible rastrea el monto de dinero que un usuario puede retirar de la cuenta. El atributo saldoTotal se refiere al monto total de dinero que el usuario tiene “en depósito” (es decir, el monto de dinero disponible, más el monto de depósitos en efectivo o la cantidad de cheques esperando a ser verificados). Por ejemplo, suponga que un usuario del ATM deposita $50.00 en efectivo, en una cuenta vacía. El atributo saldoTotal se incrementaría a $50.00 para registrar el depósito, pero el saldoDisponible permanecería en $0. [Nota: estamos suponiendo que el banco actualiza el atributo saldoDisponible de una Cuenta poco después de que se realiza la transacción del ATM, en respuesta a la confirmación de que se encontró un monto equivalente a $50.00 en efectivo o cheques en el sobre de depósito. Asumimos que esta actualización se realiza a través de una transacción que realiza el empleado del banco mediante Clase Palabras y frases descriptivas ATM el usuario es autenticado SolicitudSaldo número de cuenta Retiro número de cuenta monto Deposito número de cuenta monto BaseDatosBanco [no hay palabras o frases descriptivas] Cuenta número de cuenta NIP saldo Pantalla [no hay palabras o frases descriptivas] Teclado [no hay palabras o frases descriptivas] DispensadorEfectivo empieza cada día cargado con 500 billetes de $20 RanuraDeposito [no hay palabras o frases descriptivas] Figura 4.23 | Palabras y frases descriptivas del documento de requerimientos del ATM. 4.15 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de los atributos de las clases 147
  • 186. 148 Capítulo 4 Instrucciones de control: parte 1 el uso de un sistema bancario distinto al del ATM. Por ende, no hablaremos sobre esta transacción en nuestro ejemplo práctico]. La clase DispensadorEfectivo tiene un atributo. El documento de requerimientos establece que el dispen- sador de efectivo “empieza cada día cargado con 500 billetes de $20”. El dispensador de efectivo debe llevar el registro del número de billetes que contiene para determinar si hay suficiente efectivo disponible para satisfacer la demanda de los retiros. Asignamos a la clase DispensadorEfectivo el atributo entero conteo, el cual se esta- blece al principio en 500. Para los verdaderos problemas en la industria, no existe garantía alguna de que el documento de requeri- mientos será lo suficientemente robusto y preciso como para que el diseñador de sistemas orientados a objetos determine todos los atributos, o inclusive todas las clases. La necesidad de clases, atributos y comportamientos adicionales puede irse aclarando a medida que avance el proceso de diseño. A medida que progresemos a través de este ejemplo práctico, nosotros también seguiremos agregando, modificando y eliminando información acerca de las clases en nuestro sistema. Modelado de los atributos El diagrama de clases de la figura 4.24 enlista algunos de los atributos para las clases en nuestro sistema; las palabras y frases descriptivas en la figura 4.23 nos llevan a identificar estos atributos. Por cuestión de simpleza, la figura 4.24 no muestra las asociaciones entre las clases; en la figura 3.24 mostramos estas asociaciones. Ésta es una práctica común de los diseñadores de sistemas, a la hora de desarrollar los diseños. En la sección 3.10 vimos que en UML, los atributos de una clase se colocan en el compartimiento intermedio del rectángulo de la clase. Listamos el nombre de cada atributo y su tipo, separados por un signo de dos puntos (:), seguido en algunos casos de un signo de igual (=) y de un valor inicial. Considere el atributo usuarioAutenticado de la clase ATM: usuarioAutenticado : Boolean = false La declaración de este atributo contiene tres piezas de información acerca del atributo. El nombre del atributo es usuarioAutenticado. El tipo del atributo es Boolean. En Java, un atributo puede representarse mediante un tipo primitivo, como boolean, int o double, o por un tipo de referencia como una clase (como vimos en el capítulo 3). Hemos optado por modelar sólo los atributos de tipo primitivo en la figura 4.24; en breve hablaremos sobre el razonamiento detrás de esta decisión. [Nota: los tipos de los atributos en la figura 4.24 están en notación de UML. Asociaremos los tipos Boolean, Integer y Double en el diagrama de UML con los tipos primitivos boolean, int y double en Java, respectivamente]. También podemos indicar un valor inicial para un atributo. El atributo usuarioAutenticado en la clase ATM tiene un valor inicial de false. Esto indica que al principio el sistema no considera que el usuario está autenti- cado. Si no se especifica un valor inicial para un atributo, sólo se muestran su nombre y tipo (separados por dos puntos). Por ejemplo, el atributo numeroCuenta de la clase SolicitudSaldo es un entero. Aquí no mostramos un valor inicial, ya que el valor de este atributo es un número que todavía no conocemos. Este número se deter- minará en tiempo de ejecución, con base en el número de cuenta introducido por el usuario actual del ATM. La figura 4.24 no incluye atributos para las clases Pantalla, Teclado y RanuraDeposito. Éstos son com- ponentes importantes de nuestro sistema, para los cuales nuestro proceso de diseño aún no ha revelado ningún atributo. No obstante, tal vez descubramos algunos en las fases restantes de diseño, o cuando implementemos estas clases en Java. Esto es perfectamente normal. Observación de ingeniería de software 4.6 En las primeras fases del proceso de diseño, a menudo las clases carecen de atributos (y operaciones). Sin embargo, esas clases no deben eliminarse, ya que los atributos (y las operaciones) pueden hacerse evidentes en las fases posteriores de diseño e implementación. Observe que la figura 4.24 tampoco incluye atributos para la clase BaseDatosBanco. En el capítulo 3 vimos que en Java, los atributos pueden representarse mediante los tipos primitivos o los tipos por referencia. Hemos optado por incluir sólo los atributos de tipo primitivo en el diagrama de clases de la figura 4.24 (y en los diagramas de clases similares a lo largo del ejemplo práctico). Un atributo de tipo por referencia se modela con más claridad como una asociación (en particular, una composición) entre la clase que contiene la referencia y la clase del objeto al que apunta la referencia. Por ejemplo, el diagrama de clases de la figura 3.24 indica que
  • 187. la clase BaseDatosBanco participa en una relación de composición con cero o más objetos Cuenta. De esta composición podemos determinar que, cuando implementemos el sistema ATM en Java, tendremos que crear un atributo de la clase BaseDatosBanco para almacenar cero o más objetos Cuenta. De manera similar, podemos determinar los atributos de tipo por referencia de la clase ATM que correspondan a sus relaciones de composición con las clases Pantalla, Teclado, DispensadorEfectivo y RanuraDeposito. Estos atributos basados en com- posiciones serían redundantes si los modeláramos en la figura 4.24, ya que las composiciones modeladas en la figura 3.24 transmiten de antemano el hecho de que la base de datos contiene información acerca de cero o más cuentas, y que un ATM está compuesto por una pantalla, un teclado, un dispensador de efectivo y una ranura para depósitos. Por lo general, los desarrolladores de software modelan estas relaciones de todo/parte como asocia- ciones de composición, en vez de modelarlas como atributos requeridos para implementar las relaciones. El diagrama de clases de la figura 4.24 proporciona una base sólida para la estructura de nuestro modelo, pero no está completo. En la sección 5.11 identificaremos los estados y las actividades de los objetos en el modelo, y en la sección 6.14 identificaremos las operaciones que realizan los objetos. A medida que presentemos más acerca de UML y del diseño orientado a objetos, continuaremos reforzando la estructura de nuestro modelo. Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 4.1 Por lo general, identificamos los atributos de las clases en nuestro sistema mediante el análisis de ____________ en el documento de requerimientos. a) Los sustantivos y las frases nominales. b) Las palabras y frases descriptivas. c) Los verbos y las frases verbales. d) Todo lo anterior. 4.2 ¿Cuál de los siguientes no es un atributo de un aeroplano? a) Longitud. b) Envergadura. Figura 4.24 | Clases con atributos. ATM usuarioAutenticado : Boolean = false SolicitudSaldo numeroCuenta : Integer DispensadorEfectivo conteo : Integer = 500 RanuraDeposito Pantalla Teclado Retiro numeroCuenta : Integer monto : Double BaseDatosBanco Deposito numeroCuenta : Integer monto : Double Cuenta numeroCuenta : Integer nip : Integer saldoDisponible : Double saldoTotal : Double 4.15 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de los atributos de las clases 149
  • 188. c) Volar. d) Número de asientos. 4.3 Describa el significado de la siguiente declaración de un atributo de la clase DispensadorEfectivo en el diagrama de clases de la figura 4.24: conteo : Integer = 500 Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 4.1 b. 4.2 c. Volar es una operación o comportamiento de un aeroplano, no un atributo. 4.3 Esta declaración indica que el atributo conteo es de tipo Integer, con un valor inicial de 500. Este atributo lleva la cuenta del número de billetes disponibles en el DispensadorEfectivo, en cualquier momento dado. 4.16 Conclusión Este capítulo presentó las estrategias básicas de solución de problemas, que los programadores utilizan para crear clases y desarrollar métodos para estas clases. Demostramos cómo construir un algoritmo (es decir, una metodo- logía para resolver un problema), y después cómo refinar el algoritmo a través de diversas fases de desarrollo de seudocódigo, lo cual produce código en Java que puede ejecutarse como parte de un método. El capítulo demos- tró cómo utilizar el método de refinamiento de arriba a abajo, paso a paso, para planear las acciones específicas que debe realizar un método, y el orden en el que debe realizar estas acciones. Sólo se requieren tres tipos de estructuras de control (secuencia, selección y repetición) para desarrollar cualquier algoritmo para solucionar un problema. Específicamente, en este capítulo demostramos el uso de la instrucción de selección simple if, la instrucción de selección doble if…else y la instrucción de repetición while. Estas instrucciones son algunos de los bloques de construcción que se utilizan para construir soluciones para muchos problemas. Utilizamos el apilamiento de instrucciones de control para calcular el total y el promedio de un conjunto de calificaciones de estudiantes, mediante la repetición controlada por un contador y controlada por un centinela, y utilizamos el anidamiento de instrucciones de control para analizar y tomar decisiones con base en un conjunto de resultados de un examen. Presentamos los operadores de asignación compuestos de Java, así como sus operadores de incremento y decremento. Por último, hablamos sobre los tipos primitivos disponibles para los programadores de Java. En el capítulo 5, Instrucciones de control: parte 2, continuaremos nuestra discu- sión acerca de las instrucciones de control, en donde presentaremos las instrucciones for, do…while y switch. Resumen Sección 4.1 Introducción • Antes de escribir un programa para resolver un problema, debe tener una comprensión detallada acerca del proble- ma y una metodología cuidadosamente planeada para resolverlo. También debe comprender los bloques de cons- trucción disponibles, y emplear las técnicas probadas para construir programas. Sección 4.2 Algoritmos • Cualquier problema de cómputo puede resolverse mediante la ejecución de una serie de acciones, en un orden espe- cífico. • Un procedimiento para resolver un problema, en términos de las acciones a ejecutar y el orden en el que se ejecutan, se denomina algoritmo. • El proceso de especificar el orden en el que se ejecutan las instrucciones en un programa se denomina control del programa. Sección 4.3 Seudocódigo • El seudocódigo es un lenguaje informal, que ayuda a los programadores a desarrollar algoritmos sin tener que pre- ocuparse por los estrictos detalles de la sintaxis del lenguaje Java. • El seudocódigo es similar al lenguaje cotidiano; es conveniente y amigable para el usuario, pero no es un verdadero lenguaje de programación de computadoras. 150 Capítulo 4 Instrucciones de control: parte 1
  • 189. • El seudocódigo ayuda al programador a “idear” un programa antes de intentar escribirlo en un lenguaje de progra- mación. • El seudocódigo cuidadosamente preparado puede convertirse con facilidad en su correspondiente programa en Java. Sección 4.4 Estructuras de control • Por lo general, las instrucciones en un programa se ejecutan, una después de la otra, en el orden en el que están escritas. A este proceso se le conoce como ejecución secuencial. • Varias instrucciones de Java permiten al programador especificar que la siguiente instrucción a ejecutar no es nece- sariamente la siguiente en la secuencia. A esto se le conoce como transferencia de control. • Bohm y Jacopini demostraron que todos los programas podían escribirse en términos de sólo tres estructuras de control: la estructura de secuencia, la estructura de selección y la estructura de repetición. • El término “estructuras de control” proviene del campo de las ciencias computacionales. La Especificación del lengua- je Java se refiere a las “estructuras de control” como “instrucciones de control”. • La estructura de secuencia está integrada en Java. A menos que se indique lo contrario, la computadora ejecuta las instrucciones de Java, una después de la otra, en el orden en el que están escritas; es decir, en secuencia. • En cualquier parte en donde pueda colocarse una sola acción, pueden colocarse varias acciones en secuencia. • Los diagramas de actividad forman parte de UML. Un diagrama de actividad modela el flujo de trabajo (también conocido como la actividad) de una parte de un sistema de software. • Los diagramas de actividad se componen de símbolos de propósito especial, como los símbolos de estados de acción, rombos y pequeños círculos. Estos símbolos se conectan mediante flechas de transición, las cuales representan el flujo de la actividad. • Los estados de acción representan las acciones a realizar. Cada estado de acción contiene una expresión de acción, la cual especifica una acción específica a realizar. • Las flechas en un diagrama de actividad representan las transiciones, que indican el orden en el que ocurren las acciones representadas por los estados de acción. • El círculo relleno que se encuentra en la parte superior de un diagrama de actividad representa el estado inicial de la actividad: el comienzo del flujo de trabajo antes de que el programa realice las acciones modeladas. • El círculo sólido rodeado por una circunferencia, que aparece en la parte inferior del diagrama, representa el estado final: el término del flujo de trabajo después de que el programa realiza sus acciones. • Los rectángulos con las esquinas superiores derechas dobladas se llaman notas en UML: comentarios que describen el propósito de los símbolos en el diagrama. • Java tiene tres tipos de instrucciones de selección. La instrucción if realiza una acción si una condición es verdadera, o evita la acción si la condición es falsa. La instrucción if...else realiza una acción si una condición es verda- dera, y realiza una acción distinta si la condición es falsa. La instrucción switch realiza una de varias acciones distintas, dependiendo del valor de una expresión. • La instrucción if es una instrucción de selección simple, ya que selecciona o ignora una sola acción, o un solo grupo de acciones. • La instrucción if...else se denomina instrucción de selección doble, ya que selecciona una de dos acciones dis- tintas, o grupos de acciones. • La instrucción switch se llama instrucción de selección múltiple, ya que selecciona una de varias acciones distintas, o grupos de acciones. • Java cuenta con las instrucciones de repetición (ciclos) while, do...while y for, las cuales permiten a los progra- mas ejecutar instrucciones en forma repetida, siempre y cuando una condición de continuación de ciclo siga siendo verdadera. • Las instrucciones while y for realizan la(s) acción(es) en sus cuerpos, cero o más veces; si al principio la condición de continuación de ciclo es falsa, la(s) acción(es) no se ejecutará(n). La instrucción do...while lleva a cabo la(s) acción(es) que contiene en su cuerpo, una o más veces. • Las palabras if, else, switch, while, do y for son palabras claves en Java. Las palabras clave no pueden utilizarse como identificadores, como los nombres de variables. • Cada programa se forma mediante una combinación de todas las instrucciones de secuencia, selección y repetición que sean apropiadas para el algoritmo que implementa ese programa. • Las instrucciones de control de una sola entrada/una sola salida facilitan la construcción de los programas; “adjun- tamos” una instrucción de control a otra mediante la conexión del punto de salida de una al punto de entrada de la siguiente. A esto se le conoce como apilamiento de instrucciones de control. • Sólo hay una forma alterna en la que pueden conectarse las instrucciones de control (anidamiento de instrucciones de control), en la cual una instrucción de control aparece dentro de otra instrucción de control. Resumen 151
  • 190. 152 Capítulo 4 Instrucciones de control: parte 1 Sección 4.5 Instrucción de selección simple if • Los programas utilizan instrucciones de selección para elegir entre los cursos alternativos de acción. • El diagrama de actividad de una instrucción if de selección simple contiene el rombo, o símbolo de decisión, el cual indica que se tomará una decisión. El flujo de trabajo continuará a lo largo de una ruta determinada por las condi- ciones de guardia asociadas al símbolo, que pueden ser verdaderas o falsas. Cada flecha de transición que emerge de un símbolo de decisión tiene una condición de guardia. Si una condición de guardia es verdadera, el flujo de trabajo entra al estado de acción al que apunta la flecha de transición. • La instrucción if es una instrucción de control de una sola entrada/una sola salida. Sección 4.6 Instrucción de selección doble if...else • La instrucción if de selección simple realiza una acción indicada sólo cuando la condición es verdadera. • La instrucción if...else de selección doble realiza una acción cuando la condición es verdadera, y otra acción distinta cuando la condición es falsa. • El operador condicional (?:) puede usarse en lugar de una instrucción if...else. Éste es el único operador ternario de Java: recibe tres operandos. En conjunto, los operandos y el símbolo ?: forman una expresión condicional. • Un programa puede evaluar varios casos, colocando instrucciones if...else dentro de otras instrucciones if... else, para crear instrucciones if...else anidadas. • El compilador de Java siempre asocia un else con el if que lo precede inmediatamente, a menos que se le indique otra cosa mediante la colocación de llaves ({ y }). Este comportamiento puede conducir a lo que se conoce como el problema del else suelto. • Por lo general, la instrucción if espera sólo una instrucción en su cuerpo. Para incluir varias instrucciones en el cuerpo de un if (o en el cuerpo de un else para una instrucción if...else), encierre las instrucciones entre llaves ({ y }). • A un conjunto de instrucciones contenidas dentro de un par de llaves se le llama bloque. Un bloque puede colocarse en cualquier parte de un programa, en donde se pueda colocar una sola instrucción. • El compilador atrapa los errores de sintaxis. • Un error lógico tiene su efecto en tiempo de ejecución. Un error lógico fatal hace que un programa falle y termine antes de tiempo. Un error lógico no fatal permite que un programa continúe ejecutándose, pero hace que el progra- ma produzca resultados erróneos. • Así como podemos colocar un bloque en cualquier parte en la que pueda colocarse una sola instrucción, también podemos usar una instrucción vacía, que se representa colocando un punto y coma (;) en donde normalmente estaría una instrucción. Sección 4.7 Instrucción de repetición while • La instrucción de repetición while permite al programador especificar que un programa debe repetir una acción, mientras cierta condición siga siendo verdadera. • El símbolo de fusión de UML combina dos flujos de actividad en uno. • Los símbolos de decisión y de fusión pueden diferenciarse en base al número de flechas de transición “entrantes” y “salientes”. Un símbolo de decisión tiene una flecha de transición que apunta hacia el rombo, y dos o más flechas de transición que apuntan hacia fuera del rombo, para indicar las posibles transiciones desde ese punto. Cada flecha de transición que apunta hacia fuera de un símbolo de decisión tiene una condición de guardia. Un símbolo de fusión tiene dos o más flechas de transición que apuntan hacia el rombo, y sólo una flecha de transición que apunta hacia fuera del rombo, para indicar que se fusionarán varios flujos de actividad para continuar con la actividad. Ninguna de las flechas de transición asociadas con un símbolo de fusión tiene una condición de guardia. Sección 4.8 Cómo formular algoritmos: repetición controlada por un contador • La repetición controlada por un contador utiliza una variable llamada contador (o variable de control), para contro- lar el número de veces que se ejecuta un conjunto de instrucciones. • A la repetición controlada por contador se le conoce comúnmente como repetición definida, ya que el número de repeticiones se conoce desde antes que empiece a ejecutarse el ciclo. • Un total es una variable que se utiliza para acumular la suma de varios valores. Por lo general, las variables que se utilizan para almacenar totales se inicializan en cero antes de usarlas en un programa. • La declaración de una variable local debe aparecer antes de usarla en el método en el que está declarada. Una variable local no puede utilizarse fuera del método en el que se declaró. • Al dividir dos enteros se produce una división entera; la parte fraccionaria del cálculo se trunca.
  • 191. Sección 4.9 Cómo formular algoritmos: repetición controlada por un centinela • En la repetición controlada por un centinela, se utiliza un valor especial, conocido como valor centinela (valor de señal, valor de prueba o valor de bandera) para indicar el “fin de la entrada de datos”. • Debe elegirse un valor centinela que no pueda confundirse con un valor de entrada aceptable. • El método de refinamiento de arriba a abajo, paso a paso, es esencial para el desarrollo de programas bien estructu- rados. • Por lo general, la división entre cero es un error lógico que, si no se detecta, hace que el programa falle o que pro- duzca resultados inválidos. • Para realizar un cálculo de punto flotante con valores enteros, convierta uno de los enteros al tipo double. El uso de un operador de conversión de tipos de esta forma se denomina conversión explícita. • Java sabe cómo evaluar sólo las expresiones aritméticas en las que los tipos de los operandos son idénticos. Para ase- gurar que los operandos sean del mismo tipo, Java realiza una operación conocida como promoción (o conversión implícita) sobre los operandos seleccionados. En una expresión que contiene valores de los tipos int y double, los valores int se promueven a valores double para usarlos en la expresión. • Hay operadores de conversión de tipos disponibles para cualquier tipo. El operador de conversión de tipos se forma mediante la colocación de paréntesis alrededor del nombre de un tipo. Este operador es unario. Sección 4.11 Operadores de asignación compuestos • Java cuenta con varios operadores de asignación compuestos para abreviar las expresiones de asignación. Cualquier instrucción de la forma variable = variable operador expresión; en donde operador es uno de los operadores binarios +, -, *, / o %, puede escribirse en la forma variable operador= expresión; • El operador += suma el valor de la expresión que está a la derecha del operador, con el valor de la variable que está a la izquierda del operador, y almacena el resultado en la variable que está a la izquierda del operador. Sección 4.12 Operadores de incremento y decremento • Java cuenta con dos operadores unarios para sumar 1, o restar 1, al valor de una variable numérica. Éstos son el operador de incremento unario, ++, y el operador de decremento unario, --. • Un operador de incremento o decremento que se coloca antes de una variable es el operador de preincremento o predecremento, respectivamente. Un operador de incremento o decremento que se coloca después de una variable es el operador de postincremento o postdecremento, respectivamente. • El proceso de usar el operador de preincremento o predecremento para sumar o restar 1 se conoce como preincre- mentar o predecrementar, respectivamente. • Al preincrementar o predecrementar una variable, ésta se incrementa o decrementa por 1, y después se utiliza el nuevo valor de la variable en la expresión en la que aparece. • El proceso de usar el operador de postincremento o postdecremento para sumar o restar 1 se conoce como postin- crementar o postdecrementar, respectivamente. • Al postincrementar o postdecrementar una variable, el valor actual de ésta se utiliza en la expresión en la que aparece, y después el valor de la variable se incrementa o decrementa por 1. • Cuando se incrementa o decrementa una variable en una instrucción por sí sola, las formas de preincremento y postincremento tienen el mismo efecto, y las formas de predecremento y postdecremento tienen el mismo efecto. Sección 4.13 Tipos primitivos • Java requiere que todas las variables tengan un tipo. Por ende, Java se conoce como un lenguaje fuertemente tipifi- cado. • Como los diseñadores de Java desean que sea lo más portable posible, utilizan estándares reconocidos internacional- mente para los formatos de caracteres (Unicode) y números de punto flotante (IEEE 754). Sección 4.14 (Opcional) Ejemplo práctico de GUI y gráficos: creación de dibujos simples • El sistema de coordenadas de Java proporciona un esquema para identificar cada punto en la pantalla. De manera predeterminada, la esquina superior izquierda de un componente de la GUI tiene las coordenadas (0, 0). Resumen 153
  • 192. 154 Capítulo 4 Instrucciones de control: parte 1 • Un par de coordenadas se compone de una coordenada x (la coordenada horizontal) y una coordenada y (la coorde- nada vertical). La coordenada x es la ubicación horizontal que avanza de izquierda a derecha. La coordenada y es la ubicación vertical que avanza de arriba hacia abajo. • El eje x describe a todas las coordenadas horizontales, y el eje y a todas las coordenadas verticales. • Las unidades de las coordenadas se miden en píxeles. Un píxel es la unidad más pequeña de resolución de una pan- talla. • La clase Graphics (del paquete java.awt) proporciona varios métodos para dibujar texto y figuras en la pantalla. • La clase JPanel (del paquete javax.swing) proporciona un área en la que un programa puede hacer dibujos. • La palabra clave extends indica que una clase hereda de otra clase. La nueva clase empieza con los miembros exis- tentes (datos y métodos) de la clase existente. • La clase a partir de la cual la nueva clase hereda se conoce como la superclase, y la nueva clase se llama subclase. • Todo objeto JPanel tiene un método paintComponent, que el sistema llama automáticamente cada vez que necesita mostrar el objeto JPanel: cuando se muestra un JPanel por primera vez en la pantalla, cuando una ventana en la pantalla lo cubre y luego lo descubre, y cuando se cambia el tamaño de la ventana en la que aparece este objeto. • El método paintComponent requiere un argumento (un objeto Graphics), que el sistema proporciona por usted cuando llama a paintComponent. • La primera instrucción en cualquier método paintComponent que usted vaya a crear debe ser siempre super.paintComponent( g ); Esto asegura que el panel se despliegue de manera apropiada en la pantalla, antes de empezar a dibujar en él. • Los métodos getWidth y getHeight de JPanel devuelven la anchura y la altura de un objeto JPanel, respectiva- mente. • El método drawLine de Graphics dibuja una línea entre dos puntos representados por sus cuatro argumentos. Los primeros dos argumentos son las coordenadas x y y para un punto final de la línea, y los últimos dos argumentos son las coordenadas para el otro punto final de la línea. • Para mostrar un objeto JPanel en la pantalla, debe colocarlo en una ventana. Para crear una ventana, utilice un objeto de la clase JFrame, del paquete javax.swing. • El método setDefaultCloseOperation de JFrame con el argumento JFrame.EXIT_ON_CLOSE indica que la apli- cación debe terminar cuando el usuario cierre la ventana. • El método add de JFrame adjunta un componente de la GUI a un objeto JFrame. • El método setSize de JFrame establece la anchura y la altura del objeto JFrame. Terminología --, operador ?:, operador ++, operador +=, operador acción actividad (en UML) add, método de la clase JFrame (GUI) algoritmo anidamiento de estructuras de control apilamiento de estructuras de control bloque boolean, expresión boolean, tipo primitivo ciclo ciclo infinito cima círculo relleno (en UML) circunferencia (en UML) condición de continuación de ciclo condición de guardia (en UML) contador contador de ciclo control del programa conversión explícita conversión implícita coordenada horizontal (GUI) coordenada vertical (GUI) coordenada x coordenada y cuerpo de un ciclo decisión diagrama de actividad (en UML) división entera drawLine, método de la clase Graphics (GUI) eje x eje y ejecución secuencial error de sintaxis error fatal error lógico error lógico fatal error lógico no fatal estado de acción (en UML) estado final (en UML)
  • 193. estado inicial (en UML) estructura de repetición estructura de secuencia estructura de selección expresión condicional expresión de acción (en UML) extends false flecha de transición (en UML) flujo de trabajo getHeight, método de la clase JPanel (GUI) getWidth, método de la clase JPanel (GUI) goto, instrucción Graphics, clase (GUI) heredar de una clase existente if, instrucción de selección simple if...else, instrucción de selección doble inicialización instrucción de ciclo instrucción de control instrucción de repetición instrucción de selección instrucción de selección doble instrucción de selección múltiple instrucción de selección simple instrucciones de control anidadas instrucciones de control apiladas instrucciones de control de una sola entrada/una sola salida instrucciones if...else anidadas iteración JComponent, clase (GUI) JFrame, clase (GUI) JPanel, clase (GUI) lenguaje fuertemente tipificado línea punteada (en UML) modelo de programación acción/decisión nota (en UML) operador condicional (?:) operador de asignación compuesto operador de asignación compuesto de suma (+=) operador de conversión de tipos, (double) operador de conversión de tipos, (tipo) operador de conversión de tipos unario operador de decremento (--) operador de incremento (++) operador de multiplicación operador de postdecremento operador de postincremento operador de predecremento operador de preincremento operador ternario operadores de asignación compuestos aritméticos: +=, -=, *=, /= y %= orden en el que deben ejecutarse las acciones paintComponent, método de la clase JComponent (GUI) pequeño círculo (en UML) píxel (GUI) postdecrementar una variable postincrementar una variable predecrementar una variable preincrementar una variable primer refinamiento problema del else suelto procedimiento para resolver un problema programación estructurada promoción refinamiento de arriba a abajo, paso a paso repetición repetición controlada por centinela repetición controlada por un contador repetición definida repetición indefinida rombo (en UML) segundo refinamiento setDefaultCloseOperation, método de la clase JFrame (GUI) setSize, método de la clase JFrame (GUI) seudocódigo símbolo de decisión (en UML) símbolo de estado de acción (en UML) símbolo de fusión (en UML) sistema de coordenadas (GUI) tipos primitivos total transferencia de control transición (en UML) true truncar la parte fraccionaria de un cálculo valor centinela valor de bandera valor de prueba valor de señal variable de control while, instrucción de repetición Ejercicios de autoevaluación 4.1 Complete los siguientes enunciados: a) Todos los programas pueden escribirse en términos de tres tipos de estructuras de control: __________, __________ y __________. b) La instrucción __________ se utiliza para ejecutar una acción cuando una condición es verdadera, y otra acción cuando esa condición es falsa. Ejercicios de autoevaluación 155
  • 194. 156 Capítulo 4 Instrucciones de control: parte 1 c) Al proceso de repetir un conjunto de instrucciones un número específico de veces se le llama repetición __________. d) Cuando no se sabe de antemano cuántas veces se repetirá un conjunto de instrucciones, se puede usar un valor __________ para terminar la repetición. e) La estructura __________ está integrada en Java; de manera predeterminada, las instrucciones se ejecutan en el orden en el que aparecen. f) Todas las variables de instancia de los tipos char, byte, short, int, long, float y double reciben el valor __________ de manera predeterminada. g) Java es un lenguaje __________; requiere que todas las variables tengan un tipo. h) Si el operador de incremento se __________ de una variable, ésta se incrementa en 1 primero, y después su nuevo valor se utiliza en la expresión. 4.2 Conteste con verdadero o falso a cada una de las siguientes proposiciones; en caso de ser falso, explique por qué. a) Un algoritmo es un procedimiento para resolver un problema en términos de las acciones a ejecutar y el orden en el que se ejecutan. b) Un conjunto de instrucciones contenidas dentro de un par de paréntesis se denomina bloque. c) Una instrucción de selección especifica que una acción se repetirá, mientras cierta condición siga siendo verdadera. d) Una instrucción de control anidada aparece en el cuerpo de otra instrucción de control. e) Java cuenta con los operadores de asignación compuestos aritméticos +=, -=, *=, /= y %= para abreviar las expresiones de asignación. f) Los tipos primitivos (boolean, char, byte, short, int, long, float y double) son portables sólo en las plataformas Windows. g) Al proceso de especificar el orden en el que se ejecutan las instrucciones (acciones) en un programa se deno- mina control del programa. h) El operador de conversión de tipos unario (double) crea una copia entera temporal de su operando. i) Las variables de instancia de tipo boolean reciben el valor true de manera predeterminada. j) El seudocódigo ayuda a un programador a idear un programa, antes de tratar de escribirlo en un lenguaje de programación. 4.3 Escriba cuatro instrucciones distintas en Java, en donde cada una sume 1 a la variable entera x. 4.4 Escriba instrucciones en Java para realizar cada una de las siguientes tareas: a) Asignar la suma de x e y a z, e incrementar x en 1 después del cálculo. Use sólo una instrucción. b) Evaluar si la variable cuenta es mayor que 10. De ser así, imprimir "Cuenta es mayor que 10". c) Decrementar la variable x en 1, luego restarla a la variable total. Use sólo una instrucción. d) Calcular el residuo después de dividir q entre divisor, y asignar el resultado a q. Escriba esta instrucción de dos maneras distintas. 4.5 Escriba una instrucción en Java para realizar cada una de las siguientes tareas: a) Declarar las variables suma y x como de tipo int. b) Asignar 1 a la variable x. c) Asignar 0 a la variable suma. d) Sumar la variable x a suma y asignar el resultado a la variable suma. e) Imprimir la cadena "La suma es: ", seguida del valor de la variable suma. 4.6 Combine las instrucciones que escribió en el ejercicio 4.5 para formar una aplicación en Java que calcule e imprima la suma de los enteros del 1 al 10. Use una instrucción while para iterar a través de las instrucciones de cálculo e incremento. El ciclo debe terminar cuando el valor de x se vuelva 11. 4.7 Determine el valor de las variables en la siguiente instrucción, después de realizar el cálculo. Suponga que, cuando se empieza a ejecutar la instrucción, todas las variables son de tipo int y tienen el valor 5. producto *= x++; 4.8 Identifique y corrija los errores en cada uno de los siguientes fragmentos de código:
  • 195. a) while ( c <= 5 ) { producto *= c; ++c; b) if ( genero == 1 ) System.out.println( "Mujer" ); else; System.out.println( "Hombre" ); 4.9 ¿Qué está mal en la siguiente instrucción while? while ( z >= 0 ) suma += z; Respuestas a los ejercicios de autoevaluación 4.1 a) secuencia, selección, repetición. b) if...else. c) controlada por contador (o definida). d) centinela, de señal, de prueba o de bandera. e) secuencia. f) 0 (cero). g) fuertemente tipificado. h) coloca antes. 4.2 a) Verdadero. b) Falso. Un conjunto de instrucciones contenidas dentro de un par de llaves ({ y }) se denomina bloque. c) Falso. Una instrucción de repetición especifica que una acción se repetirá mientras que cierta condición siga siendo verdadera. d) Verdadero. e) Verdadero. f) Falso. Los tipos primitivos (boolean, char, byte, short, int, long, float y double) son portables a través de todas las plataformas de computadora que soportan Java. g) Verdadero. h) Falso. El operador de conversión de tipos unario (double) crea una copia temporal de punto flotante de su operando. i) Falso. Las variables de instancia de tipo boolean reciben el valor false de manera predeterminada. j) Verdadero. 4.3 x = x + 1; x += 1; ++x; x++; 4.4 a) z = x++ + y; b) if ( cuenta > 10 ) System.out.println( "Cuenta es mayor que 10" ); c) total -= --x; d) q %= divisor; q = q % divisor; 4.5 a) int suma, x; b) x = 1; c) suma = 0; d) suma += x; o suma = suma + x; e) System.out.printf( "La suma es: %dn", suma ); 4.6 El programa se muestra a continuación: 1 // Calcula la suma de los enteros del 1 al 10 2 public class Calcular 3 { 4 public static void main( String args[] ) 5 { 6 int suma; 7 int x; 8 9 x = 1; // inicializa x en 1 para contar 10 suma = 0; // inicializa suma en 0 para el total 11 12 while ( x <= 10 ) // mientras que x sea menor o igual que 10 13 { 14 suma += x; // suma x a suma 15 ++x; // incrementa x Respuestas a los ejercicios de autoevaluación 157
  • 196. 158 Capítulo 4 Instrucciones de control: parte 1 16 } // fin de while 17 18 System.out.printf( "La suma es: %dn", suma ); 19 } // fin de main 20 21 } // fin de la clase Calcular La suma es: 55 4.7 producto = 25, x = 6 4.8 a) Error: falta la llave derecha de cierre del cuerpo de la instrucción while. Corrección: Agregar una llave derecha de cierre después de la instrucción ++c;. b) Error: El punto y coma después de else produce un error lógico. La segunda instrucción de salida siempre se ejecutará. Corrección: Quitar el punto y coma después de else. 4.9 El valor de la variable z nunca se cambia en la instrucción while. Por lo tanto, si la condición de continuación de ciclo ( z >= 0 ) es verdadera, se crea un ciclo infinito. Para evitar que ocurra un ciclo infinito, z debe decrementar- se de manera que eventualmente se vuelva menor que 0. Ejercicios 4.10 Compare y contraste la instrucción if de selección simple y la instrucción de repetición while. ¿Cuál es la similitud en las dos instrucciones? ¿Cuál es su diferencia? 4.11 Explique lo que ocurre cuando un programa en Java trata de dividir un entero entre otro. ¿Qué ocurre con la parte fraccionaria del cálculo? ¿Cómo puede un programador evitar ese resultado? 4.12 Describa las dos formas en las que pueden combinarse las instrucciones de control. 4.13 ¿Qué tipo de repetición sería apropiada para calcular la suma de los primeros 100 enteros positivos? ¿Qué tipo de repetición sería apropiada para calcular la suma de un número arbitrario de enteros positivos? Describa brevemente cómo podría realizarse cada una de estas tareas. 4.14 ¿Cuál es la diferencia entre preincrementar y postincrementar una variable? 4.15 Identifique y corrija los errores en cada uno de los siguientes fragmentos de código. [Nota: puede haber más de un error en cada fragmento de código]. a) if ( edad >= 65 ); System.out.println( "Edad es mayor o igual que 65" ); else System.out.println( "Edad es menor que 65 )"; b) int x = 1, total; while ( x <= 10 ) { total += x; ++x; } c) while ( x <= 100 ) total += x; ++x; d) while ( y > 0 ) { System.out.println( y ); ++y; 4.16 ¿Qué es lo que imprime el siguiente programa?
  • 197. 1 public class Misterio 2 { 3 public static void main( String args[] ) 4 { 5 int y; 6 int x = 1; 7 int total = 0; 8 9 while ( x <= 10 ) 10 { 11 y = x * x; 12 System.out.println( y ); 13 total += y; 14 ++x; 15 } // fin de while 16 17 System.out.printf( "El total es %dn", total ); 18 } // fin de main 19 20 } // fin de la clase Misterio Para los ejercicios 4.17 a 4.20, realice cada uno de los siguientes pasos: a) Lea el enunciado del problema. b) Formule el algoritmo utilizando seudocódigo y el proceso de refinamiento de arriba a abajo, paso a paso. c) Escriba un programa en Java. d) Pruebe, depure y ejecute el programa en Java. e) Procese tres conjuntos completos de datos. 4.17 Los conductores se preocupan acerca del kilometraje de sus automóviles. Un conductor ha llevado el registro de varios reabastecimientos de gasolina, registrando los kilómetros conducidos y los litros usados en cada reabastecimiento. Desarrolle una aplicación en Java que reciba como entrada los kilómetros conducidos y los litros usados (ambos como enteros) por cada reabastecimiento. El programa debe calcular y mostrar los kilómetros por litro obtenidos en cada reabastecimiento, y debe imprimir el total de kilómetros por litro obtenidos en todos los reabastecimientos hasta este punto. Todos los cálculos del promedio deben producir resultados en números de punto flotante. Use la clase Scanner y la repetición controlada por centinela para obtener los datos del usuario. 4.18 Desarrolle una aplicación en Java que determine si alguno de los clientes de una tienda de departamentos se ha excedido del límite de crédito en una cuenta. Para cada cliente se tienen los siguientes datos: a) El número de cuenta. b) El saldo al inicio del mes. c) El total de todos los artículos cargados por el cliente en el mes. d) El total de todos los créditos aplicados a la cuenta del cliente en el mes. e) El límite de crédito permitido. El programa debe recibir como entrada cada uno de estos datos en forma de números enteros, debe calcular el nuevo saldo (= saldo inicial + cargos – créditos), mostrar el nuevo balance y determinar si éste excede el límite de crédito del cliente. Para los clientes cuyo límite de crédito sea excedido, el programa debe mostrar el mensaje "Se excedió el límite de su crédito". 4.19 Una empresa grande paga a sus vendedores mediante comisiones. Los vendedores reciben $200 por semana, más el 9% de sus ventas brutas durante esa semana. Por ejemplo, un vendedor que vende $5000 de mercancía en una semana, recibe $200 más el 9% de 5000, o un total de $650. Usted acaba de recibir una lista de los artículos vendidos por cada vendedor. Los valores de estos artículos son los siguientes: Artículo Valor 1 239.99 2 129.75 3 99.95 4 350.89 Ejercicios 159
  • 198. 160 Capítulo 4 Instrucciones de control: parte 1 Desarrolle una aplicación en Java que reciba como entrada los artículos vendidos por un vendedor durante la última semana, y que calcule y muestre los ingresos de ese vendedor. No hay límite en cuanto al número de artículos que un vendedor puede vender. 4.20 Desarrolle una aplicación en Java que determine el sueldo bruto para cada uno de tres empleados. La empresa paga la cuota normal en las primeras 40 horas de trabajo de cada empleado, y paga cuota y media en todas las horas trabajadas que excedan de 40. Usted recibe una lista de los empleados de la empresa, el número de horas que trabajó cada empleado la semana pasada y la tarifa por horas de cada empleado. Su programa debe recibir como entrada esta información para cada empleado, debe determinar y mostrar el sueldo bruto de cada empleado. Utilice la clase Scanner para introducir los datos. 4.21 El proceso de encontrar el valor más grande (es decir, el máximo de un grupo de valores) se utiliza frecuen- temente en aplicaciones de computadora. Por ejemplo, un programa para determinar el ganador de un concurso de ventas recibe como entrada el número de unidades vendidas por cada vendedor. El vendedor que haya vendido más unidades es el que gana el concurso. Escriba un programa en seudocódigo y después una aplicación en Java que reciba como entrada una serie de 10 números enteros, y que determine e imprima el mayor de los números. Su programa debe utilizar cuando menos las siguientes tres variables: a) contador: un contador para contar hasta 10 (es decir, para llevar el registro de cuántos números se han introducido, y para determinar cuando se hayan procesado los 10 números). b) numero: el entero más reciente introducido por el usuario. c) mayor: el número más grande encontrado hasta ahora. 4.22 Escriba una aplicación en Java que utilice ciclos para imprimir la siguiente tabla de valores: N 1 2 3 4 5 10*N 10 20 30 40 50 100*N 100 200 300 400 500 1000*N 1000 2000 3000 4000 5000 4.23 Utilizando una metodología similar a la del ejercicio 4.21, encuentre los dos valores más grandes de los 10 que se introdujeron. [Nota: puede introducir cada número sólo una vez]. 4.24 Modifique el programa de la figura 4.12 para validar sus entradas. Para cualquier entrada, si el valor introducido es distinto de 1 o 2, debe seguir iterando hasta que el usuario introduzca un valor correcto. 4.25 ¿Qué es lo que imprime el siguiente programa? 1 public class Misterio2 2 { 3 public static void main( String args[] ) 4 { 5 int cuenta = 1; 6 7 while ( cuenta <= 10 ) 8 { 9 System.out.println( cuenta % 2 == 1 ? "****" : "++++++++" ); 10 ++cuenta; 11 } // fin de while 12 } // fin de main 13 14 } // fin de la clase Misterio2 4.26 ¿Qué es lo que imprime el siguiente programa? 1 public class Misterio3 2 {
  • 199. 3 public static void main( String args[] ) 4 { 5 int fila = 10; 6 int columna; 7 8 while ( fila >= 1 ) 9 { 10 columna = 1; 11 12 while ( columna <= 10 ) 13 { 14 System.out.print( fila % 2 == 1 ? "<" : ">" ); 15 ++columna; 16 } // fin de while 17 18 --fila; 19 System.out.println(); 20 } // fin de while 21 } // fin de main 22 23 } // fin de la clase Misterio3 4.27 (Problema del else suelto) Determine la salida de cada uno de los siguientes conjuntos de código, cuando x es 9 y y es 11, y cuando x es 11 y y es 9. Observe que el compilador ignora la sangría en un programa en Java. Además, el compilador de Java siempre asocia un else con el if que le precede inmediatamente, a menos que se le indique de otra forma mediante la colocación de llaves ({}). A primera vista, el programador tal vez no esté seguro de cuál if correspon- de a cuál else; esta situación se conoce como el “problema del else suelto”. Hemos eliminado la sangría del siguiente código para hacer el problema más retador. [Sugerencia: aplique las convenciones de sangría que ha aprendido]. a) if ( x < 10 ) if ( y > 10 ) System.out.println( "*****" ); else System.out.println( "#####" ); System.out.println( "$$$$$" ); b) if ( x < 10 ) { if ( y > 10 ) System.out.println( "*****" ); } else { System.out.println( "#####" ); System.out.println( "$$$$$" ); } 4.28 (Otro problema de else suelto) Modifique el código dado para producir la salida que se muestra en cada parte del problema. Utilice las técnicas de sangría apropiadas. No haga modificaciones en el código, sólo inserte llaves o modifique la sangría del código. El compilador ignora la sangría en un programa en Java. Hemos eliminado la sangría en el código dado, para hacer el problema más retador. [Nota: es posible que no se requieran modificaciones en algunas de las partes]. if ( y == 8 ) if ( x == 5 ) System.out.println( "@@@@@" ); else System.out.println( "#####" ); System.out.println( "$$$$$" ); System.out.println( "&&&&&" ); Ejercicios 161
  • 200. 162 Capítulo 4 Instrucciones de control: parte 1 a) Suponiendo que x = 5 y y = 8, se produce la siguiente salida: @@@@@ $$$$$ &&&&& b) Suponiendo que x = 5 y y = 8, se produce la siguiente salida: @@@@@ c) Suponiendo que x = 5 y y = 8, se produce la siguiente salida: @@@@@ &&&&& d) Suponiendo que x = 5 y y = 7, se produce la siguiente salida. [Nota: las tres últimas instrucciones de salida después del else forman parte de un bloque.] ##### $$$$$ &&&&& 4.29 Escriba una aplicación que pida al usuario que introduzca el tamaño del lado de un cuadrado y que muestre un cuadrado hueco de ese tamaño, compuesto de asteriscos. Su programa debe funcionar con cuadrados que tengan lados de todas las longitudes entre 1 y 20. 4.30 (Palíndromos) Un palíndromo es una secuencia de caracteres que se lee igual al derecho y al revés. Por ejemplo, cada uno de los siguientes enteros de cinco dígitos es un palíndromo: 12321, 55555, 45554 y 11611. Escriba una aplicación que lea un entero de cinco dígitos y determine si es un palíndromo. Si el número no es de cinco dígitos, el programa debe mostrar un mensaje de error y permitir al usuario que introduzca un nuevo valor. 4.31 Escriba una aplicación que reciba como entrada un entero que contenga sólo 0s y 1s (es decir, un entero bina- rio), y que imprima su equivalente decimal. [Sugerencia: use los operadores residuo y división para elegir los dígitos del número binario uno a la vez, de derecha a izquierda. En el sistema numérico decimal, el dígito más a la derecha tiene un valor posicional de 1 y el siguiente dígito a la izquierda tiene un valor posicional de 10, después 100, después 1000, etcétera. El número decimal 234 puede interpretarse como 4 * 1 + 3 * 10 + 2 * 100. En el sistema numérico binario, el dígito más a la derecha tiene un valor posicional de 1, el siguiente dígito a la izquierda tiene un valor posicional de 2, luego 4, luego 8, etcétera. El equivalente decimal del número binario 1101 es 1 * 1 + 0 * 2 + 1 * 4 + 1 * 8, o 1 + 0 + 4 + 8, o 13]. 4.32 Escriba una aplicación que utilice sólo las instrucciones de salida System.out.print( "* " ); System.out.print( " " ); System.out.println(); para mostrar el patrón de tablero de damas que se muestra a continuación. Observe que una llamada al método Sys- tem.out.println sin argumentos hace que el programa imprima un solo carácter de nueva línea. [Sugerencia: se requieren estructuras de repetición]. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4.33 Escriba una aplicación que muestre en la ventana de comandos los múltiplos del entero 2 (es decir, 2, 4, 8, 16, 32, 64, etcétera). Su ciclo no debe terminar (es decir, debe crear un ciclo infinito). ¿Qué ocurre cuando ejecuta este programa? 4.34 ¿Qué está mal en la siguiente instrucción? Proporcione la instrucción correcta para sumar uno a la suma de x e y. System.out.println( ++(x + y) );
  • 201. 4.35 Escriba una aplicación que lea tres valores distintos de cero introducidos por el usuario, y que determine e imprima si podrían representar los lados de un triángulo. 4.36 Escriba una aplicación que lea tres enteros distintos de cero, determine e imprima si estos enteros podrían representar los lados de un triángulo rectángulo. 4.37 Una compañía desea transmitir datos a través del teléfono, pero le preocupa que sus teléfonos puedan estar intervenidos. Le ha pedido a usted que escriba un programa que cifre sus datos, de manera que éstos puedan trans- mitirse con más seguridad. Todos los datos se transmiten como enteros de cuatro dígitos. Su aplicación debe leer un entero de cuatro dígitos introducido por el usuario y cifrarlo de la siguiente manera: reemplace cada dígito con el resultado de sumar 7 al dígito y obtener el residuo después de dividir el nuevo valor entre 10. Luego intercambie el primer dígito con el tercero, e intercambie el segundo dígito con el cuarto. Después imprima el entero cifrado. Escriba una aplicación separada que reciba como entrada un entero de cuatro dígitos cifrado, y que lo descifre para formar el número original. 4.38 El factorial de un entero n no negativo se escribe como n! y se define de la siguiente manera: n! = n · (n – 1) · (n – 2) · ... · 1 (para valores de n mayores o iguales a 1) y n! = 1 (para n = 0) Por ejemplo, 5! = 5 · 4 · 3 · 2 · 1, que es 120. a) Escriba una aplicación que lea un entero no negativo, y calcule e imprima su factorial. b) Escriba una aplicación que estime el valor de la constante matemática e, utilizando la fórmula e 1 1 1! ---- - 1 2! ---- - 1 3! ---- - + + + + = c) Escriba una aplicación que calcule el valor de ex, utilizando la fórmula e x 1 x 1! ---- - x 2 2! ---- - x 3 3! ---- - + + + + = Ejercicios 163
  • 202. Instrucciones de control: parte 2 OBJETIVO S En este capítulo aprenderá a: Conocer los fundamentos acerca de la repetición controlada por un contador. Utilizar las instrucciones de repetición for y do...while para ejecutar instrucciones de manera repetitiva en un programa. Comprender la selección múltiple utilizando la instrucción de selección switch. Utilizar las instrucciones de control de programa break y continue para alterar el flujo de control. Utilizar los operadores lógicos para formar expresiones condicionales complejas en instrucciones de control. Q Q Q Q Q No todo lo que puede contarse cuenta, y no todo lo que cuenta puede contarse. —Albert Einstein ¿Quién puede controlar su destino? —William Shakespeare La llave usada siempre es brillante. —Benjamin Franklin La inteligencia... es la facultad de hacer objetos artificiales, especialmente herramientas para hacer herramientas. —Henri Bergson Cada ventaja en el pasado se juzga a la luz de la cuestión final. —Demóstenes 5
  • 203. 5.1 Introducción El capítulo 4 nos introdujo a los tipos de bloques de construcción disponibles para solucionar problemas. Utiliza- mos dichos bloques de construcción para emplear las técnicas, ya comprobadas, de la construcción de programas. En este capítulo continuaremos nuestra presentación de la teoría y los principios de la programación estructurada, presentando el resto de las instrucciones de control en Java. Las instrucciones de control que estudiaremos aquí y las que vimos en el capítulo 4 son útiles para crear y manipular objetos. En este capítulo demostraremos las instrucciones for, do...while y switch de Java. A través de una serie de ejemplos cortos en los que utilizaremos las instrucciones while y for, exploraremos los fundamentos acerca de la repetición controlada por contador. Dedicaremos una parte de este capítulo (y del capítulo 7) a expandir la clase LibroCalificaciones que presentamos en los capítulos 3 y 4. En especial, crearemos una versión de la clase LibroCalificaciones que utiliza una instrucción switch para contar el número de calificaciones equiva- lentes de A, B, C, D y F, en un conjunto de calificaciones numéricas introducidas por el usuario. Presentaremos las instrucciones de control de programa break y continue. Hablaremos sobre los operadores lógicos de Java, que nos permiten utilizar expresiones condicionales más complejas en las instrucciones de control. Por último, veremos un resumen de las instrucciones de control de Java y las técnicas ya probadas de solución de problemas que presentamos en éste y en el capítulo 4. 5.2 Fundamentos de la repetición controlada por contador Esta sección utiliza la instrucción de repetición while, presentada en el capítulo 4, para formalizar los elementos requeridos y llevar a cabo la repetición controlada por contador. Este tipo de repetición requiere: 1. Una variable de control (o contador de ciclo). 2. El valor inicial de la variable de control. 3. El incremento (o decremento) con el que se modifica la variable de control cada vez que pasa por el ciclo (lo que también se conoce como cada iteración del ciclo). 4. La condición de continuación de ciclo, que determina si el ciclo debe continuar o no. Para ver estos elementos de la repetición controlada por contador, considere la aplicación de la figura 5.1, que utiliza un ciclo para mostrar los números del 1 al 10. Observe que esta figura sólo contiene un método, main, que realiza todo el trabajo de la clase. Para la mayoría de las aplicaciones, en los capítulos 3 y 4 hemos preferido el uso de dos archivos separados: uno que declara una clase reutilizable (por ejemplo, Cuenta) y otro que instancia uno o más objetos de esa clase (por ejemplo, PruebaCuenta) y demuestra su funcionalidad. Sin embargo, en algunas ocasiones es más apropiado crear sólo una clase, cuyo método main ilustra en forma concisa un concepto básico. A lo largo de este capítulo, utilizaremos varios ejemplos de una clase, como el de la figura 5.1, para demos- trar la mecánica de las instrucciones de control de Java. 5.1 Introducción 5.2 Fundamentos de la repetición controlada por contador 5.3 Instrucción de repetición for 5.4 Ejemplos sobre el uso de la instrucción for 5.5 Instrucción de repetición do...while 5.6 Instrucción de selección múltiple switch 5.7 Instrucciones break y continue 5.8 Operadores lógicos 5.9 Resumen sobre programación estructurada 5.10 (Opcional) Ejemplo práctico de GUI y gráficos: dibujo de rectángulos y óvalos 5.11 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo identificar los estados y actividades de los objetos 5.12 Conclusión Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios Pla n g e ne r a l 5.2 Fundamentos de la repetición controlada por contador 165
  • 204. 166 Capítulo 5 Instrucciones de control: parte 2 En la figura 5.1, los elementos de la repetición controlada por contador se definen en las líneas 8, 10 y 13. La línea 8 declara la variable de control (contador) como un int, reserva espacio para esta variable en memoria y establece su valor inicial en 1. La variable contador también podría haberse declarado e inicializado con la siguientes instrucciones de declaración y asignación de variables locales: int contador; // declara contador contador = 1; // inicializa contador en 1 La línea 12 muestra el valor de la variable de control contador durante cada iteración del ciclo. La línea 13 incrementa la variable de control en 1, para cada iteración del ciclo. La condición de continuación de ciclo en la instrucción while (línea 10) evalúa si el valor de la variable de control es menor o igual que 10 (el valor final para el que la condición es true). Observe que el programa ejecuta el cuerpo de este while aun y cuando la variable de control sea 10. El ciclo termina cuando la variable de control es mayor a 10 (es decir, cuando contador se convierte en 11). Error común de programación 5.1 Debido a que los valores de punto flotante pueden ser aproximados, controlar los ciclos con variables de punto flotante puede producir valores imprecisos del contador y pruebas de terminación imprecisas. Tip para prevenir errores 5.1 Controle los ciclos de contador con enteros. Buena práctica de programación 5.1 Coloque líneas en blanco por encima y debajo de las instrucciones de control de repetición y selección, y aplique sangría a los cuerpos de las instrucciones para mejorar la legibilidad. El programa de la figura 5.1 puede hacerse más conciso si inicializamos contador en 0 en la línea 8, y pre- incrementamos contador en la condición while de la siguiente forma: while ( ++contador <= 10 ) // condición de continuación de ciclo System.out.printf( “%d “, contador ); 1 // Fig. 5.1: ContadorWhile.java 2 // Repetición controlada con contador, con la instrucción de repetición while. 3 4 public class ContadorWhile 5 { 6 public static void main( String args[] ) 7 { 8 int contador = 1; // declara e inicializa la variable de control 9 10 while ( contador <= 10 ) // condición de continuación de ciclo 11 { 12 System.out.printf( “%d “, contador ); 13 ++contador; // incrementa la variable de control en 1 14 } // fin de while 15 16 System.out.println(); // imprime una nueva línea 17 } // fin de main 18 } // fin de la clase ContadorWhile 1 2 3 4 5 6 7 8 9 10 Figura 5.1 | Repetición controlada con contador, con la instrucción de repetición while.
  • 205. 5.3 Instrucción de repetición for 167 Este código ahorra una instrucción (y elimina la necesidad de usar llaves alrededor del cuerpo del ciclo), ya que la condición de while realiza el incremento antes de evaluar la condición. (En la sección 4.12 vimos que la prece- dencia de ++ es mayor que la de <=). La codificación en esta forma tan condensada requiere de práctica y puede hacer que el código sea más difícil de leer, depurar, modificar y mantener, así que en general, es mejor evitarla. Observación de ingeniería de software 5.1 “Mantener las cosas simples” es un buen consejo para la mayoría del código que usted escriba. 5.3 Instrucción de repetición for La sección 5.2 presentó los aspectos esenciales de la repetición controlada por contador. La instrucción while puede utilizarse para implementar cualquier ciclo controlado por un contador. Java también cuenta con la ins- trucción de repetición for, que especifica los detalles de la repetición controlada por contador en una sola línea de código. La figura 5.2 reimplementa la aplicación de la figura 5.1, usando la instrucción for. El método main de la aplicación opera de la siguiente manera: cuando la instrucción for (líneas 10 y 11) comienza a ejecutarse, la variable de control contador se declara e inicializa en 1 (en la sección 5.2 vimos que los primeros dos elementos de la repetición controlada por un contador son la variable de control y su valor inicial). A continuación, el programa verifica la condición de continuación de ciclo, contador <= 10, la cual se encuentra entre los dos signos de punto y coma requeridos. Como el valor inicial de contador es 1, al principio la condición es verdadera. Por lo tanto, la instrucción del cuerpo (línea 11) muestra el valor de la variable de control contador, que es 1. Después de ejecutar el cuerpo del ciclo, el programa incrementa a contador en la expresión contador++, la cual aparece a la derecha del segundo signo de punto y coma. Después, la prueba de continuación de ciclo se ejecuta de nuevo para determinar si el programa debe continuar con la siguiente iteración del ciclo. En este punto, el valor de la variable de control es 2, por lo que la condición sigue siendo verdadera (el valor final no se excede); así, el programa ejecuta la instrucción del cuerpo otra vez (es decir, la siguiente iteración del ciclo). Este proceso continúa hasta que se muestran en pantalla los números del 1 al 10 y el valor de contador se vuelve 11, con lo cual falla la prueba de continuación de ciclo y termina la repetición (después de 10 repeticio- nes del cuerpo del ciclo en la línea 11). Después, el programa ejecuta la primera instrucción después del for; en este caso, la línea 13. Observe que la figura 5.2 utiliza (en la línea 10) la condición de continuación de ciclo contador <= 10. Si usted especificara por error contador < 10 como la condición, el ciclo sólo iteraría nueve veces. A este error lógico común se le conoce como error por desplazamiento en 1. 1 // Fig. 5.2: ContadorFor.java 2 // Repetición controlada con contador, con la instrucción de repetición for. 3 4 public class ContadorFor 5 { 6 public static void main( String args[] ) 7 { 8 // el encabezado de la instrucción for incluye la inicialización, 9 // la condición de continuación de ciclo y el incremento 10 for ( int contador = 1; contador <= 10; contador++ ) 11 System.out.printf( "%d ", contador ); 12 13 System.out.println(); // imprime una nueva línea 14 } // fin de main 15 } // fin de la clase ContadorFor 1 2 3 4 5 6 7 8 9 10 Figura 5.2 | Repetición controlada con contador, con la instrucción de repetición for.
  • 206. 168 Capítulo 5 Instrucciones de control: parte 2 Error común de programación 5.2 Utilizar un operador relacional incorrecto o un valor final incorrecto de un contador de ciclo en la condición de continuación de ciclo de una instrucción de repetición puede producir un error por desplazamiento en 1. Buena práctica de programación 5.2 Utilizar el valor final en la condición de una instrucción while o for con el operador relacional <= nos ayuda a evi- tar los errores por desplazamiento en 1. Para un ciclo que imprime los valores del 1 al 10, la condición de continua- ción de ciclo debe ser contador <= 10, en vez de contador < 10 (lo cual produce un error por desplazamiento en uno) o contador <11 (que es correcto). Muchos programadores prefieren el llamado conteo con base cero, en el cual para contar 10 veces, contador se inicializaría en cero y la prueba de continuación de ciclo sería contador < 10. La figura 5.3 analiza con más detalle la instrucción for de la figura 5.2. A la primera línea del for (incluyen- do la palabra clave for y todo lo que está entre paréntesis después de ésta), la línea 10 de la figura 5.2, se le llama algunas veces encabezado de la instrucción for, o simplemente encabezado del for. Observe que el encabezado del for “se encarga de todo”: especifica cada uno de los elementos necesarios para la repetición controlada por un contador con una variable de control. Si hay más de una instrucción en el cuerpo del for, se requieren llaves ({ y }) para definir el cuerpo del ciclo. Valor inicial de la variable de control Condición de continuación de ciclo Incremento de la variable de control Palabra clave for Variable de control Separador de punto y coma requerido Separador de punto y coma requerido for ( int contador = 1; contador <= 10; contador++ ) Figura 5.3 | Componentes del encabezado de la instrucción for. El formato general de la instrucción for es for ( inicialización; condiciónDeContinuaciónDeCiclo; incremento ) instrucción en donde la expresión inicialización nombra a la variable de control de ciclo y proporciona su valor inicial, la con- diciónDeContinuaciónDeCiclo es la condición que determina si el ciclo debe seguir ejecutándose, y el incremento modifica el valor de la variable de control (ya sea un incremento o un decremento), de manera que la condición de continuación de ciclo se vuelva falsa en un momento dado. Los dos signos de punto y coma en el encabezado del for son requeridos. Error común de programación 5.3 Utilizar comas en vez de los dos signos de punto y coma requeridos en el encabezado de una instrucción for es un error de sintaxis. En la mayoría de los casos, la instrucción for puede representarse con una instrucción while equivalente, de la siguiente manera: inicialización; while ( condiciónDeContinuaciónDeCiclo )
  • 207. { instrucción incremento; } En la sección 5.7 veremos un caso para el cual no es posible representar una instrucción for con una instrucción while equivalente. Por lo general, las instrucciones for se utilizan para la repetición controlada por un contador, y las instruc- ciones while se utilizan para la repetición controlada por un centinela. No obstante, while y for pueden utili- zarse para cualquiera de los dos tipos de repetición. Si la expresión de inicialización en el encabezado del for declara la variable de control (es decir, si el tipo de la variable de control se especifica antes del nombre de la variable, como en la figura 5.2), la variable de control puede utilizarse sólo en esa instrucción for; no existirá fuera de esta instrucción. Este uso restringido del nombre de la variable de control se conoce como el alcance de la variable. El alcance de una variable define en dónde puede utilizarse en un programa. Por ejemplo, una variable local sólo puede utilizarse en el método que declara a esa variable, y sólo a partir del punto de declaración, hasta el final del método. En el capítulo 6, Métodos: un análisis más detallado, veremos con detalle el concepto de alcance. Error común de programación 5.4 Cuando se declara la variable de control de una instrucción for en la sección de inicialización del encabezado del for, si se utiliza la variable de control fuera del cuerpo del for se produce un error de compilación. Las tres expresiones en un encabezado for son opcionales. Si se omite la condiciónDeContinuaciónDeCiclo, Java asume que esta condición siempre será verdadera, con lo cual se crea un ciclo infinito. Podríamos omitir la expresión de inicialización si el programa inicializa la variable de control antes del ciclo. Podríamos omitir la expresión de incremento si el programa calcula el incremento mediante instrucciones dentro del cuerpo del ciclo, o si no se necesita un incremento. La expresión de incremento en un for actúa como si fuera una instrucción independiente al final del cuerpo del for. Por lo tanto, las expresiones contador = contador + 1 contador += 1 ++contador contador++ son expresiones de incremento equivalentes en una instrucción for. Muchos programadores prefieren conta- dor++, ya que es concisa y además un ciclo for evalúa su expresión de incremento después de la ejecución de su cuerpo. Por ende, la forma de postincremento parece más natural. En este caso, la variable que se incrementa no aparece en una expresión más grande, por lo que los operadores de preincremento y postdecremento tienen en realidad el mismo efecto. Tip de rendimiento 5.1 Hay una ligera ventaja de rendimiento al utilizar el operador de preincremento, pero si elije el operador de postincre- mento debido a que parece ser más natural (como en el encabezado de un for), los compiladores con optimización generarán código byte de Java que utilice la forma más eficiente, de todas maneras. Buena práctica de programación 5.3 En muchos casos, los operadores de preincremento y postincremento se utilizan para sumar 1 a una variable en una instrucción por sí sola. En estos casos el efecto es idéntico, sólo que el operador de preincremento tiene una ligera ventaja de rendimiento. Dado que el compilador por lo general optimiza el código que usted escribe para ayudarlo a obtener el mejor rendimiento, puede usar cualquiera de los dos operadores (preincremento o postincremento) con el que se sienta más cómodo en estas situaciones. Error común de programación 5.5 Colocar un punto y coma inmediatamente a la derecha del paréntesis derecho del encabezado de un for convierte el cuerpo de ese for en una instrucción vacía. Por lo general esto es un error lógico. 5.3 Instrucción de repetición for 169
  • 208. 170 Capítulo 5 Instrucciones de control: parte 2 Tip para prevenir errores 5.2 Los ciclos infinitos ocurren cuando la condición de continuación de ciclo en una instrucción de repetición nunca se vuelve false. Para evitar esta situación en un ciclo controlado por un contador, debe asegurarse que la variable de control se incremente (o decremente) durante cada iteración del ciclo. En un ciclo controlado por centinela, asegúrese que el valor centinela se introduzca en algún momento dado. Las porciones correspondientes a la inicialización, la condición de continuación de ciclo y el incremento de una instrucción for pueden contener expresiones aritméticas. Por ejemplo, suponga que x = 2 y y = 10; si x y y no se modifican en el cuerpo del ciclo, entonces la instrucción for ( int j = x; j <= 4 * x * y; j += y / x ) es equivalente a la instrucción for ( int j = 2; j <= 80; j += 5 ) El incremento de una instrucción for también puede ser negativo, en cuyo caso sería un decremento y el ciclo contaría en orden descendente. Si al principio la condición de continuación de ciclo es false, el programa no ejecutará el cuerpo de la ins- trucción for, sino que la ejecución continuará con la instrucción que siga inmediatamente después del for. Con frecuencia, los programas muestran en pantalla el valor de la variable de control o lo utilizan en cálculos dentro del cuerpo del ciclo, pero este uso no es obligatorio. Por lo general, la variable de control se utiliza para controlar la repetición sin que se le mencione dentro del cuerpo del for. Tip para prevenir errores 5.3 Aunque el valor de la variable de control puede cambiarse en el cuerpo de un ciclo for, evite hacerlo, ya que esta práctica puede llevarlo a cometer errores sutiles. El diagrama de actividad de UML de la instrucción for es similar al de la instrucción while (figura 4.4). La figura 5.4 muestra el diagrama de actividad de la instrucción for de la figura 5.2. El diagrama hace evidente que la inicialización ocurre sólo una vez antes de evaluar la condición de continuación de ciclo por primera vez, y que el incremento ocurre cada vez que se realiza una iteración, después de que se ejecuta la instrucción del cuerpo. Determina si el ciclo debe continuar System.out.printf( “%d ”, contador ); [contador > 10] [contador <= 10] int contador = 1 contador++ Muestra en pantalla el valor del contador Inicializa la variable de control Incrementa la variable de control Figura 5.4 | Diagrama de actividad de UML para la instrucción for de la figura 5.2.
  • 209. 5.4 Ejemplos sobre el uso de la instrucción for Los siguientes ejemplos muestran técnicas para modificar la variable de control en una instrucción for. En cada caso, escribimos el encabezado for apropiado. Observe el cambio en el operador relacional para los ciclos que decrementan la variable de control. a) Modificar la variable de control de 1 a 100 en incrementos de 1. for ( int i = 1; i <= 100; i++ ) b) Modificar la variable de control de 100 a 1 en decrementos de 1. for ( int i = 100; i >= 1; i-- ) c) Modificar la variable de control de 7 a 77 en incrementos de 7. for ( int i = 7; i <= 77; i += 7 ) d) Modificar la variable de control de 20 a 2 en decrementos de 2. for ( int i = 20; i >= 2; i -= 2 ) e) Modificar la variable de control con la siguiente secuencia de valores: 2, 5, 8, 11, 14, 17, 20. for ( int i = 2; i <= 20; i += 3 ) f) Modificar la variable de control con la siguiente secuencia de valores: 99, 88, 77, 66, 55, 44, 33, 22, 11, 0. for ( int i = 99; i >= 0; i -= 11 ) Error común de programación 5.6 No utilizar el operador relacional apropiado en la condición de continuación de un ciclo que cuente en forma regresiva (como usar i <= 1 en lugar de i >= 1 en un ciclo que cuente en forma regresiva hasta llegar a 1) es generalmente un error lógico. Aplicación: sumar los enteros pares del 2 al 20 Ahora consideremos dos aplicaciones de ejemplo que demuestran usos simples de la instrucción for. La aplica- ción de la figura 5.5 utiliza una instrucción for para sumar los enteros pares del 2 al 20 y guardar el resultado en una variable int llamada total. 1 // Fig. 5.5: Suma.java 2 // Sumar enteros con la instrucción for. 3 4 public class Suma 5 { 6 public static void main( String args[] ) 7 { 8 int total = 0; // inicializa el total 9 10 // total de los enteros pares del 2 al 20 11 for ( int numero = 2; numero <= 20; numero += 2 ) 12 total += numero; 13 14 System.out.printf( “La suma es %dn”, total ); // muestra los resultados 15 } // fin de main 16 } // fin de la clase Suma La suma es 110 Figura 5.5 | Sumar enteros con la instrucción for. 5.4 Ejemplos sobre el uso de la instrucción for 171
  • 210. 172 Capítulo 5 Instrucciones de control: parte 2 Las expresiones de inicialización e incremento pueden ser listas separadas por comas de expresiones que nos permitan utilizar varias expresiones de inicialización, o varias expresiones de incremento. Por ejemplo, aunque esto no se recomienda, el cuerpo de la instrucción for en las líneas 11 y 12 de la figura 5.5 podría mezclarse con la porción del incremento del encabezado for mediante el uso de una coma, como se muestra a continuación: for ( int numero = 2; numero <= 20; total += numero, numero += 2 ) ; // instrucción vacía Buena práctica de programación 5.4 Limite el tamaño de los encabezados de las instrucciones de control a una sola línea, si es posible. Aplicación: cálculo del interés compuesto La siguiente aplicación utiliza la instrucción for para calcular el interés compuesto. Considere el siguiente pro- blema: Una persona invierte $1000.00 en una cuenta de ahorro que produce el 5% de interés. Supo- niendo que todo el interés se deposita en la cuenta, calcule e imprima el monto de dinero en la cuenta al final de cada año, durante 10 años. Use la siguiente fórmula para determinar los montos: c = p(1 + r)n en donde p es el monto que se invirtió originalmente (es decir, el monto principal) r es la tasa de interés anual (por ejemplo, use 0.05 para el 5%) n es el número de años c es la cantidad depositada al final del n-ésimo año. Este problema implica el uso de un ciclo que realiza los cálculos indicados para cada uno de los 10 años que el dinero permanece depositado. La solución es la aplicación que se muestra en la figura 5.6. Las líneas 8 a 10 en el método main declaran las variables double llamadas monto, principal y tasa, e inicializan principal con 1000.0 y tasa con 0.05. Java trata a las constantes de punto flotante, como 1000.0 y 0.05, como de tipo double. De manera similar, Java trata a las constantes de números enteros, como 7 y -22, como de tipo int. La línea 13 imprime en pantalla los encabezados para las dos columnas de resultados de esta aplicación. La primera columna muestra el año y la segunda, la cantidad depositada al final de ese año. Observe que utilizamos el especificador de formato %20s para mostrar en pantalla el objeto String “Monto en deposito”. El entero 20 después del % y el carácter de conversión s indica que el valor a imprimir debe mostrarse con una anchura de campo de 20; esto es, printf debe mostrar el valor con al menos 20 posiciones de caracteres. Si el valor a imprimir tiene una anchura menor a 20 posiciones de caracteres (en este ejemplo son 17 caracteres), el valor se justifica a la derecha en el campo de manera predeterminada. Si el valor anio a imprimir tuviera una anchura mayor a cuatro posiciones de caracteres, la anchura del campo se extendería a la derecha para dar cabida a todo el valor; esto desplazaría al campo monto a la derecha, con lo que se desacomodarían las columnas ordenadas de nuestros resultados tabulares. Para indicar que los valores deben imprimirse justificados a la izquierda, sólo hay que anteponer a la anchura de campo la bandera de formato de signo negativo (–). La instrucción for (líneas 16 a 23) ejecuta su cuerpo 10 veces, con lo cual la variable de control anio varía de 1 a 10, en incrementos de 1. Este ciclo termina cuando la variable de control anio se vuelve 11 (observe que anio representa a la n en el enunciado del problema). Las clases proporcionan métodos que realizan tareas comunes sobre los objetos. De hecho, la mayoría de los métodos a llamar deben pertenecer a un objeto específico. Por ejemplo, para imprimir texto en la figura 5.6, la línea 13 llama al método printf en el objeto System.out. Muchas clases también cuentan con métodos que realizan tareas comunes y no requieren objetos. En la sección 3.9 vimos que a estos métodos se les llama static. Por ejemplo, Java no incluye un operador de exponenciación, por lo que los diseñadores de la clase Math definie- ron el método static llamado pow para elevar un valor a una potencia. Para llamar a un método static debe especificar el nombre de la clase, seguido de un punto (.) y el nombre del método, como en NombreClase.nombreMétodo ( argumentos )
  • 211. En el capítulo 6 aprenderá a implementar métodos static en sus propias clases. Utilizamos el método static pow de la clase Math para realizar el cálculo del interés compuesto en la figura 5.6. Math.pow(x, y) calcula el valor de x elevado a la y-ésima potencia. El método recibe dos argumentos double y devuelve un valor double. La línea 19 realiza el cálculo c = p(1 + r)n, en donde c es monto, p es principal, r es tasa y n es anio. Después de cada cálculo, la línea 22 imprime en pantalla el año y el monto depositado al final de ese año. El año se imprime en una anchura de campo de cuatro caracteres (según lo especificado por %4d). El monto se imprime como un número de punto flotante con el especificador de formato %,20.2f. La bandera de formato coma (,) indica que el valor debe imprimirse con un separador de agrupamiento. El separador que se utiliza real- mente es específico de la configuración regional del usuario (es decir, el país). Por ejemplo, en los Estados Unidos, el número se imprimirá usando comas para separar cada tres dígitos, y un punto decimal para separar la parte fraccionaria del número, como en 1,234.45. El número 20 en la especificación de formato indica que el valor debe imprimirse justificado a la derecha, con una anchura de campo de 20 caracteres. El .2 especifica la precisión del número con formato; en este caso, el número se redondea a la centésima más cercana y se imprime con dos dígitos a la derecha del punto decimal. 1 // Fig. 5.6: Interes.java 2 // Cálculo del interés compuesto con for. 3 4 public class Interes 5 { 6 public static void main( String args[] ) 7 { 8 double monto; // Monto depositado al final de cada año 9 double principal = 1000.0; // monto inicial antes de los intereses 10 double tasa = 0.05; // tasa de interés 11 12 // muestra los encabezados 13 System.out.printf( "s%20sn", "Anio", "Monto en deposito" ); 14 15 // calcula el monto en deposito para cada uno de diez años 16 for ( int anio = 1; anio <= 10; anio++ ) 17 { 18 // calcula el nuevo monto para el año especificado 19 monto = principal * Math.pow( 1.0 + tasa, anio ); 20 21 // muestra el año y el monto 22 System.out.printf( "%4d%,20.2fn", anio, monto ); 23 } // fin de for 24 } // fin de main 25 } // fin de la clase Interes Anio Monto en deposito 1 1,050.00 2 1,102.50 3 1,157.63 4 1,215.51 5 1,276.28 6 1,340.10 7 1,407.10 8 1,477.46 9 1,551.33 10 1,628.89 Figura 5.6 | Cálculo del interés compuesto con for. 5.4 Ejemplos sobre el uso de la instrucción for 173
  • 212. 174 Capítulo 5 Instrucciones de control: parte 2 En este ejemplo declaramos las variables monto, capital y tasa de tipo double. Estamos tratando con partes fraccionales de dólares y, por ende, necesitamos un tipo que permita puntos decimales en sus valores. Por desgracia, los números de punto flotante pueden provocar problemas. He aquí una sencilla explicación de lo que puede salir mal al utilizar double (o float) para representar montos en dólares (suponiendo que los montos en dólares se muestran con dos dígitos a la derecha del punto decimal): dos montos en dólares tipo double alma- cenados en la máquina podrían ser 14.234 (que por lo general se redondea a 14.23 para fines de mostrarlo en pantalla) y 18.673 (que por lo general se redondea a 18.67 para fines de mostrarlo en pantalla). Al sumar estos montos, producen una suma interna de 32.907, que por lo general se redondea a 32.91 para fines de mostrarlo en pantalla. Por lo tanto, sus resultados podrían aparecer como 14.23 + 18.67 ------- 32.91 pero una persona que sume los números individuales, como se muestran, esperaría que la suma fuera de 32.90. ¡Ya ha sido advertido! Buena práctica de programación 5.5 No utilice variables de tipo double (o float) para realizar cálculos monetarios precisos. La imprecisión de los núme- ros de punto flotante puede provocar errores. En los ejercicios usaremos enteros para realizar cálculos monetarios. Algunos distribuidores independientes venden bibliotecas de clase que realizan cálculos monetarios precisos. Además, la API de Java cuenta con la clase java.math.BigDecimal para realizar cálculos con valores de punto flotante y precisión arbitraria. Observe que el cuerpo de la instrucción for contiene el cálculo 1.0 + tasa, el cual aparece como argumento para el método Math.pow. De hecho, este cálculo produce el mismo resultado cada vez que se realiza una itera- ción en el ciclo, por lo que repetir el cálculo en todas las iteraciones del ciclo es un desperdicio. Tip de rendimiento 5.2 En los ciclos, evite cálculos para los cuales el resultado nunca cambia; dichos cálculos, por lo general, deben colocarse antes del ciclo. [Nota: actualmente, muchos de los compiladores con optimización colocan dichos cálculos fuera de los ciclos en el código compilado]. 5.5 Instrucción de repetición do...while La instrucción de repetición do...while es similar a la instrucción while, ya que el programa evalúa la condi- ción de continuación de ciclo al principio, antes de ejecutar el cuerpo del ciclo. Si la condición es falsa, el cuerpo nunca se ejecuta. La instrucción do...while evalúa la condición de continuación de ciclo después de ejecutar el cuerpo del ciclo; por lo tanto, el cuerpo siempre se ejecuta por lo menos una vez. Cuando termina una instrucción do...while, la ejecución continúa con la siguiente instrucción en la secuencia. La figura 5.7 utiliza una instruc- ción do...while para imprimir los números del 1 al 10. La línea 8 declara e inicializa la variable de control contador. Al entrar a la instrucción do...while, la línea 12 imprime el valor de contador y la 13 incrementa a contador. Después, el programa evalúa la prueba de continuación de ciclo al final del mismo (línea 14). Si la condición es verdadera, el ciclo continúa a partir de la primera instrucción del cuerpo en la instrucción do...while (línea 12). Si la condición es falsa, el ciclo termina y el programa continúa con la siguiente instrucción después del ciclo. La figura 5.8 contiene el diagrama de actividad de UML para la instrucción do...while. Este diagrama hace evidente que la condición de continuación de ciclo no se evalúa sino hasta después que el ciclo ejecuta el estado de acción, por lo menos una vez. Compare este diagrama de actividad con el de la instrucción while (figura 4.4). No es necesario utilizar llaves en la estructura de repetición do...while si sólo hay una instrucción en el cuerpo. Sin embargo, la mayoría de los programadores incluyen las llaves para evitar la confusión entre las ins- trucciones while y do...while. Por ejemplo: while ( condición )
  • 213. generalmente es la primera línea de una instrucción while. Una instrucción do...while sin llaves, alrededor de un cuerpo con una sola instrucción, aparece así: do instrucción while ( condición ); 1 // Fig. 5.7: PruebaDoWhile.java 2 // La instrucción de repetición do...while. 3 4 public class PruebaDoWhile 5 { 6 public static void main( String args[] ) 7 { 8 int contador = 1; // inicializa contador 9 10 do 11 { 12 System.out.printf( "%d ", contador ); 13 ++contador; 14 } while ( contador <= 10 ); // fin de do...while 15 16 System.out.println(); // imprime una nueva línea 17 } // fin de main 18 } // fin de la clase PruebaDoWhile 1 2 3 4 5 6 7 8 9 10 Figura 5.7 | La instrucción de repetición do...while. Figura 5.8 | Diagrama de actividad de UML de la instrucción de repetición do...while. Determina si debe continuar el ciclo [contador > 10] [contador <= 10] ++contador Muestra el valor del contador Incrementa la variable de control System.out.printf( “%d ”, contador ); 5.5 Instrucción de repetición do...while 175
  • 214. 176 Capítulo 5 Instrucciones de control: parte 2 lo cual puede ser confuso. Un lector podría malinterpretar la última línea [while( condición );], como una ins- trucción while que contiene una instrucción vacía (el punto y coma por sí solo). Por ende, la instrucción do... while con una instrucción en su cuerpo se escribe generalmente así: do { Instrucción } while ( condición ); Buena práctica de programación 5.6 Incluya siempre las llaves en una instrucción do...while, aun y cuando éstas no sean necesarias. Esto ayuda a eliminar la ambigüedad entre las instrucciones while y do...while que contienen sólo una instrucción. 5.6 Instrucción de selección múltiple switch En el capítulo 4 hablamos sobre la instrucción if de selección simple y la instrucción if...else de selección doble. Java cuenta con la instrucción switch de selección múltiple para realizar distintas acciones, con base en los posibles valores de una variable o expresión entera. Cada acción se asocia con un valor de una expresión entera constante (es decir, un valor constante de tipo byte, short, int o char, pero no long) que la variable o expre- sión en la que se basa la instrucción switch pueda asumir. La clase LibroCalificaciones con la instrucción switch para contar las calificaciones A, B, C, D y F La figura 5.9 contiene una versión mejorada de la clase LibroCalificaciones que presentamos en el capítulo 3 y desarrollamos un poco más en el capítulo 4. La versión de la clase que presentamos ahora no sólo calcula el promedio de un conjunto de calificaciones numéricas introducidas por el usuario, sino que utiliza una instrucción switch para determinar si cada calificación es el equivalente de A, B, C, D o F, y para incrementar el contador de la calificación apropiada. La clase también imprime en pantalla un resumen del número de estudiantes que recibieron cada calificación. La figura 5.10 muestra la entrada y la salida de ejemplo de la aplicación PruebaLi- broCalificaciones, que utiliza la clase LibroCalificaciones para procesar un conjunto de calificaciones. 1 // Fig. 5.9: LibroCalificaciones.java 2 // La clase LibroCalificaciones usa la instrucción switch para contar las calificaciones A, B, C, D y F. 3 import java.util.Scanner; // el programa usa la clase Scanner 4 5 public class LibroCalificaciones 6 { 7 private String nombreDelCurso; // nombre del curso que representa este LibroCalificaciones 8 private int total; // suma de las calificaciones 9 private int contadorCalif; // número de calificaciones introducidas 10 private int aCuenta; // cuenta de calificaciones A 11 private int bCuenta; // cuenta de calificaciones B 12 private int cCuenta; // cuenta de calificaciones C 13 private int dCuenta; // cuenta de calificaciones D 14 private int fCuenta; // cuenta de calificaciones F 15 16 // el constructor inicializa nombreDelCurso; 17 // las variables de instancia int se inicializan en 0 de manera predeterminada 18 public LibroCalificaciones( String nombre ) 19 { Figura 5.9 | Clase LibroCalificaciones que utiliza una instrucción switch para contar las calificaciones A, B, C, D y F. (Parte 1 de 3).
  • 215. 20 nombreDelCurso = nombre; // inicializa nombreDelCurso 21 } // fin del constructor 22 23 // método para establecer el nombre del curso 24 public void establecerNombreDelCurso( String nombre ) 25 { 26 nombreDelCurso = nombre; // almacena el nombre del curso 27 } // fin del método establecerNombreDelCurso 28 29 // método para obtener el nombre del curso 30 public String obtenerNombreDelCurso() 31 { 32 return nombreDelCurso; 33 } // fin del método obtenerNombreDelCurso 34 35 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones 36 public void mostrarMensaje() 37 { 38 // obtenerNombreDelCurso obtiene el nombre del curso 39 System.out.printf( "Bienvenido al libro de calificaciones paran%s!nn", 40 obtenerNombreDelCurso() ); 41 } // fin del método mostrarMensaje 42 43 // introduce un número arbitrario de calificaciones del usuario 44 public void introducirCalif() 45 { 46 Scanner entrada = new Scanner( System.in ); 47 48 int calificacion; // calificación introducida por el usuario 49 50 System.out.printf( "%sn%sn %sn %sn", 51 "Escriba las calificaciones enteras en el rango de 0 a 100.", 52 "Escriba el indicador de fin de archivo para terminar la entrada:", 53 "En UNIX/Linux/Mac OS X escriba <ctrl> d y después oprima Intro", 54 "En Windows escriba <ctrl> z y después oprima Intro" ); 55 56 // itera hasta que el usuario introduzca el indicador de fin de archivo 57 while ( entrada.hasNext() ) 58 { 59 calificacion = entrada.nextInt(); // lee calificación 60 total += calificacion; // suma calificación a total 61 ++contadorCalif; // incrementa el número de calificaciones 62 63 // llama al método para incrementar el contador apropiado 64 incrementarContadorCalifLetra( calificacion ); 65 } // fin de while 66 } // fin del método introducirCalif 67 68 // suma 1 al contador apropiado para la calificación especificada 69 public void incrementarContadorCalifLetra( int calificacion ) 70 { 71 // determina cuál calificación se introdujo 72 switch ( calificacion / 10 ) 73 { 74 case 9: // calificación está entre 90 75 case 10: // y 100 76 ++aCuenta; // incrementa aCuenta 77 break; // necesaria para salir del switch Figura 5.9 | Clase LibroCalificaciones que utiliza una instrucción switch para contar las calificaciones A, B, C, D y F. (Parte 2 de 3). 5.6 Instrucción de selección múltiple switch 177
  • 216. 178 Capítulo 5 Instrucciones de control: parte 2 78 79 case 8: // calificación está entre 80 y 89 80 ++bCuenta; // incrementa bCuenta 81 break; // sale del switch 82 83 case 7: // calificación está entre 70 y 79 84 ++cCuenta; // incrementa cCuenta 85 break; // sale del switch 86 87 case 6: // calificación está entre 60 y 69 88 ++dCuenta; // incrementa dCuenta 89 break; // sale del switch 90 91 default: // calificación es menor que 60 92 ++fCuenta; // incrementa fCuenta 93 break; // opcional; de todas formas sale del switch 94 } // fin de switch 95 } // fin del método incrementarContadorCalifLetra 96 97 // muestra un reporte con base en las calificaciones introducidas por el usuario 98 public void mostrarReporteCalif() 99 { 100 System.out.println( "nReporte de calificaciones:" ); 101 102 // si el usuario introdujo por lo menos una calificación... 103 if ( contadorCalif != 0 ) 104 { 105 // calcula el promedio de todas las calificaciones introducidas 106 double promedio = (double) total / contadorCalif; 107 108 // imprime resumen de resultados 109 System.out.printf( "El total de las %d calificaciones introducidas es %dn", 110 contadorCalif, total ); 111 System.out.printf( "El promedio de la clase es %.2fn", promedio ); 112 System.out.printf( "%sn%s%dn%s%dn%s%dn%s%dn%s%dn", 113 "Numero de estudiantes que recibieron cada calificacion:", 114 "A: ", aCuenta, // muestra el número de calificaciones A 115 "B: ", bCuenta, // muestra el número de calificaciones B 116 "C: ", cCuenta, // muestra el número de calificaciones C 117 "D: ", dCuenta, // muestra el número de calificaciones D 118 "F: ", fCuenta ); // muestra el número de calificaciones F 119 } // fin de if 120 else // no se introdujeron calificaciones, por lo que imprime el mensaje apropiado 121 System.out.println( "No se introdujeron calificaciones" ); 122 } // fin del método mostrarReporteCalif 123 } // fin de la clase LibroCalificaciones Figura 5.9 | Clase LibroCalificaciones que utiliza una instrucción switch para contar las calificaciones A, B, C, D y F. (Parte 3 de 3). Al igual que las versiones anteriores de la clase, LibroCalificaciones (figura 5.9) declara la variable de ins- tancia nombreDelCurso (línea 7) y contiene los métodos establecerNombreDelCurso (líneas 24 a 27), obte- nerNombreDelCurso (líneas 30 a 33) y mostrarMensaje (líneas 36 a 41), que establecen el nombre del curso, lo almacenan y muestran un mensaje de bienvenida al usuario, respectivamente. La clase también contiene un constructor (líneas 18 a 21) que inicializa el nombre del curso. La clase LibroCalificaciones también declara las variables de instancia total (línea 8) y contadorCalif (línea 9), que llevan la cuenta de la suma de las calificaciones introducidas por el usuario y el número de cali-
  • 217. ficaciones introducidas, respectivamente. Las líneas 10 a 14 declaran las variables contador para cada categoría de calificaciones. La clase LibroCalificaciones mantiene a total, contadorCalif y a los cinco contadores de las letras de calificación como variables de instancia, de manera que estas variables puedan utilizarse o modificarse en cualquiera de los métodos de la clase. Observe que el constructor de la clase (líneas 18 a 21) establece sólo el nombre del curso; las siete variables de instancia restantes son de tipo int y se inicializan con 0, de manera predeterminada. La clase LibroCalificaciones (figura 5.9) contiene tres métodos adicionales: introducirCalif, incre- mentarContadorCalifLetra y mostrarReporteCalif. El método introducirCalif (líneas 44 a 66) lee un número arbitrario de calificaciones enteras del usuario mediante el uso de la repetición controlada por un centine- la, y actualiza las variables de instancia total y contadorCalif. El método introducirCalif llama al método incrementarContadorCalifLetra (líneas 69 a 95) para actualizar el contador de letra de calificación apropiado para cada calificación introducida. La clase LibroCalificaciones también contiene el método mostrarRepor- teCalif (líneas 98 a 122), el cual imprime en pantalla un reporte que contiene el total de todas las calificaciones introducidas, el promedio de las mismas y el número de estudiantes que recibieron cada letra de calificación. Examinaremos estos métodos con más detalle. La línea 48 en el método introducirCalif declara la variable calificacion que almacenará la entrada del usuario. Las líneas 50 a 54 piden al usuario que introduzca calificaciones enteras y escriba el indicador de fin de archivo para terminar la entrada. El indicador de fin de archivo es una combinación de teclas dependiente del sis- tema, que el usuario introduce para indicar que no hay más datos que introducir. En el capítulo 14, Archivos y flu- jos, veremos cómo se utiliza el indicador de fin de archivo cuando un programa lee su entrada desde un archivo. En los sistemas UNIX/Linux/Mac OS X, el fin de archivo se introduce escribiendo la secuencia <ctrl> d en una línea por sí sola. Esta notación significa que hay que oprimir al mismo tiempo la tecla ctrl y la tecla d. En los sistemas Windows, para introducir el fin de archivo se escribe <ctrl> z [Nota: en algunos sistemas, es necesario oprimir Intro después de escribir la secuencia de teclas de fin de archivo. Además, Windows generalmente muestra los caracteres ^Z en la pantalla cuando se escribe el indicador de fin de archivo, como se muestra en la salida de la figura 5.10]. Tip de portabilidad 5.1 Las combinaciones de teclas para introducir el fin de archivo son dependientes del sistema. La instrucción while (líneas 57 a 65) obtiene la entrada del usuario. La condición en la línea 57 llama al método hasNext de Scanner para determinar si hay más datos a introducir. Este método devuelve el valor boolean true si hay más datos; en caso contrario, devuelve false. Después, el valor devuelto se utiliza como el valor de la condición en la instrucción while. Mientras no se haya escrito el indicador de fin de archivo, el método hasNext devolverá true. La línea 59 recibe como entrada un valor del usuario. La línea 60 utiliza el operador += para sumar califi- cacion a total. La línea 61 incrementa contadorCalif. El método mostrarReporteCalif de la clase utiliza estas variables para calcular el promedio de las calificaciones. La línea 64 llama al método incrementarConta- dorCalifLetra de la clase (declarado en las líneas 69 a 95) para incrementar el contador de letra de calificación apropiado, con base en la calificación numérica introducida. El método incrementarContadoraCalifLetra contiene una instrucción switch (líneas 72 a 94) que determina cuál contador se debe incrementar. En este ejemplo, suponemos que el usuario introduce una califica- ción válida en el rango de 0 a 100. Una calificación en el rango de 90 a 100 representa la A: de 80 a 89, la B; de 70 a 79, la C; de 60 a 69, la D y de 0 a 59, la F. La instrucción switch consiste en un bloque que contiene una secuencia de etiquetas case y una instrucción case default opcional. Estas etiquetas se utilizan en este ejemplo para determinar cuál contador se debe incrementar, con base en la calificación. Cuando el flujo de control llega al switch, el programa evalúa la expresión entre paréntesis (calificacion / 10) que va después de la palabra clave switch. A esto se le conoce como la expresión de control de la instrucción switch. El programa compara el valor de la expresión de control (que se debe evaluar como un valor entero de 5.6 Instrucción de selección múltiple switch 179
  • 218. 180 Capítulo 5 Instrucciones de control: parte 2 tipo byte, char, short o int) con cada una de las etiquetas case. La expresión de control de la línea 72 realiza la división entera, que trunca la parte fraccionaria del resultado. Por ende, cuando dividimos cualquier valor en el rango de 0 a 100 entre 10, el resultado es siempre un valor de 0 a 10. Utilizamos varios de estos valores en nuestras etiquetas case. Por ejemplo, si el usuario introduce el entero 85, la expresión de control se evalúa como el valor int 8. La instrucción switch compara a 8 con cada etiqueta case. Si ocurre una coincidencia (case 8: en la línea 79), el programa ejecuta las instrucciones para esa instrucción case. Para el entero 8, la línea 80 incrementa a bCuenta, ya que una calificación entre 80 y 89 es una B. La instrucción break (línea 81) hace que el control del programa proceda con la primera instrucción después del switch; en este programa, llegamos al final del cuerpo del método incrementarContadorCalifLetra, por lo que el control regresa a la línea 65 en el método intro- ducirCalif (la primera línea después de la llamada a incrementarContadorCalifLetra). Esta línea marca el fin del cuerpo del ciclo while que recibe las calificaciones de entrada (líneas 57 a 65), por lo que el control fluye hacia la condición del while (línea 57) para determinar si el ciclo debe seguir ejecutándose. Las etiquetas case en nuestro switch evalúan explícitamente los valores 10, 9, 8, 7 y 6. Observe los casos en las líneas 74 y 75, que evalúan los valores 9 y 10 (los cuales representan la calificación A). Al listar las etiquetas case en forma consecutiva, sin instrucciones entre ellas, pueden ejecutar el mismo conjunto de instrucciones; cuando la expresión de control se evalúa como 9 o 10, se ejecutan las instrucciones de las líneas 76 y 77. La instrucción switch no cuenta con un mecanismo para evaluar rangos de valores, por lo que cada valor que deba evaluarse se tiene que listar en una etiqueta case separada. Observe que cada case puede tener varias instruc- ciones. La instrucción switch es distinta de otras instrucciones de control, en cuanto a que no requiere llaves alrededor de varias instrucciones en cada case. Sin instrucciones break, cada vez que ocurre una coincidencia en el switch, se ejecutan las instrucciones para ese case y los subsiguientes, hasta encontrar una instrucción break o el final del switch. A menudo a esto se le conoce como que las etiquetas case “se pasarían” hacia las instrucciones en las etiquetas case subsiguientes. (Esta característica es perfecta para escribir un programa conciso, que muestre la canción iterativa “Los Doce Días de Navidad” en el ejercicio 5.29). Error común de programación 5.7 Olvidar una instrucción break cuando se necesita una en una instrucción switch es un error lógico. Si no ocurre una coincidencia entre el valor de la expresión de control y una etiqueta case, se ejecuta el caso default (líneas 91 a 93). Utilizamos el caso default en este ejemplo para procesar todos los valores de la expresión de control que sean menores de 6; esto es, todas las calificaciones de reprobado. Si no ocurre una coin- cidencia y la instrucción switch no contiene un caso default, el control del programa simplemente continúa con la primera instrucción después de la instrucción switch. La clase PruebaLibroCalificaciones para demostrar la clase LibroCalificaciones La clase PruebaLibroCalificaciones (figura 5.10) crea un objeto LibroCalificaciones (líneas 10 y 11). La línea 13 invoca el método mostrarMensaje del objeto para imprimir en pantalla un mensaje de bienvenida para el usuario. La línea 14 invoca el método introducirCalif del objeto para leer un conjunto de calificaciones del usuario y llevar el registro de la suma de todas las calificaciones introducidas, y el número de calificaciones. Recuerde que el método introducirCalif también llama al método incrementarContadorCalifLetra para llevar el registro del número de estudiantes que recibieron cada letra de calificación. La línea 15 invoca el método mostrarReporteCalif de la clase LibroCalificaciones, el cual imprime en pantalla un reporte con base en las calificaciones introducidas (como en la ventana de entrada/salida en la figura 5.10). La línea 103 de la clase LibroCalificaciones (figura 5.9) determina si el usuario introdujo por lo menos una calificación; esto evita la división entre cero. De ser así, la línea 106 calcula el promedio de las calificaciones. A continuación, las líneas 109 a 118 imprimen en pantalla el total de todas las calificaciones, el promedio de la clase y el número de estudiantes que recibieron cada letra de calificación. Si no se introdujeron calificaciones, la línea 121 imprime en pantalla un mensaje apropiado. Los resultados en la figura 5.10 muestran un reporte de calificaciones de ejemplo, con base en 10 calificaciones. Observe que la clase PruebaLibroCalificaciones (figura 5.10) no llama directamente al método incre- mentarContadorCalifLetra de LibroCalificaciones (líneas 69 a 95 de la figura 5.9). Este método lo utiliza exclusivamente el método introducirCalif de la clase LibroCalificaciones para actualizar el contador de la
  • 219. 1 // Fig. 5.10: PruebaLibroCalificaciones.java 2 // Crea un objeto LibroCalificaciones, introduce las calificaciones y muestra un reporte. 3 4 public class PruebaLibroCalificaciones 5 { 6 public static void main( String args[] ) 7 { 8 // crea un objeto LibroCalificaciones llamado miLibroCalificaciones y 9 // pasa el nombre del curso al constructor 10 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones( 11 "CS101 Introducción a la programación en Java" ); 12 13 miLibroCalificaciones.mostrarMensaje(); // muestra un mensaje de bienvenida 14 miLibroCalificaciones.introducirCalif(); // lee calificaciones del usuario 15 miLibroCalificaciones.mostrarReporteCalif(); // muestra reporte basado en las calificaciones 16 } // fin de main 17 } // fin de la clase PruebaLibroCalificaciones Bienvenido al libro de calificaciones para CS101 Introduccion a la programacion en Java! Escriba las calificaciones enteras en el rango de 0 a 100. Escriba el indicador de fin de archivo para terminar la entrada: En UNIX/Linux/Mac OS X escriba <ctrl> d y despues oprima Intro En Windows escriba <ctrl> z y despues oprima Intro 99 92 45 57 63 71 76 85 90 100 ^Z Reporte de calificaciones: El total de las 10 calificaciones introducidas es 778 El promedio de la clase es 77.80 Numero de estudiantes que recibieron cada calificacion: A: 4 B: 1 C: 2 D: 1 F: 2 Figura 5.10 | PruebaLibroCalificaciones crea un objeto LibroCalificaciones e invoca a sus métodos. calificación de letra apropiado, a medida que el usuario introduce cada nueva calificación. El método incre- mentarContadorCalifLetra existe únicamente para dar soporte a las operaciones de los demás métodos de la clase LibroCalificaciones, por lo cual se declara como private. En el capítulo 3 vimos que los métodos que se declaran con el modificador de acceso private pueden llamarse sólo por otros métodos de la clase en la que es- tán declarados los métodos private. Dichos métodos se conocen comúnmente como métodos utilitarios o métodos ayudantes, debido a que sólo pueden llamarse mediante otros métodos de esa clase y se utilizan para dar soporte a la operación de esos métodos. 5.6 Instrucción de selección múltiple switch 181
  • 220. 182 Capítulo 5 Instrucciones de control: parte 2 Diagrama de actividad de UML de la instrucción switch La figura 5.11 muestra el diagrama de actividad de UML para la instrucción switch general. La mayoría de las instrucciones switch utilizan una instrucción break en cada case para terminar la instrucción switch después de procesar el case. La figura 5.11 enfatiza esto al incluir instrucciones break en el diagrama de actividad. Es- te diagrama hace evidente que break al final de una etiqueta case hace que el control salga de la instrucción switch de inmediato. No se requiere una instrucción break para la última etiqueta case del switch (o para el caso default opcional, cuando aparece al último), ya que la ejecución continúa con la siguiente instrucción que va después del switch. Observación de ingeniería de software 5.2 Proporcione un caso default en las instrucciones switch. Al incluir un caso default usted puede enfocarse en la necesidad de procesar las condiciones excepcionales. Buena práctica de programación 5.7 Aunque cada case y el caso default en una instrucción switch pueden ocurrir en cualquier orden, es conveniente colocar la etiqueta default. Cuando el caso default se lista al último, no se requiere el break para ese caso. Algu- nos programadores incluyen este break para mejorar la legibilidad y tener simetría con los demás casos. Cuando utilice la instrucción switch, recuerde que la expresión después de cada case debe ser una expre- sión entera constante; es decir, cualquier combinación de constantes enteras que se evalúen como un valor entero constante (por ejemplo, –7, 0 o 221). Una constante entera es tan solo un valor entero. Además, puede utili- zar constantes tipo carácter: caracteres específicos entre comillas sencillas, como ‘A’, ‘7’ o ‘$’, las cuales Figura 5.11 | Diagrama de actividad de UML de la instrucción switch de selección múltiple con instrucciones break. ... Acción(es) de default Acción(es) del case a Acción(es) del case b Acción(es) del case z break break break case b case z case a [falso] [verdadero] [verdadero] [verdadero] [falso] [falso]
  • 221. representan los valores enteros de los caracteres. (En el apéndice B, Conjunto de caracteres ASCII, se muestran los valores enteros de los caracteres en el conjunto de caracteres ASCII, que es un subconjunto del conjunto de caracteres Unicode utilizado por Java). La expresión en cada case también puede ser una variable constante: una variable que contiene un valor que no cambia durante todo el programa. Dicha variable se declara mediante la palabra clave final (que des- cribiremos en el capítulo 6, Métodos: un análisis más detallado). Java tiene una característica conocida como enumeraciones, que también presentaremos en el capítulo 6. Las constantes de enumeración también pueden utilizarse en etiquetas case. En el capítulo 10, Programación orientada a objetos: polimorfismo, presentaremos una manera más elegante de implementar la lógica del switch; utilizaremos una técnica llamada polimorfismo para crear programas que a menudo son más legibles, fáciles de mantener y de extender que los programas que utilizan lógica de switch. 5.7 Instrucciones break y continue Además de las instrucciones de selección y repetición, Java cuenta con las instrucciones break y continue (que presentamos en esta sección y en el apéndice N, Instrucciones break y continue etiquetadas) para alterar el flujo de control. En la sección anterior mostramos cómo puede utilizarse la instrucción break para terminar la ejecución de una instrucción switch. En esta sección veremos cómo utilizar break en las instrucciones de repetición. Java también cuenta con las instrucciones break y continue etiquetadas, para usarlas en los casos en los que es conveniente alterar el flujo de control en las instrucciones de control anidadas. En el apéndice N hablaremos sobre las instrucciones break y continue etiquetadas. Instrucción break Cuando break se ejecuta en una instrucción while, for, do...while, o switch, ocasiona la salida inmediata de esa instrucción. La ejecución continúa con la primera instrucción después de la instrucción de control. Los usos comunes de break son para escapar anticipadamente del ciclo, o para omitir el resto de una instrucción switch (como en la figura 5.9). La figura 5.12 demuestra el uso de una instrucción break para salir de un ciclo for. 1 // Fig. 5.12: PruebaBreak.java 2 // La instrucción break para salir de una instrucción for. 3 public class PruebaBreak 4 { 5 public static void main( String args[] ) 6 { 7 int cuenta; // la variable de control también se usa cuando termina el ciclo 8 9 for ( cuenta = 1; cuenta <= 10; cuenta++ ) // itera 10 veces 10 { 11 if ( cuenta == 5 ) // si cuenta es 5, 12 break; // termina el ciclo 13 14 System.out.printf( "%d ", cuenta ); 15 } // fin de for 16 17 System.out.printf( "nSalio del ciclo en cuenta = %dn", cuenta ); 18 } // fin de main 19 } // fin de la clase PruebaBreak 1 2 3 4 Salio del ciclo en cuenta = 5 Figura 5.12 | La instrucción break para salir de una instrucción for. 5.7 Instrucciones break y continue 183
  • 222. 184 Capítulo 5 Instrucciones de control: parte 2 Cuando la instrucción if anidada en la línea 11 dentro de la instrucción for (líneas 9 a 15) determina que cuenta es 5, se ejecuta la instrucción break en la línea 12. Esto termina la instrucción for y el programa conti- núa a la línea 17 (inmediatamente después de la instrucción for), la cual muestra un mensaje indicando el valor de la variable de control cuando terminó el ciclo. El ciclo ejecuta su cuerpo por completo sólo cuatro veces en vez de 10. Instrucción continue Cuando la instrucción continue se ejecuta en una instrucción while, for o do...while, omite las instruccio- nes restantes en el cuerpo del ciclo y continúa con la siguiente iteración del ciclo. En las instrucciones while y do...while, la aplicación evalúa la prueba de continuación de ciclo justo después de que se ejecuta la instrucción continue. En una instrucción for se ejecuta la expresión de incremento y después el programa evalúa la prueba de continuación de ciclo. La figura 5.13 utiliza la instrucción continue en un ciclo for para omitir la instrucción de la línea 12 cuan- do la instrucción if anidada (línea 9) determina que el valor de cuenta es 5. Cuando se ejecuta la instrucción continue, el control del programa continúa con el incremento de la variable de control en la instrucción for (línea 7). En la sección 5.3 declaramos que la instrucción while puede utilizarse, en la mayoría de los casos, en lugar de for. La única excepción ocurre cuando la expresión de incremento en el while va después de una instrucción continue. En este caso, el incremento no se ejecuta antes de que el programa evalúe la condición de continua- ción de repetición, por lo que el while no se ejecuta de la misma manera que el for. Observación de ingeniería de software 5.3 Algunos programadores sienten que las instrucciones break y continue violan la programación estructurada. Ya que pueden lograrse los mismos efectos con las técnicas de programación estructurada, estos programadores prefieren no utilizar instrucciones break o continue. Observación de ingeniería de software 5.4 Existe una tensión entre lograr la ingeniería de software de calidad y lograr el software con mejor desempeño. A menudo, una de estas metas se logra a expensas de la otra. Para todas las situaciones excepto las que demanden el mayor rendimiento, aplique la siguiente regla empírica: primero, asegúrese de que su código sea simple y correcto; después hágalo rápido y pequeño, pero sólo si es necesario. 1 // Fig. 5.13: PruebaContinue.java 2 // Instrucción continue para terminar una iteración de una instrucción for. 3 public class PruebaContinue 4 { 5 public static void main( String args[] ) 6 { 7 for ( int cuenta = 1; cuenta <= 10; cuenta++ ) // itera 10 veces 8 { 9 if ( cuenta == 5 ) // si cuenta es 5, 10 continue; // omite el resto del código en el ciclo 11 12 System.out.printf( "%d ", cuenta ); 13 } // fin de for 14 15 System.out.println( "nSe uso continue para omitir imprimir 5" ); 16 } // fin de main 17 } // fin de la clase PruebaContinue 1 2 3 4 6 7 8 9 10 Se uso continue para omitir imprimir 5 Figura 5.13 | Instrucción continue para terminar una iteración de una instrucción for.
  • 223. 5.8 Operadores lógicos Cada una de las instrucciones if, if...else, while, do...while y for requieren una condición para determi- nar cómo continuar con el flujo de control de un programa. Hasta ahora sólo hemos estudiado las condiciones simples, como cuenta <= 10, numero != valorCentinela y total > 1000. Las condiciones simples se expre- san en términos de los operadores relacionales >, <, >= y <=, y los operadores de igualdad == y !=; cada expresión evalúa sólo una condición. Para evaluar condiciones múltiples en el proceso de tomar una decisión, ejecutamos estas pruebas en instrucciones separadas o en instrucciones if o if...else anidadas. En ocasiones, las instruc- ciones de control requieren condiciones más complejas para determinar el flujo de control de un programa. Java cuenta con los operadores lógicos para que usted pueda formar condiciones más complejas, al combi- nar las condiciones simples. Los operadores lógicos son && (AND condicional), || (OR condicional), & (AND lógico booleano), | (OR inclusivo lógico booleano), ^ (OR exclusivo lógico booleano) y ! (NOT lógico). Operador AND (&&) condicional Suponga que deseamos asegurar en cierto punto de una aplicación que dos condiciones sean ambas verdaderas, antes de elegir cierta ruta de ejecución. En este caso, podemos utilizar el operador && (AND condicional) de la siguiente manera: if ( genero == FEMENINO && edad >= 65 ) ++mujeresMayores; Esta instrucción if contiene dos condiciones simples. La condición genero == FEMENINO compara la variable genero con la constante FEMENINO. Por ejemplo, esto podría evaluarse para determinar si una persona es mujer. La condición edad >= 65 podría evaluarse para determinar si una persona es un ciudadano mayor. La instrucción if considera la condición combinada genero == FEMENINO && edad >= 65 la cual es verdadera si, y sólo si ambas condiciones simples son verdaderas. Si la condición combinada es verdade- ra, el cuerpo de la instrucción if incrementa a mujeresMayores en 1. Si una o ambas condiciones simples son falsas, el programa omite el incremento. Algunos programadores consideran que la condición combinada anterior es más legible si se agregan paréntesis redundantes, como por ejemplo: ( genero == FEMENINO ) && ( edad >= 65 ) La tabla de la figura 5.14 sintetiza el uso del operador &&. Esta tabla muestra las cuatro combinaciones posibles de valores false y true para expresión1 y expresión2. A dichas tablas se les conoce como tablas de verdad. Java evalúa todas las expresiones que incluyen operadores relacionales, de igualdad o lógicos como true o false. expresión1 expresión2 expresión1 && expresión2 false false false false true false true false false true true true Figura 5.14 | Tabla de verdad del operador && (AND condicional). Operador OR condicional (||) Ahora suponga que deseamos asegurar que cualquiera o ambas condiciones sean verdaderas antes de elegir cierta ruta de ejecución. En este caso, utilizamos el operador || (OR condicional), como se muestra en el siguiente segmento de un programa: if ( ( promedioSemestre >= 90 ) || ( examenFinal >= 90 ) ) System.out.println ( “La calificacion del estudiante es A” ); 5.8 Operadores lógicos 185
  • 224. 186 Capítulo 5 Instrucciones de control: parte 2 Esta instrucción también contiene dos condiciones simples. La condición promedioSemestre >= 90 se evalúa para determinar si el estudiante merece una A en el curso, debido a que tuvo un sólido rendimiento a lo largo del semestre. La condición examenFinal >= 90 se evalúa para determinar si el estudiante merece una A en el curso debido a un desempeño sobresaliente en el examen final. Después, la instrucción if considera la condición combinada ( promedioSemestre >= 90 ) || ( examenFinal >= 90 ) y otorga una A al estudiante si una o ambas de las condiciones simples son verdaderas. La única vez que no se imprime el mensaje “La calificación del estudiante es A” es cuando ambas condiciones simples son falsas. La figura 5.15 es una tabla de verdad para el operador OR condicional (||). El operador && tiene mayor precedencia que el operador ||. Ambos operadores se asocian de izquierda a derecha. expresión1 expresión2 expresión1 || expresión2 false false false false true true true false true true true true Figura 5.15 | Tabla de verdad del operador (OR condicional) ||. Evaluación en corto circuito de condiciones complejas Las partes de una expresión que contienen los operadores && o || se evalúan sólo hasta que se sabe si la condición es verdadera o falsa. Por ende, la evaluación de la expresión ( genero == FEMENINO ) && ( edad >= 65 ) se detiene de inmediato si genero no es igual a FEMENINO (es decir, en este punto es evidente que toda la expre- sión es false) y continúa si genero es igual a FEMENINO (es decir, toda la expresión podría ser aún true si la condición edad >= 65 es true). Esta característica de las expresiones AND y OR condicional se conoce como evaluación en corto circuito. Error común de programación 5.8 En las expresiones que utilizan el operador &&, una condición (a la cual le llamamos condición dependiente) puede requerir que otra condición sea verdadera para que la evaluación de la condición dependiente tenga significado. En este caso, la condición dependiente debe colocarse después de la otra condición, o podría ocurrir un error. Por ejemplo, en la expresión ( i != 0) && (10 / i == 2), la segunda condición debe aparecer después de la primera, o podría ocurrir un error de división entre cero. Operadores AND lógico booleano (&) y OR inclusivo lógico booleano (|) Los operadores AND lógico booleano (&) y OR inclusivo lógico booleano (|) funcionan en forma idéntica a los operadores && (AND condicional) y || (OR condicional), con una excepción: los operadores lógicos boolea- nos siempre evalúan ambos operandos (es decir, no realizan una evaluación en corto circuito). Por lo tanto, la expresión ( genero == 1 ) & ( edad >= 65 ) evalúa edad >= 65, sin importar que genero sea igual o no a 1. Esto es útil si el operando derecho del operador AND lógico booleano o del OR inclusivo lógico booleano tiene un efecto secundario requerido: la modificación del valor de una variable. Por ejemplo, la expresión ( cumpleanios == true ) | ( ++edad >= 65 ) garantiza que se evalúe la condición ++edad >= 65. Por ende, la variable edad se incrementa en la expresión anterior, sin importar que la expresión total sea true o false.
  • 225. Tip para prevenir errores 5.4 Por cuestión de claridad, evite las expresiones con efectos secundarios en las condiciones. Los efectos secundarios pue- den tener una apariencia inteligente, pero pueden hacer que el código sea más difícil de entender y pueden llegar a producir errores lógicos sutiles. OR exclusivo lógico booleano (^) Una condición compleja que contiene el operador OR exclusivo lógico booleano (^) es true si y sólo si uno de sus operandos es true y el otro es false. Si ambos operandos son true o si ambos son false, toda la condición es false. La figura 5.16 es una tabla de verdad para el operador OR exclusivo lógico booleano (^). También se garantiza que este operador evaluará ambos operandos. expresión1 expresión2 expresión1 ^ expresión2 false false false false true true true false true true true false Figura 5.16 | Tabla de verdad del operador ^ (OR exclusivo lógico booleano). expresión !expresión false true true false Figura 5.17 | Tabla de verdad del operador ! (negación lógica, o NOT lógico). Operador lógico de negación (!) El operador ! (NOT lógico, también conocido como negación lógica o complemento lógico) “invierte” el sig- nificado de una condición. A diferencia de los operadores lógicos &&, ||, &, | y ^, que son operadores binarios que combinan dos condiciones, el operador lógico de negación es un operador unario que sólo tiene una con- dición como operando. Este operador se coloca antes de una condición para elegir una ruta de ejecución si la condición original (sin el operador lógico de negación) es false, como en el siguiente segmento de código: if ( ! ( calificacion == valorCentinela ) ) System.out.printf( “La siguiente calificación es %dn”, calificacion ); que ejecuta la llamada a printf sólo si calificacion no es igual a valorCentinela. Los paréntesis alrededor de la condición calificacion == valorCentinela son necesarios, ya que el operador lógico de negación tiene mayor precedencia que el de igualdad. En la mayoría de los casos, puede evitar el uso de la negación lógica si expresa la condición en forma distinta, con un operador relacional o de igualdad apropiado. Por ejemplo, la instrucción anterior también puede escribirse de la siguiente manera: if ( calificacion != valorCentinela ) System.out.printf( “La siguiente calificación es %dn”, calificacion ); Esta flexibilidad le puede ayudar a expresar una condición de una manera más conveniente. La figura 5.17 es una tabla de verdad para el operador lógico de negación. 5.8 Operadores lógicos 187
  • 226. 188 Capítulo 5 Instrucciones de control: parte 2 Ejemplo de los operadores lógicos La figura 5.18 demuestra el uso de los operadores lógicos y lógicos booleanos; para ello produce sus tablas de verdad. Los resultados muestran la expresión que se evalúo y el resultado boolean de esa expresión. Los valores de las expresiones boolean se muestran mediante printf, usando el especificador de formato %b, que imprime la palabra “true” o “false”, con base en el valor de la expresión. Las líneas 9 a 13 producen la tabla de verdad para el &&. Las líneas 16 a 20 producen la tabla de verdad para el ||. Las líneas 23 a 27 producen la tabla de verdad para el &. Las líneas 30 a 35 producen la tabla de verdad para el |. Las líneas 38 a 43 producen la tabla de verdad para el ^. Las líneas 46 a 47 producen la tabla de verdad para el !. 1 // Fig. 5.18: OperadoresLogicos.java 2 // Los operadores lógicos. 3 4 public class OperadoresLogicos 5 { 6 public static void main( String args[] ) 7 { 8 // crea tabla de verdad para el operador && (AND condicional) 9 System.out.printf( "sn%s: %bn%s: %bn%s: %bn%s: %bnn", 10 "AND condicional (&&)", "false && false"( false && false ), 11 "false && true", ( false && true ), 12 "true && false", ( true && false ), 13 "true && true", ( true && true ) ); 14 15 // crea tabla de verdad para el operador || (OR condicional) 16 System.out.printf( "%sn%s: %bn%s: %bn%s: %bn%s: %bnn", 17 "OR condicional (||)", "false || false", ( false || false ), 18 "false || true", ( false || true ), 19 "true || false", ( true || false ), 20 "true || true", ( true || true ) ); 21 22 // crea tabla de verdad para el operador & (AND lógico booleano) 23 System.out.printf( "%sn%s: %bn%s: %bn%s: %bn%s: %bnn", 24 "AND logico booleano (&)", "false & false", ( false & false ), 25 "false & true", ( false & true ), 26 "true & false", ( true & false ), 27 "true & true", ( true & true ) ); 28 29 // crea tabla de verdad para el operador | (OR inclusivo lógico booleano) 30 System.out.printf( "%sn%s: %bn%s: %bn%s: %bn%s: %bnn", 31 "OR inclusivo logico booleano (|)", 32 "false | false", ( false | false ), 33 "false | true", ( false | true ), 34 "true | false", ( true | false ), 35 "true | true", ( true | true ) ); 36 37 // crea tabla de verdad para el operador ^ (OR exclusivo lógico booleano) 38 System.out.printf( "%sn%s: %bn%s: %bn%s: %bn%s: %bnn", 39 "OR exclusivo logico booleano (^)", 40 "false ^ false", ( false ^ false ), 41 "false ^ true", ( false ^ true ), 42 "true ^ false", ( true ^ false ), 43 "true ^ true", ( true ^ true ) ); 44 45 // crea tabla de verdad para el operador ! (negación lógica) 46 System.out.printf( "%sn%s: %bn%s: %bn", "NOT logico (!)", 47 "!false"( !false ), "!true", ( !true ) ); Figura 5.18 | Los operadores lógicos. (Parte 1 de 2).
  • 227. La figura 5.19 muestra la precedencia y la asociatividad de los operadores de Java presentados hasta ahora. Los operadores se muestran de arriba hacia abajo, en orden descendente de precedencia. 48 } // fin de main 49 } // fin de la clase OperadoresLogicos AND condicional (&&) false && false: false false && true: false true && false: false true && true: true OR condicional (||) false || false: false false || true: true true || false: true true || true: true AND logico booleano (&) false & false: false false & true: false true & false: false true & true: true OR inclusivo logico booleano (|) false | false: false false | true: true true | false: true true | true: true OR exclusivo logico booleano (^) false ^ false: false false ^ true: true true ^ false: true true ^ true: false NOT logico (!) !false: true !true: false Figura 5.18 | Los operadores lógicos. (Parte 2 de 2). Operadores Asociatividad Tipo ++ -- derecha a izquierda postfijo unario ++ - + - ! (tipo) derecha a izquierda prefijo unario * / % izquierda a derecha multiplicativo + - izquierda a derecha aditivo < <= > >= izquierda a derecha relacional == != izquierda a derecha igualdad & izquierda a derecha AND lógico booleano Figura 5.19 | Precedencia/asociatividad de los operadores descritos hasta ahora. (Parte 1 de 2). 5.8 Operadores lógicos 189
  • 228. 190 Capítulo 5 Instrucciones de control: parte 2 5.9 Resumen sobre programación estructurada Así como los arquitectos diseñan edificios empleando la sabiduría colectiva de su profesión, de igual forma, los programadores diseñan programas. Nuestro campo es mucho más joven que la arquitectura, y nuestra sabiduría colectiva es mucho más escasa. Hemos aprendido que la programación estructurada produce programas que son más fáciles de entender, probar, depurar, modificar que los programas no estructurados, e incluso probar que son correctos en sentido matemático. La figura 5.20 utiliza diagramas de actividad de UML para sintetizar las instrucciones de control de Java. Los estados inicial y final indican el único punto de entrada y el único punto de salida de cada instrucción de control. Si conectamos los símbolos individuales de un diagrama de actividad en forma arbitrara, existe la posibi- lidad de que se produzcan programas no estructurados. Por lo tanto, la profesión de la programación ha elegido un conjunto limitado de instrucciones de control que pueden combinarse sólo de dos formas simples, para crear programas estructurados. Por cuestión de simpleza, sólo se utilizan instrucciones de control de una sola entrada/una sola salida; sólo hay una forma de entrar y una forma de salir de cada instrucción de control. Es sencillo conectar instrucciones de control en secuencia para formar programas estructurados. El estado final de una instrucción de control se conecta al estado inicial de la siguiente instrucción de control; es decir, las instrucciones de control se colocan una después de la otra en un programa en secuencia. A esto le llamamos “apilamiento de instrucciones de control”. Las reglas para formar programas estructurados también permiten anidar las instrucciones de control. La figura 5.21 muestra las reglas para formar programas estructurados. Las reglas suponen que pueden utilizarse estados de acción para indicar cualquier acción. Además, las reglas suponen que comenzamos con el diagrama de actividad más sencillo (figura 5.22), que consiste solamente de un estado inicial, un estado de acción, un estado final y flechas de transición. Al aplicar las reglas de la figura 5.21, siempre se obtiene un diagrama de actividad estructurado apropiada- mente, con una agradable apariencia de bloque de construcción. Por ejemplo, si se aplica la regla 2 repetidamente al diagrama de actividad más sencillo, se obtiene un diagrama de actividad que contiene muchos estados de acción en secuencia (figura 5.23). La regla 2 genera una pila de estructuras de control, por lo que llamaremos a la regla 2 regla de apilamiento. [Nota: las líneas punteadas verticales en la figura 5.23 no son parte de UML. Las utilizamos para separar los cuatro diagramas de actividad que demuestran cómo se aplica la regla 2 de la figura 5.21]. La regla 3 se conoce como regla de anidamiento. Al aplicar la regla 3 repetidamente al diagrama de acti- vidad más sencillo, se obtiene un diagrama de actividad con instrucciones de control perfectamente anidadas. Por ejemplo, en la figura 5.24 el estado de acción en el diagrama de actividad más sencillo se reemplaza con una instrucción de selección doble (if...else). Luego la regla 3 se aplica otra vez a los estados de acción en la ins- trucción de selección doble, reemplazando cada uno de estos estados con una instrucción de selección doble. El símbolo punteado de estado de acción alrededor de cada una de las instrucciones de selección doble, representa el estado de acción que se reemplazó. [Nota: las flechas punteadas y los símbolos punteados de estado de acción que se muestran en la figura 5.24 no son parte de UML. Aquí se utilizan para ilustrar que cualquier estado de acción puede reemplazarse con un enunciado de control]. La regla 4 genera instrucciones más grandes, más implicadas y más profundamente anidadas. Los diagramas que surgen debido a la aplicación de las reglas de la figura 5.21 constituyen el conjunto de todos los posibles Operadores Asociatividad Tipo ^ izquierda a derecha OR exclusivo lógico booleano | izquierda a derecha OR inclusivo lógico booleano && izquierda a derecha AND condicional || izquierda a derecha OR condicional ?: derecha a izquierda condicional = += -= *= /= %= derecha a izquierda asignación Figura 5.19 | Precedencia/asociatividad de los operadores descritos hasta ahora. (Parte 2 de 2).
  • 229. diagramas de actividad estructurados y, por lo tanto, el conjunto de todos los posibles programas estructurados. La belleza de la metodología estructurada es que utilizamos sólo siete instrucciones de control simples de una sola entrada/una sola salida, y las ensamblamos en una de sólo dos formas simples. Si se siguen las reglas de la figura 5.21, no podrá crearse un diagrama de actividad “sin estructura” (como el de la figura 5.25). Si usted no está seguro de que cierto diagrama sea estructurado, aplique las reglas de la 5.9 Resumen sobre programación estructurada 191 break [v] [f] instrucción if…else (selección doble) instrucción if (selección simple) [v] [f] [v] [f] break [v] break [v] [f] [f] instrucción switch con instrucciones breaks (selección múltiple) Secuencia Selección Repetición procesamiento default inicialización incremento ... ... [v] [f] instrucción for [v] [f] instrucción while [v] [f] instrucción do…while Figura 5.20 | Instrucciones de secuencia, selección y repetición de una sola entrada/una sola salida de Java.
  • 230. 192 Capítulo 5 Instrucciones de control: parte 2 figura 5.21 en orden inverso para reducir el diagrama al diagrama de actividad más sencillo. Si puede reducirlo, entonces el diagrama original es estructurado; de lo contrario, no es estructurado. Reglas para formar programas estructurados 1) Comenzar con el diagrama de actividad más sencillo (figura 5.22). 2) Cualquier estado de acción puede reemplazarse por dos estados de acción en secuencia. 3) Cualquier estado de acción puede reemplazarse por cualquier instrucción de control (secuencia de estados de acción, if, if...else, switch, while, do...while o for). 4) Las reglas 2 y 3 pueden aplicarse tantas veces como se desee y en cualquier orden. Figura 5.21 | Reglas para formar programas estructurados. estado de acción estado de acción estado de acción aplicar regla 2 aplicar regla 2 aplicar regla 2 estado de acción estado de acción estado de acción estado de acción estado de acción estado de acción ... Figura 5.22 | El diagrama de actividad más sencillo. Figura 5.23 | El resultado de aplicar la regla de apilamiento (regla 2) de la figura 5.21 repetidamente al diagrama de actividad más sencillo.
  • 231. estado de acción estado de acción [v] [f] [v] [f] [v] [f] [v] [f] estado de acción estado de acción estado de acción estado de acción estado de acción aplicar regla 3 aplicar regla 3 aplicar regla 3 Figura 5.24 | Aplicación de la regla de anidamiento (regla 3) de la figura 5.21 al diagrama de actividad más sencillo. Figura 5.25 | Diagrama de actividad “sin estructura”. estado de acción estado de acción estado de acción estado de acción 5.9 Resumen sobre programación estructurada 193
  • 232. 194 Capítulo 5 Instrucciones de control: parte 2 La programación estructurada promueve la simpleza. Bohm y Jacopini nos han dado el resultado de que sólo se necesitan tres formas de control para implementar un algoritmo: Secuencia. Selección. Repetición. La estructura de secuencia es trivial. Simplemente enliste las instrucciones a ejecutar en el orden en el que deben ejecutarse. La selección se implementa en una de tres formas: Instrucción if (selección simple). Instrucción if...else (selección doble). Instrucción switch (selección múltiple). De hecho, es sencillo demostrar que la instrucción if simple es suficiente para ofrecer cualquier forma de selec- ción; todo lo que pueda hacerse con las instrucciones if...else y switch puede implementarse si se combinan instrucciones if (aunque tal vez no con tanta claridad y eficiencia). La repetición se implementa en una de tres maneras: Instrucción while. Instrucción do...while. Instrucción for. Es sencillo demostrar que la instrucción while es suficiente para proporcionar cualquier forma de repetición. Todo lo que puede hacerse con las instrucciones do...while y for, puede hacerse también con la instrucción while (aunque tal vez no sea tan sencillo). Si se combinan estos resultados, se demuestra que cualquier forma de control necesaria en un programa en Java puede expresarse en términos de: Secuencia. Instrucción if (selección). Instrucción while (repetición). y que estos tres elementos pueden combinarse en sólo dos formas: apilamiento y anidamiento. Evidentemente, la programación estructurada es la esencia de la simpleza. 5.10 (Opcional) Ejemplo práctico de GUI y gráficos: dibujo de rectángulos y óvalos Esta sección demuestra cómo dibujar rectángulos y óvalos, usando los métodos drawRect y drawOval de Gra- phics, respectivamente. Estos métodos se demuestran en la figura 5.26. La línea 6 empieza la declaración de la clase para Figuras, que extiende a JPanel. La variable de instancia opcion, declarada en la línea 8, determina si paintComponent debe dibujar rectángulos u óvalos. El constructor de Figuras en las líneas 11 a 14 inicializa opcion con el valor que se pasa en el parámetro opcionUsuario. El método paintComponent (líneas 17 a 36) realiza el dibujo actual. Recuerde que la primera instrucción en todo método paintComponent debe ser una llamada a super.paintComponent, como en la línea 19. Las líneas 21 a 35 iteran 10 veces para dibujar 10 figuras. La instrucción switch (líneas 24 a 34) elije entre dibujar rectángulos y dibujar óvalos. Si opcion es 1, entonces el programa dibuja un rectángulo. Las líneas 27 y 28 llaman al método drawRect de Graphics. El método drawRect requiere cuatro argumentos. Los primeros dos representan las coordenadas x y y de la esquina superior izquierda del rectángulo; los siguientes dos representan la anchura y la altura del rectángulo. En este ejemplo, empezamos en la posición 10 píxeles hacia abajo y 10 píxeles a la derecha de la esquina superior izquierda, y cada iteración del ciclo avanza la esquina superior izquierda otros 10 píxeles hacia abajo y a la derecha. La anchura y la altura del rectángulo empiezan en 50 píxeles, y se incrementan por 10 píxeles en cada iteración. Si opcion es 2, el programa dibuja un óvalo. Al dibujar un óvalo se crea un rectángulo imaginario llamado rectángulo delimitador, y dentro de éste se crea un óvalo que toca los puntos medios de todos los cuatro lados • • • • • • • • • • • •
  • 233. del rectángulo delimitador. El método drawOval (líneas 31 y 32) requiere los mismos cuatro argumentos que el método drawRect. Los argumentos especifican la posición y el tamaño del rectángulo delimitador para el óvalo. Los valores que se pasan a drawOval en este ejemplo son exactamente los mismos valores que se pasan a drawRect en las líneas 27 y 28. Como la anchura y la altura del rectángulo delimitador son idénticas en este ejemplo, las líneas 27 y 28 dibujan un círculo. Puede modificar el programa para dibujar rectángulos y óvalos, para ver cómo se relacionan drawOval y drawRect. La figura 5.27 es responsable de manejar la entrada del usuario y crear una ventana para mostrar el dibujo apropiado, con base en la respuesta del usuario. La línea 3 importa a JFrame para manejar la pantalla, y la línea 4 importa a JOptionPane para manejar la entrada. Las líneas 11 a 13 muestran un cuadro de diálogo al usuario y almacenan la respuesta de éste en la variable entrada. La línea 15 utiliza el método parseInt de Integer para convertir el objeto String introducido por el usuario en un int, y almacena el resultado en la variable opcion. En la línea 18 se crea una instancia de la clase Figuras, y se pasa la opción del usuario al constructor. Las líneas 20 a 25 realizan las operaciones estándar para crear y establecer una ventana: crear un marco, configurarlo para que la aplicación termine cuando se cierre, agregar el dibujo al marco, establecer su tamaño y hacerlo visible. Figura 5.26 | Cómo dibujar una cascada de figuras, con base en la opción elegida por el usuario. 1 // Fig. 5.26: Figuras.java 2 // Demuestra cómo dibujar distintas figuras. 3 import java.awt.Graphics; 4 import javax.swing.JPanel; 5 6 public class Figuras extends JPanel 7 { 8 private int opcion; // opción del usuario acerca de cuál figura dibujar 9 10 // el constructor establece la opción del usuario 11 public Figuras( int opcionUsuario ) 12 { 13 opcion = opcionUsuario; 14 } // fin del constructor de Figuras 15 16 // dibuja una cascada de figuras, empezando desde la esquina superior izquierda 17 public void paintComponent( Graphics g ) 18 { 19 super.paintComponent( g ); 20 21 for ( int i = 0; i < 10; i++ ) 22 { 23 // elije la figura con base en la opción del usuario 24 switch ( opcion ) 25 { 26 case 1: // dibuja rectángulos 27 g.drawRect( 10 + i * 10, 10 + i * 10, 28 50 + i * 10, 50 + i * 10 ); 29 break; 30 case 2: // dibuja óvalos 31 g.drawOval( 10 + i * 10, 10 + i * 10, 32 50 + i * 10, 50 + i * 10 ); 33 break; 34 } // fin del switch 35 } // fin del for 36 } // fin del método paintComponent 37 } // fin de la clase Figuras 5.10 (Opcional) Ejemplo práctico de GUI y gráficos: dibujo de rectángulos y óvalos 195
  • 234. 196 Capítulo 5 Instrucciones de control: parte 2 Figura 5.27 | Cómo obtener datos de entrada del usuario y crear un objeto JFrame para mostrar figuras. 1 // Fig. 5.27: PruebaFiguras.java 2 // Aplicación de prueba que muestra la clase Figuras. 3 import javax.swing.JFrame; 4 import javax.swing.JOptionPane; 5 6 public class PruebaFiguras 7 { 8 public static void main( String args[] ) 9 { 10 // obtiene la opción del usuario 11 String entrada = JOptionPane.showInputDialog( 12 "Escriba 1 para dibujar rectangulosn" + 13 "Escriba 2 para dibujar ovalos" ); 14 15 int opcion = Integer.parseInt( entrada ); // convierte entrada en int 16 17 // crea el panel con la entrada del usuario 18 Figuras panel = new Figuras( opcion ); 19 20 JFrame aplicacion = new JFrame(); // crea un nuevo objeto JFrame 21 22 aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); 23 aplicacion.add( panel ); // agrega el panel al marco 24 aplicacion.setSize( 300, 300 ); // establece el tamaño deseado 25 aplicacion.setVisible( true ); // muestra el marco 26 } // fin de main 27 } // fin de la clase PruebaFiguras
  • 235. Ejercicios del ejemplo práctico de GUI y gráficos 5.1 Dibuje 12 círculos concéntricos en el centro de un objeto JPanel (figura 5.28). El círculo más interno debe tener un radio de 10 píxeles, y cada círculo sucesivo debe tener un radio 10 píxeles mayor que el anterior. Empiece por buscar el centro del objeto JPanel. Para obtener la esquina superior izquierda de un círculo, avance un radio hacia arriba y un radio a la izquierda, partiendo del centro. La anchura y la altura del rectángulo delimitador es el diámetro del círculo (el doble del radio). 5.2 Modifique el ejercicio 5.16 de los ejercicios de fin de capítulo para leer la entrada usando cuadros de diálogo, y mos- trar el gráfico de barras usando rectángulos de longitudes variables. 5.11 (Opcional) Ejemplo práctico de Ingeniería de Software: cómo identificar los estados y actividades de los objetos En la sección 4.15 identificamos muchos de los atributos de las clases necesarios para implementar el sistema ATM, y los agregamos al diagrama de clases de la figura 4.24. En esta sección le mostraremos la forma en que estos atributos representan el estado de un objeto. Identificaremos algunos estados clave que pueden ocupar nues- tros objetos y hablaremos acerca de cómo cambian los objetos de estado, en respuesta a los diversos eventos que ocurren en el sistema. También hablaremos sobre el flujo de trabajo, o actividades, que realizan los objetos en el sistema ATM. En esta sección presentaremos las actividades de los objetos de transacción SolicitudSaldo y Retiro. Diagramas de máquina de estado Cada objeto en un sistema pasa a través de una serie de estados. El estado actual de un objeto se indica mediante los valores de los atributos del objeto en cualquier momento dado. Los diagramas de máquina de estado (que se conocen comúnmente como diagramas de estado) modelan varios estados de un objeto y muestran bajo qué circunstancias el objeto cambia de estado. A diferencia de los diagramas de clases que presentamos en las secciones anteriores del ejemplo práctico, que se enfocaban principalmente en la estructura del sistema, los diagra- mas de estado modelan parte del comportamiento del sistema. La figura 5.29 es un diagrama de estado simple que modela algunos de los estados de un objeto de la clase ATM. UML representa a cada estado en un diagrama de estado como un rectángulo redondeado con el nom- bre del estado dentro de éste. Un círculo relleno con una punta de flecha designa el estado inicial. Recuerde que en el diagrama de clases de la figura 4.24 modelamos esta información de estado como el atributo Boolean de nombre usuarioAutenticado. Este atributo se inicializa en false, o en el estado “Usuario no autentica- do”, de acuerdo con el diagrama de estado. Las flechas indican las transiciones entre los estados. Un objeto puede pasar de un estado a otro, en respuesta a los diversos eventos que ocurren en el sistema. El nombre o la descripción del evento que ocasiona una transi- ción se escribe cerca de la línea que corresponde a esa transición. Por ejemplo, el objeto ATM cambia del estado “Usuario no autenticado” al estado “Usuario autenticado”, una vez que la base de datos autentica al usuario. En el documento de requerimientos vimos que para autenticar a un usuario, la base de datos compara el número de Figura 5.28 | Cómo dibujar círculos concéntricos. 5.11 (Opcional) Ejemplo práctico de Ingeniería de Software: como identificar los estados y ... 197
  • 236. 198 Capítulo 5 Instrucciones de control: parte 2 cuenta y el NIP introducidos por el usuario con los de la cuenta correspondiente en la base de datos. Si la base de datos indica que el usuario ha introducido un número de cuenta válido y el NIP correcto, el objeto ATM pasa al estado “Usuario autenticado” y cambia su atributo usuarioAutenticado al valor true. Cuando el usuario sale del sistema al seleccionar la opción “salir” del menú principal, el objeto ATM regresa al estado “Usuario no autenticado”. Figura 5.30 | Diagrama de actividad para un objeto SolicitudSaldo. obtener saldo de cuenta de la base de datos mostrar saldo en la pantalla Usuario no autenticado Usuario autenticado la base de datos del banco autentica al usuario el usuario sale del sistema Figura 5.29 | Diagrama de estado para el objeto ATM. Observación de ingeniería de software 5.5 Por lo general, los diseñadores de software no crean diagramas de estado que muestren todos los posibles estados y transiciones de estados para todos los atributos; simplemente hay demasiados. Lo común es que los diagramas de estado muestren sólo los estados y las transiciones de estado importantes. Diagramas de actividad Al igual que un diagrama de estado, un diagrama de actividad modela los aspectos del comportamiento de un sistema. A diferencia de un diagrama de estado, un diagrama de actividad modela el flujo de trabajo (secuencia de objetos) de un objeto durante la ejecución de un programa. Un diagrama de actividad modela las accio- nes a realizar y en qué orden las realizará el objeto. El diagrama de actividad de la figura 5.30 modela las acciones involucradas en la ejecución de una transacción de solicitud de saldo. Asumimos que ya se ha inicializado un objeto SolicitudSaldo y que ya se le ha asignado un número de cuenta válido (el del usuario actual), por lo que el objeto sabe qué saldo extraer de la base de datos. El diagrama incluye las acciones que ocurren después de que el usuario selecciona la opción de solicitud de saldo del menú principal y antes de que el ATM devuelva al usuario al menú principal; un objeto SolicitudSaldo no realiza ni inicia estas acciones, por lo que no las mode- lamos aquí. El diagrama empieza extrayendo de la base de datos el saldo de la cuenta. Después, SolicitudSaldo muestra el saldo en la pantalla. Esta acción completa la ejecución de la transacción. Recuerde que hemos optado por representar el saldo de una cuenta como los atributos saldoDisponible y saldoTotal de la clase Cuenta, por lo que las acciones que se modelan en la figura 5.30 hacen referencia a la obtención y visualización de ambos atributos del saldo. UML representa una acción en un diagrama de actividad como un estado de acción, el cual se modela mediante un rectángulo en el que sus lados izquierdo y derecho se sustituyen por arcos hacia fuera. Cada estado de acción contiene una expresión de acción; por ejemplo, “obtener de la base de datos el saldo de la cuenta”; eso especifica una acción a realizar. Una flecha conecta dos estados de acción, con lo cual indica el orden en el que
  • 237. 5.11 (Opcional) Ejemplo práctico de Ingeniería de Software: como identificar los estados y... 199 ocurren las acciones representadas por los estados de acción. El círculo relleno (en la parte superior de la figura 5.30) representa el estado inicial de la actividad: el inicio del flujo de trabajo antes de que el objeto realice las acciones modeladas. En este caso, la transacción primero ejecuta la expresión de acción “obtener de la base de datos el saldo de la cuenta”. Después, la transacción muestra ambos saldos en la pantalla. El círculo relleno ence- rrado en un círculo sin relleno (en la parte inferior de la figura 5.30) representa el estado final: el fin del flujo de trabajo una vez que el objeto realiza las acciones modeladas. Utilizamos diagramas de actividad de UML para ilustrar el flujo de control para las instrucciones de control que presentamos en los capítulos 4 y 5. La figura 5.31 muestra un diagrama de actividad para una transacción de retiro. Asumimos que ya se ha asignado un número de cuenta válido a un objeto Retiro. No modelaremos al usuario seleccionando la opción [el usuario seleccionó una cantidad] [el usuario canceló la transacción] [monto > saldo disponible] [monto <= saldo disponible] [hay suficiente efectivo disponible] [no hay suficiente efectivo disponible] muestra el menú de montos de retiro y la opción para cancelar introduce la selección del menú interactuar con la base de datos para cargar el monto a la cuenta del usuario dispensar efectivo instruir al usuario para que tome el efectivo establecer atributo del monto mostrar mensaje de error apropiado evaluar si hay suficiente efectivo en el dispensador de efectivo obtener de la base de datos el saldo disponible de la cuenta del usuario Figura 5.31 | Diagrama de actividad para una transacción de retiro.
  • 238. 200 Capítulo 5 Instrucciones de control: parte 2 de retiro del menú principal ni al ATM devolviendo al usuario al menú principal, ya que estas acciones no las realiza un objeto Retiro. La transacción primero muestra un menú de montos estándar de retiro (que se muestra en la figura 2.19) y una opción para cancelar la transacción. Después la transacción recibe una selección del menú de parte del usuario. Ahora el flujo de actividad llega a una decisión (una bifurcación indicada por el pequeño símbolo de rombo). [Nota: en versiones anteriores de UML, una decisión se conocía como una bifurcación]. Este punto determina la siguiente acción con base en la condición de guardia asociada (entre corchetes, enseguida de la transición), que indica que la transición ocurre si se cumple esta condición de guardia. Si el usuario cancela la transacción al elegir la opción “cancelar” del menú, el flujo de actividad salta inmediatamente al siguiente estado. Observe la fusión (indicada mediante el pequeño símbolo de rombo), en donde el flujo de actividad de cance- lación se combina con el flujo de actividad principal, antes de llegar al estado final de la actividad. Si el usuario selecciona un monto de retiro del menú, Retiro establece monto (un atributo modelado originalmente en la figura 4.24) al valor elegido por el usuario. Después de establecer el monto de retiro, la transacción obtiene el saldo disponible de la cuenta del usuario (es decir, el atributo saldoDisponible del objeto Cuenta del usuario) de la base de datos. Después el flujo de actividad llega a otra decisión. Si el monto de retiro solicitado excede al saldo disponible del usuario, el sistema muestra un mensaje de error apropiado, en el cual informa al usuario sobre el problema y después regresa al prin- cipio del diagrama de actividad, y pide al usuario que introduzca un nuevo monto. Si el monto de retiro solicitado es menor o igual al saldo disponible del usuario, la transacción continúa. A continuación, la transacción evalúa si el dispensador de efectivo tiene suficiente efectivo para satisfacer la solicitud de retiro. Si éste no es el caso, la transacción muestra un mensaje de error apropiado, después regresa al principio del diagrama de actividad y pide al usuario que seleccione un nuevo monto. Si hay suficiente efectivo disponible, la transacción interactúa con la base de datos para cargar el monto retirado de la cuenta del usuario (es decir, restar el monto tanto del atributo saldoDisponible como del atributo saldoTotal del objeto Cuenta del usuario). Después la transacción entre- ga el monto deseado de efectivo e instruye al usuario para que lo tome. Por último, el flujo de actividad se fusiona con el flujo de actividad de cancelación antes de llegar al estado final. Hemos llevado a cabo los primeros pasos para modelar el comportamiento del sistema ATM y hemos mos- trado cómo participan los atributos de un objeto para realizar las actividades del mismo. En la sección 6.14 inves- tigaremos los comportamientos para todas las clases, de manera que obtengamos una interpretación más precisa del comportamiento del sistema, al “completar” los terceros compartimientos de las clases en nuestro diagrama de clases. Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 5.1 Indique si el siguiente enunciado es verdadero o falso y, si es falso, explique por qué: los diagramas de estado modelan los aspectos estructurales de un sistema. 5.2 Un diagrama de actividad modela las (los) _________ que realiza un objeto y el orden en el que las(los) realiza. a) acciones b) atributos c) estados d) transiciones de estado 5.3 Con base en el documento de requerimientos, cree un diagrama de actividad para una transacción de depósito. Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 5.1 Falso. Los diagramas de estado modelan parte del comportamiento del sistema. 5.2 a. 5.3 La figura 5.32 presenta un diagrama de actividad para una transacción de depósito. El diagrama modela las acciones que ocurren una vez que el usuario selecciona la opción de depósito del menú principal, y antes de que el ATM regrese al usua- rio al menú principal. Recuerde que una parte del proceso de recibir un monto de depósito de parte del usuario implica con- vertir un número entero de centavos a una cantidad en dólares. Recuerde también que para acreditar un monto de depósito a una cuenta sólo hay que incrementar el atributo saldoTotal del objeto Cuenta del usuario. El banco actualiza el atributo saldoDisponible del objeto Cuenta del usuario sólo después de confirmar el monto de efectivo en el sobre de depósito y después de verificar los cheques que haya incluido; esto ocurre en forma independiente del sistema ATM. 5.12 Conclusión En este capítulo completamos nuestra introducción a las instrucciones de control de Java, las cuales nos permi- ten controlar el flujo de la ejecución en los métodos. El capítulo 4 trató acerca de las instrucciones de control if,
  • 239. if...else y while de Java. En este capítulo vimos el resto de las instrucciones de control de Java: for, do...whi- le y switch. Aquí le mostramos que cualquier algoritmo puede desarrollarse mediante el uso de combinaciones de instrucciones de secuencia (es decir, instrucciones que se listan en el orden en el que deben ejecutarse), los tres tipos de instrucciones de selección (if, if...else y switch) y los tres tipos de instrucciones de repetición (while, do...while y for). En este capítulo y en el anterior hablamos acerca de cómo puede combinar estos bloques de construcción para utilizar las técnicas, ya probadas, de construcción de programas y solución de problemas. En este capítulo también se introdujeron los operadores lógicos de Java, que nos permiten utilizar expresiones condicionales más complejas en las instrucciones de control. En el capítulo 3 presentamos los conceptos básicos de los objetos, las clases y los métodos. En los capítulos 4 y 5 se introdujeron los tipos de instrucciones de control que podemos utilizar para especificar la lógica de los programas en métodos. En el capítulo 6 examinaremos los métodos con más detalle. Figura 5.32 | Diagrama de actividad para una transacción de depósito. [el usuario canceló la transacción] [el usuario escribió un monto] [se recibió el sobre de depósito] [no se recibió el sobre del depósito] pedir al usuario que escriba un monto a depositar o que cancele recibir la entrada del usuario tratar de recibir el sobre de depósito interactuar con la base de datos para abonar el monto a la cuenta del usuario mostrar mensaje de error establecer el atributo monto instruir al usuario para que inserte el sobre de depósito Resumen 201
  • 240. Resumen Sección 5.2 Fundamentos de la repetición controlada por contador • La repetición controlada por contador requiere una variable de control (o contador de ciclo), el valor inicial de la variable de control, el incremento (o decremento) en base al cual se modifica la variable de control cada vez que pasa por el ciclo (lo que también se conoce como cada iteración del ciclo) y la condición de continuación de ciclo, que determina si el ciclo debe seguir ejecutándose. • Podemos declarar e inicializar una variable en la misma instrucción. Sección 5.3 Instrucción de repetición for • La instrucción while puede usarse para implementar cualquier ciclo controlado por contador. • La instrucción de repetición for especifica los detalles acerca de la repetición controlada por contador, en una sola línea de código. • Cuando la instrucción for comienza a ejecutarse, su variable de control se declara y se inicializa. Después, el pro- grama verifica la condición de continuación de ciclo. Si al principio la condición es verdadera, el cuerpo se ejecuta. Después de ejecutar el cuerpo del ciclo, se ejecuta la expresión de incremento. Después, se lleva a cabo otra vez la prueba de continuación de ciclo, para determinar si el programa debe continuar con la siguiente iteración del ciclo. • El formato general de la instrucción for es for ( inicialización; condiciónDeContinuacionDeCiclo; incremento ) instrucción en donde la expresión inicialización asigna un nombre a la variable de control del ciclo y, de manera opcional, pro- porciona su valor inicial. condiciónDeContinuaciónDeCiclo es la condición que determina si el ciclo debe continuar su ejecución, e incremento modifica el valor de la variable de control (posiblemente un incremento o decremento), de manera que la condición de continuación de ciclo se vuelve falsa en un momento dado. Los dos signos de punto y coma en el encabezado for son obligatorios. • En la mayoría de los casos, la instrucción for se puede representar con una instrucción while equivalente, de la siguiente forma: inicialización; while ( condiciónDeContinuaciónDeCiclo ) { instrucción incremento; } • Por lo general, las instrucciones for se utilizan para la repetición controlada por contador y las instrucciones while para la repetición controlada por centinela. • Si la expresión de inicialización en el encabezado del for declara la variable de control, ésta sólo puede usarse en esa instrucción for; no existirá fuera de la instrucción for. • Las tres expresiones en un encabezado for son opcionales. Si se omite la condiciónDeContinuaciónDeCiclo, Java asu- me que la condición de continuación de ciclo siempre es verdadera, con lo cual se crea un ciclo infinito. Podríamos omitir la expresión inicialización si el programa inicializa la variable de control antes del ciclo. Podríamos omitir la expresión incremento si el programa calcula el incremento con instrucciones en el cuerpo del ciclo, o si no se necesita un incremento. • La expresión de incremento en un for actúa como si fuera una instrucción independiente al final del cuerpo del for. • El incremento de una instrucción for puede ser también negativo, en cuyo caso es en realidad un decremento, y el ciclo cuenta en forma descendente. • Si al principio la condición de continuación de ciclo es false, el programa no ejecuta el cuerpo de la instrucción for. En vez de ello, la ejecución continúa con la instrucción después del for. Sección 5.4 Ejemplos sobre el uso de la instrucción for • Java trata a las constantes de punto flotante, como 1000.0 y 0.05, como de tipo double. De manera similar, Java trata a las constantes de números enteros, como 7 y -22, como de tipo int. 202 Capítulo 5 Instrucciones de control: parte 2
  • 241. • El especificador de formato %20s indica que el objeto String de salida debe mostrarse con una anchura de campo de 20; es decir, printf muestra el valor con al menos 20 posiciones de caracteres. Si el valor a imprimir es menor de 20 posiciones de caracteres de ancho, se justifica a la derecha en el campo de manera predeterminada. • Math.pow(x, y) calcula el valor de x elevado a la y-ésima potencia. El método recibe dos argumentos double y devuelve un valor double. • La bandera de formato coma (,) en un especificador de formato (por ejemplo, %,20.2f) indica que un valor de punto flotante debe imprimirse con un separador de agrupamiento. El separador actual que se utiliza es específico de la configuración regional del usuario (es decir, el país). Por ejemplo, en los Estados Unidos el número se impri- mirá usando comas para separar cada tres dígitos, y un punto decimal para separar la parte fraccionaria del número, como en 1,234.45. • El .2 en un especificador de formato (por ejemplo, %,20.2f) indica la precisión de un número con formato; en este caso, el número se redondea a la centésima más cercana y se imprime con dos dígitos a la derecha del punto decimal. Sección 5.5 Instrucción de repetición do...while • La instrucción de repetición do...while es similar a la instrucción while. En la instrucción while, el programa eva- lúa la condición de continuación de ciclo al principio del ciclo, antes de ejecutar su cuerpo; si la condición es falsa, el cuerpo nunca se ejecuta. La instrucción do...while evalúa la condición de continuación de ciclo después de ejecutar el cuerpo del ciclo; por lo tanto, el cuerpo siempre se ejecuta por lo menos una vez. Cuando termina una instrucción do...while, la ejecución continúa con la siguiente instrucción en secuencia. • No es necesario usar llaves en la instrucción de repetición do...while si sólo hay una instrucción en el cuerpo. Sin embargo, la mayoría de los programadores incluyen las llaves, para evitar confusión entre las instrucciones while y do...while. Sección 5.6 Instrucción de selección múltiple switch • La instrucción switch de selección múltiple realiza distintas acciones, con base en los posibles valores de una variable o expresión entera. Cada acción se asocia con el valor de una expresión entera constante (es decir, un valor constante de tipo byte, short, int o char, pero no long) que la variable o expresión en la que se basa el switch puede asumir. • El indicador de fin de archivo es una combinación de teclas dependiente del sistema, que el usuario escribe para indicar que no hay más datos qué introducir. En los sistemas UNIX/Linux/Mac OS X, el fin de archivo se introduce escribiendo la secuencia <ctrl> d en una línea por sí sola. Esta notación significa que hay que imprimir al mismo tiempo la tecla ctrl y la tecla d. En los sistemas Windows, el fin de archivo se puede introducir escribiendo <ctrl> z. • El método hasNext de Scanner determina si hay más datos qué introducir. Este método devuelve el valor boolean true si hay más datos; en caso contrario, devuelve false. Mientras no se haya escrito el indicador de fin de archivo, el método hasNext devolverá true. • La instrucción switch consiste en un bloque que contiene una secuencia de etiquetas case y un caso default opcional. • Cuando el flujo de control llega al switch, el programa evalúa la expresión de control del switch. El programa com- para el valor de la expresión de control (que debe evaluarse como un valor entero de tipo byte, char, short o int) con cada etiqueta case. Si ocurre una coincidencia, el programa ejecuta las instrucciones para esa etiqueta case. • Al enlistar etiquetas case en forma consecutiva, sin instrucciones entre ellas, permite que las etiquetas ejecuten el mismo conjunto de instrucciones. • La instrucción switch no cuenta con un mecanismo para evaluar rangos de valores, por lo que todo valor que deba evaluarse tiene que enlistarse en una etiqueta case separada. • Cada case puede tener varias instrucciones. La instrucción switch se diferencia de las otras instrucciones de con- trol, en cuanto a que no requiere llaves alrededor de varias instrucciones en una etiqueta case. • Sin las instrucciones break, cada vez que ocurre una coincidencia en el switch, las instrucciones para ese case y los case subsiguientes se ejecutarán hasta llegar a una instrucción break o al final de la instrucción switch. A menudo esto se conoce como “pasar” a las instrucciones en las etiquetas case subsiguientes. • Si no ocurre una coincidencia entre el valor de la expresión de control y una etiqueta case, se ejecuta el caso default opcional. Si no ocurre una coincidencia y la instrucción switch no tiene un caso default, el control del programa simplemente continúa con la primera instrucción después del switch. • La instrucción break no se requiere para la última etiqueta case de la instrucción switch (ni para el caso default opcional, cuando aparece al último), ya que la ejecución continúa con la siguiente instrucción después del switch. Sección 5.7 Instrucciones break y continue • Además de las instrucciones de selección y repetición, Java cuenta con las instrucciones break y continue (que presentamos en esta sección y en el apéndice N, Instrucciones break y continue etiquetadas) para alterar el flujo Resumen 203
  • 242. de control. La sección anterior mostró cómo se puede utilizar break para terminar la ejecución de una instrucción switch. Esta sección habla acerca de cómo utilizar break en instrucciones de repetición. • Cuando la instrucción break se ejecuta en una instrucción while, for, do...while o switch, provoca la salida inmediata de esa instrucción. La ejecución continúa con la primera instrucción después de la instrucción de control. • Cuando la instrucción continue se ejecuta en una instrucción while, for o do...while, omite el resto de las instrucciones en el cuerpo del ciclo y continúa con la siguiente iteración del mismo. En las instrucciones while y do...while, el programa evalúa la prueba de continuación de ciclo inmediatamente después de que se ejecuta la instrucción continue. En una instrucción for, se ejecuta la expresión de incremento y después el programa evalúa la prueba de continuación de ciclo. Sección 5.8 Operadores lógicos • Las condiciones simples se expresan en términos de los operadores relacionales >, <, >= y <=, y los operadores de igualdad == y !=, y cada expresión sólo evalúa una condición. • Los operadores lógicos nos permiten formar condiciones más complejas, mediante la combinación de condiciones simples. Los operadores lógicos son && (AND condicional), || (OR condicional), & (AND lógico booleano), | (OR inclusivo lógico booleano), ^ (OR exclusivo lógico booleano) y ! (NOT lógico). • Para asegurar que dos condiciones sean ambas verdaderas antes de elegir cierta ruta de ejecución, utilice el operador && (AND condicional). Este operador da como resultado verdadero sí, y sólo si ambas de sus condiciones simples son verdaderas. Si una o ambas condiciones simples son falsas, la expresión completa es falsa. • Para asegurar que una o ambas condiciones sean verdaderas antes de elegir cierta ruta de ejecución, utilice el opera- dor || (OR condicional), que se evalúa como verdadero si una o ambas de sus condiciones simples son verdaderas. • Las partes de una expresión que contienen operadores && o || se evalúan sólo hasta que se conoce si la condición es verdadera o falsa. Esta característica de las expresiones AND condicional y OR condicional se conoce como evalua- ción de corto circuito. • Los operadores AND lógico booleano (&) y OR inclusivo lógico booleano (|) funcionan de manera idéntica a los operadores && (AND condicional) y || (OR condicional), con una excepción: los operadores lógicos boleanos siem- pre evalúan ambos operandos (es decir, no realizan una evaluación de corto circuito). • Una condición simple que contiene el operador OR exclusivo lógico booleano (^) es true si, y sólo si uno de sus operandos es true y el otro es false. Si ambos operandos son true o ambos son false, toda la condición es false. También se garantiza que este operador evaluará ambos operandos. • El operador ! (NOT lógico, también conocido como negación lógica o complemento lógico) “invierte” el signifi- cado de una condición. El operador de negación lógico es un operador unario, que sólo tiene una condición como operando. Este operador se coloca antes de una condición, para elegir una ruta de ejecución si la condición original (sin el operador de negación lógico) es false. • En la mayoría de los casos, podemos evitar el uso de la negación lógica si expresamos la condición de manera distin- ta, con un operador relacional o de igualdad apropiado. Sección 5.10 (Opcional) Ejemplo práctico de GUI y gráficos: dibujo de rectángulos y óvalos • Los métodos drawRect y drawOval de Graphics dibujan rectángulos y óvalos, respectivamente. • El método drawRect de Graphics requiere cuatro argumentos. Los primeros dos representan las coordenadas x y y de la esquina superior izquierda del rectángulo; los otros dos representan la anchura y la altura del rectángulo. • Al dibujar un óvalo, se crea un rectángulo imaginario llamado rectángulo delimitador, y se coloca en su interior un óvalo que toca los puntos medios de los cuatro lados del rectángulo delimitador. El método drawOval requiere los mismos cuatro argumentos que el método drawRect. Los argumentos especifican la posición y el tamaño del rectángulo delimitador para el óvalo. Terminología 204 Capítulo 5 Instrucciones de control: parte 2 - (menos), bandera de formato !, operador NOT lógico %b, especificador de formato &, operador AND lógico booleano &&, operador AND condicional , (coma), bandera de formato ^, operador OR exclusivo lógico booleano |, operador OR lógico booleano ||, operador OR condicional alcance de una variable anchura de campo AND condicional (&&) AND lógico booleano (&) break, instrucción case, etiqueta complemento lógico (!) condición de continuación de ciclo condición simple
  • 243. constante de caracteres continue, instrucción decrementar una variable de control default, caso en una instrucción switch do…while, instrucción de repetición drawOval, método de la clase Graphics (GUI) drawRect, método de la clase Graphics (GUI) efecto secundario error por desplazamiento en 1 evaluación de corto circuito expresión de control de una instrucción switch expresión entera constante final, palabra clave for, encabezado for, encabezado de la instrucción for, instrucción de repetición hasNext, método de la clase Scanner incrementar una variable de control indicador de fin de archivo instrucción de repetición instrucciones de control anidadas instrucciones de control apiladas instrucciones de control de una sola entrada/una sola salida iteración de un ciclo justificar a la derecha justificar a la izquierda método ayudante negación lógica (!) operadores lógicos OR condicional (||) OR exclusivo lógico booleano (^) OR inclusivo lógico booleano (|) rectángulo delimitador de un óvalo (GUI) regla de anidamiento regla de apilamiento selección múltiple static, método switch, instrucción de selección tabla de verdad valor inicial variable constante variable de control Ejercicios de autoevaluación 205 Ejercicios de autoevaluación 5.1 Complete los siguientes enunciados: a) Por lo general, las instrucciones ______________ se utilizan para la repetición controlada por contador y las instrucciones ______________ se utilizan para la repetición controlada por centinela. b) La instrucción do...while evalúa la condición de continuación de ciclo _______________ ejecutar el cuerpo del ciclo; por lo tanto, el cuerpo siempre se ejecuta por lo menos una vez. c) La instrucción _______________ selecciona una de varias acciones, con base en los posibles valores de una variable o expresión entera. d) Cuando se ejecuta la instrucción _______________ en una instrucción de repetición, se omite el resto de las instrucciones en el cuerpo del ciclo y se continúa con la siguiente iteración del ciclo. e) El operador ______________ se puede utilizar para asegurar que ambas condiciones sean verdaderas, antes de elegir cierta ruta de ejecución. f) Si al principio, la condición de continuación de ciclo en un encabezado for es _____________, el progra- ma no ejecuta el cuerpo de la instrucción for. g) Los métodos que realizan tareas comunes y no requieren objetos se llaman métodos _____________. 5.2 Conteste con verdadero o falso a cada una de las siguientes proposiciones; en caso de ser falso, explique por qué. a) El caso default es requerido en la instrucción de selección switch. b) La instrucción break es requerida en el último caso de una instrucción de selección switch. c) La expresión ( ( x > y ) && ( a < b ) ) es verdadera si x > y es verdadera, o si a < b es verdadera. d) Una expresión que contiene el operador || es verdadera si uno o ambos de sus operandos son verdaderos. e) La bandera de formato coma (,) en un especificador de formato (por ejemplo, %,20.2f) indica que un valor debe imprimirse con un separador de miles. f) Para evaluar un rango de valores en una instrucción switch, use un guión corto (–) entre los valores inicial y final del rango en una etiqueta case. g) Al enlistar las instrucciones case en forma consecutiva, sin instrucciones entre ellas, pueden ejecutar el mismo conjunto de instrucciones. 5.3 Escriba una instrucción o un conjunto de instrucciones en Java, para realizar cada una de las siguientes tareas: a) Sumar los enteros impares entre 1 y 99, utilizando una instrucción for. Suponga que se han declarado las variables enteras suma y cuenta.
  • 244. b) Calcular el valor de 2.5 elevado a la potencia de 3, utilizando el método pow. c) Imprimir los enteros del 1 al 20, utilizando un ciclo while y la variable contador i. Suponga que la variable i se ha declarado, pero no se ha inicializado. Imprima solamente cinco enteros por línea. [Sugerencia: use el cálculo i % 5. Cuando el valor de esta expresión sea 0, imprima un carácter de nueva línea; de lo contrario, imprima un carácter de tabulación. Suponga que este código es una aplicación. Utilice el método System. out.println() para producir el carácter de nueva línea, y el método System.out.print( ‘t’ ) para producir el carácter de tabulación]. d) Repita la parte (c), usando una instrucción for. 5.4 Encuentre el error en cada uno de los siguientes segmentos de código, y explique cómo corregirlo: a) i = 1; while ( i <= 10 ); i++; } b) for ( k = 0.1; k != 1.0; k += 0.1 ) System.out.println ( k ); c) switch ( n ) { case 1: System.out.println( "El número es 1" ); case 2: System.out.println( "El número es 2" ); break; default: System.out.println( "El número no es 1 ni 2" ); break; } d) El siguiente código debe imprimir los valores 1 a 10: n = 1; while ( n < 10 ) System.out.println( n++ ); Respuestas a los ejercicios de autoevaluación 5.1 a) for, while. b) después de. c) switch. d) continue. e) && (AND condicional). f) false. g) static. 5.2 a) Falso. El caso default es opcional. Si no se necesita una acción predeterminada, entonces no hay necesi- dad de un caso default. b) Falso. La instrucción break se utiliza para salir de la instrucción switch. La instrucción break no se requiere para el último caso en una instrucción switch. c) Falso. Ambas expresiones relacionales deben ser verdaderas para que toda la expresión sea verdadera, cuando se utilice el operador &&. d) Verdadero. e) Verdadero. f) Falso. La instrucción switch no cuenta con un mecanismo para evaluar rangos de valores, por lo que todo valor que deba evaluarse se debe enlistar en una etiqueta case por separado. g) Verdadero. 5.3 a) suma = 0; for ( cuenta = 1; cuenta <= 99; cuenta += 2 ) suma += cuenta; b) double resultado = Math.pow( 2.5, 3 ); c) i = 1; while ( i <= 20 ) { System.out.print( i ); if ( i % 5 == 0 ) System.out.println() else System.out.print( 't' ); ++i; } 206 Capítulo 5 Instrucciones de control: parte 2
  • 245. d) for ( i = 1; i <= 20; i++ ) { System.out.print( i ); if ( i % 5 == 0 ) System.out.println(); else System.out.print( 't' ); } 5.4 a) Error: el punto y coma después del encabezado while provoca un ciclo infinito, y falta una llave izquierda. Corrección: reemplazar el punto y coma por una llave izquierda ({), o eliminar tanto el punto y coma (;) como la llave derecha (}). b) Error: utilizar un número de punto flotante para controlar una instrucción for tal vez no funcione, ya que los números de punto flotante se representan sólo aproximadamente en la mayoría de las computadoras. Corrección: utilice un entero, y realice el cálculo apropiado en orden para obtener los valores deseados: for ( k = 1; k != 10, k++ ) System.out.println( ( double ) k / 10 ); c) Error: el código que falta es la instrucción break en las instrucciones del primer case. Corrección: agregue una instrucción break al final de las instrucciones para el primer case. Observe que esta omisión no es necesariamente un error, si el programador desea que la instrucción del case 2: se eje- cute siempre que lo haga la instrucción del case 1:. d) Error: se está utilizando un operador relacional inadecuado en la condición de continuación de la instruc- ción de repetición while. Corrección: use <= en vez de <, o cambie 10 a 11. Ejercicios 5.5 Describa los cuatro elementos básicos de la repetición controlada por contador. 5.6 Compare y contraste las instrucciones de repetición while y for. 5.7 Hable sobre una situación en la que sería más apropiado usar una instrucción do...while que una instrucción while. Explique por qué. 5.8 Compare y contraste las instrucciones break y continue. 5.9 Encuentre y corrija el(los) error(es) en cada uno de los siguientes fragmentos de código: a) for ( i = 100, i >= 1, i++ ) System.out.println ( i ); b) El siguiente código debe imprimirse sin importar si el valor entero es par o impar: switch ( valor % 2 ) { case 0: System.out.println( "Entero par" ); case 1: System.out.println( "Entero impar" ); } c) El siguiente código debe imprimir los enteros impares del 19 al 1: for ( i = 19; i >= 1; i += 2 ) System.out.println( i ); d) El siguiente código debe imprimir los enteros pares del 2 al 100: contador = 2; do { System.out.println( contador ); contador += 2; } While ( contador < 100 ); Ejercicios 207
  • 246. 5.10 ¿Qué es lo que hace el siguiente programa? 1 public class Imprimir 2 { 3 public static void main( String args[] ) 4 { 5 for ( int i = 1; i <= 10; i++ ) 6 { 7 for (int j = 1; j <= 5; j++ ) 8 System.out.print( '@' ); 9 10 System.out.println(); 11 } // fin del for exterior 12 } // fin de main 13 } // fin de la clase Imprimir. 5.11 Escriba una aplicación que encuentre el menor de varios enteros. Suponga que el primer valor leído especifica el número de valores que el usuario introducirá. 5.12 Escriba una aplicación que calcule el producto de los enteros impares del 1 al 15. 5.13 Los factoriales se utilizan frecuentemente en los problemas de probabilidad. El factorial de un entero positivo n (se escribe como n!) es igual al producto de los enteros positivos del 1 a n. Escriba una aplicación que evalúe los fac- toriales de los enteros del 1 al 5. Muestre los resultados en formato tabular. ¿Qué dificultad podría impedir que usted calculara el factorial de 20? 5.14 Modifique la aplicación de interés compuesto de la figura 5.6, repitiendo sus pasos para las tasas de interés del 5, 6, 7, 8, 9 y 10%. Use un ciclo for para variar la tasa de interés. 5.15 Escriba una aplicación que muestre los siguientes patrones por separado, uno debajo del otro. Use ciclos for para generar los patrones. Todos los asteriscos (*) deben imprimirse mediante una sola instrucción de la forma System. out.print( '*' ); la cual hace que los asteriscos se impriman uno al lado del otro. Puede utilizarse una instrucción de la forma System.out.println(); para posicionarse en la siguiente línea. Puede usarse una instrucción de la forma System.out.print( ' ' ); para mostrar un espacio para los últimos dos patrones. No debe haber ninguna otra instrucción de salida en el programa. [Sugerencia: los últimos dos patrones requieren que cada línea empiece con un número apropiado de espacios en blanco]. (a) (b) (c) (d) * ********** ********** * ** ********* ********* ** *** ******** ******** *** **** ******* ******* **** ***** ****** ****** ***** ****** ***** ***** ****** ******* **** **** ******* ******** *** *** ******** ********* ** ** ********* ********** * * ********** 5.16 Una aplicación interesante de las computadoras es dibujar gráficos convencionales y de barra. Escriba una apli- cación que lea cinco números, cada uno entre 1 y 30. Por cada número leído, su programa debe mostrar ese número de asteriscos adyacentes. Por ejemplo, si su programa lee el número 7, debe mostrar *******. 5.17 Un almacén de pedidos por correo vende cinco productos cuyos precios de venta son los siguientes: producto 1, $2.98; producto 2, $4.50; producto 3, $9.98; producto 4, $4.49 y producto 5, $6.87. Escriba una aplicación que lea una serie de pares de números, como se muestra a continuación: a) número del producto; b) cantidad vendida. Su programa debe utilizar una instrucción switch para determinar el precio de venta de cada producto. Debe calcular y mostrar el valor total de venta de todos los productos vendidos. Use un ciclo controlado por centinela para determinar cuándo debe el programa dejar de iterar para mostrar los resultados finales. 208 Capítulo 5 Instrucciones de control: parte 2
  • 247. 5.18 Modifique la aplicación de la figura 5.6, de manera que se utilicen sólo enteros para calcular el interés compues- to. [Sugerencia: trate todas las cantidades monetarias como números enteros de centavos. Luego divida el resultado en su porción de dólares y su porción de centavos, utilizando las operaciones de división y residuo, respectivamente. Inserte un punto entre las porciones de dólares y centavos]. 5.19 Suponga que i = 1, j = 2, k = 3 y m = 2. ¿Qué es lo que imprime cada una de las siguientes instruc- ciones? a) System.out.println( i == 1 ); b) System.out.println( j == 3 ); c) System.out.println( ( i >= 1 ) && ( j < 4 ) ); d) System.out.println( ( m <= 99 ) & ( k < m ) ); e) System.out.println( ( j >= i ) || ( k == m ) ); f) System.out.println( ( k + m < j ) | ( 3 – j >= k ) ); g) System.out.println( !( k > m ) ); 5.20 Calcule el valor de π a partir de la serie infinita p 4 4 3 -- - – 4 5 -- - 4 7 -- - – 4 9 -- - 4 11 ----- - – … + + + = Imprima una tabla que muestre el valor aproximado de π, calculando un término de esta serie, dos términos, tres, etcé- tera. ¿Cuántos términos de esta serie tiene que utilizar para obtener 3.14? ¿3.141? ¿3.1415? ¿3.14159? 5.21 (Triples de Pitágoras) Un triángulo recto puede tener lados cuyas longitudes sean valores enteros. El conjunto de tres valores enteros para las longitudes de los lados de un triángulo recto se conoce como triple de Pitágoras. Las longitudes de los tres lados deben satisfacer la relación que establece que la suma de los cuadrados de dos lados es igual al cuadrado de la hipotenusa. Escriba una aplicación para encontrar todos los triples de Pitágoras para lado1, lado2, y la hipotenusa, que no sean mayores de 500. Use un ciclo for triplemente anidado para probar todas las posibilidades. Este método es un ejemplo de la computación de “fuerza bruta”. En cursos de ciencias computacionales más avanzados aprenderá que existen muchos problemas interesantes para los cuales no hay otra metodología algorítmica conocida, más que el uso de la fuerza bruta. 5.22 Modifique el ejercicio 5.15 para combinar su código de los cuatro triángulos separados de asteriscos, de manera que los cuatro patrones se impriman uno al lado del otro. [Sugerencia: utilice astutamente los ciclos for anidados]. 5.23 (Leyes de De Morgan) En este capítulo, hemos hablado sobre los operadores lógicos &&, &, ||, |, ^ y !. Algunas veces, las leyes de De Morgan pueden hacer que sea más conveniente para nosotros expresar una expresión lógica. Estas leyes establecen que la expresión ! (condición1 && condición2) es lógicamente equivalente a la expresión (!condición1 || !condición2). También establecen que la expresión !(condición1 || condición2) es lógicamente equivalente a la expresión (!condición1 && !condición2). Use las leyes de De Morgan para escribir expresiones equivalentes para cada una de las siguientes expresiones, luego escriba una aplicación que demuestre que, tanto la expresión original como la nueva expresión, producen en cada caso el mismo valor: a) !( x < 5 ) && !( y >= 7 ) b) !( a == b ) || !( g != 5 ) c) !( ( x <= 8 ) && ( y > 4 ) ) d) !( ( i > 4 ) || ( j <= 6 ) ) 5.24 Escriba una aplicación que imprima la siguiente figura de rombo. Puede utilizar instrucciones de salida que impriman un solo asterisco (*), un solo espacio o un solo carácter de nueva línea. Maximice el uso de la repetición (con instrucciones for anidadas), y minimice el número de instrucciones de salida. Ejercicios 209 * *** ***** ******* ********* ******* ***** *** *
  • 248. 5.25 Modifique la aplicación que escribió en el ejercicio 5.24, para que lea un número impar en el rango de 1 a 19, de manera que especifique el número de filas en el rombo. Su programa debe entonces mostrar un rombo del tamaño apropiado. 5.26 Una crítica de las instrucciones break y continue es que ninguna es estructurada. En realidad, estas instruc- ciones pueden reemplazarse en todo momento por instrucciones estructuradas, aunque hacerlo podría ser inadecuado. Describa, en general, cómo eliminaría las instrucciones break de un ciclo en un programa, para reemplazarlas con alguna de las instrucciones estructuradas equivalentes. [Sugerencia: la instrucción break se sale de un ciclo desde el cuerpo de éste. La otra forma de salir es que falle la prueba de continuación de ciclo. Considere utilizar en la prueba de continuación de ciclo una segunda prueba que indique una “salida anticipada debido a una condición de ‘interrup- ción’”]. Use la técnica que desarrolló aquí para eliminar la instrucción break de la aplicación de la figura 5.12. 5.27 ¿Qué hace el siguiente segmento de programa? for ( i = 1; i <= 5; i++ ) { for ( j = 1; j <= 3; j++ ) { for ( k = 1; k <= 4; k++ ) System.out.print( '*' ); System.out.println(); } // fin del for interior System.out.println(); } // fin del for exterior 5.28 Describa, en general, cómo eliminaría las instrucciones continue de un ciclo en un programa, para reemplazar- las con uno de sus equivalentes estructurados. Use la técnica que desarrolló aquí para eliminar la instrucción continue del programa de la figura 5.13. 5.29 (Canción “Los Doce Días de Navidad”) Escriba una aplicación que utilice instrucciones de repetición y switch para imprimir la canción “Los Doce Días de Navidad”. Una instrucción switch debe utilizarse para imprimir el día (es decir, “primer”, “segundo”, etcétera). Una instrucción switch separada debe utilizarse para imprimir el resto de cada verso. Visite el sitio Web en.wikipedia.org/wiki/Twelvetide para obtener la letra completa de la canción. 210 Capítulo 5 Instrucciones de control: parte 2
  • 249. Métodos: un análisis más detallado OBJETIVOS En este capítulo aprenderá a: Conocer cómo se asocian los métodos y los campos static con toda una clase, en vez de asociarse con instancias específicas de la clase. Utilizar los métodos comunes de Math disponibles en la API de Java. Comprender los mecanismos para pasar información entre métodos. Comprender cómo se soporta el mecanismo de llamada/retorno de los métodos mediante la pila de llamadas a métodos y los registros de activación. Conocer cómo los paquetes agrupan las clases relacionadas. Utilizar la generación de números aleatorios para implementar aplicaciones para juegos. Comprender cómo se limita la visibilidad de las declaraciones a regiones específicas de los programas. Acerca de la sobrecarga de métodos y cómo crear métodos sobrecargados. Q Q Q Q Q Q Q Q La más grande invención del siglo diecinueve fue la invención del método de la invención. —Alfred North Whitehead Llámame Ismael. —Herman Melville Cuando me llames así, sonríe. —Owen Wister Respóndeme en una palabra. —William Shakespeare ¡Oh! volvió a llamar ayer, ofreciéndome volver. —William Shakespeare Hay un punto en el cual los métodos se devoran a sí mismos. —Frantz Fanon 6
  • 250. 212 Capítulo 6 Métodos: un análisis más detallado 6.1 Introducción La mayoría de los programas de cómputo que resuelven los problemas reales son mucho más extensos que los programas que se presentan en los primeros capítulos de este libro. La experiencia ha demostrado que la mejor manera de desarrollar y mantener un programa extenso es construirlo a partir de pequeñas piezas sencillas, o módulos. A esta técnica se le llama divide y vencerás. En el capítulo 3 presentamos los métodos, y en éste lo estudiaremos con más detalle. Haremos énfasis en cómo declarar y utilizar métodos para facilitar el diseño, la implementación, operación y el mantenimiento de programas extensos. En breve verá que es posible que ciertos métodos, conocidos como static (métodos estáticos), puedan llamarse sin necesidad de que exista un objeto de la clase a la que pertenecen. Aprenderá a declarar un método con más de un parámetro. También aprenderá acerca de cómo Java es capaz de llevar el rastro de qué método se ejecuta en un momento dado, cómo se mantienen las variables locales de los métodos en memoria y cómo sabe un método a dónde regresar una vez que termina su ejecución. Hablaremos brevemente sobre las técnicas de simulación mediante la generación de números aleatorios y desarrollaremos una versión de un juego de dados conocido como “craps”, el cual utiliza la mayoría de las téc- nicas de programación que usted ha aprendido hasta este capítulo. Además, aprenderá a declarar valores que no pueden cambiar (es decir, constantes) en sus programas. Muchas de las clases que utilizará o creará mientras desarrolla aplicaciones tendrán más de un método con el mismo nombre. Esta técnica, conocida como sobrecarga, se utiliza para implementar métodos que realizan tareas similares, para argumentos de distintos tipos, o para un número distinto de argumentos. En el capítulo 15, Recursividad, continuaremos nuestra discusión sobre los métodos. La recursividad propor- ciona una manera completamente distinta de pensar acerca de los métodos y los algoritmos. 6.2 Módulos de programas en Java Existen tres tipos de módulos en Java: métodos, clases y paquetes. Para escribir programas en Java, se combinan los nuevos métodos y clases que usted escribe con los métodos y clases predefinidos, que están disponibles en la Interfaz de Programación de Aplicaciones de Java (también conocida como la API de Java o biblioteca de 6.1 Introducción 6.2 Módulos de programas en Java 6.3 Métodos static, campos static y la clase Math 6.4 Declaración de métodos con múltiples parámetros 6.5 Notas acerca de cómo declarar y utilizar los métodos 6.6 Pila de llamadas a los métodos y registros de activación 6.7 Promoción y conversión de argumentos 6.8 Paquetes de la API de Java 6.9 Ejemplo práctico: generación de números aleatorios 6.9.1 Escalamiento y desplazamiento generalizados de números aleatorios 6.9.2 Repetitividad de números aleatorios para prueba y depuración 6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones) 6.11 Alcance de las declaraciones 6.12 Sobrecarga de métodos 6.13 (Opcional) Ejemplo práctico de GUI y gráficos: colores y figuras rellenas 6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones de las clases 6.15 Conclusión Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios Pla n g e ne r a l
  • 251. clases de Java) y en diversas bibliotecas de clases. Por lo general, las clases relacionadas están agrupadas en paque- tes, de manera que se pueden importar a los programas y reutilizarse. En el capítulo 8 aprenderá a agrupar sus propias clases en paquetes. La API de Java proporciona una vasta colección de clases que contienen métodos para realizar cálculos matemáticos, manipulaciones de cadenas, manipulaciones de caracteres, operaciones de entrada/ salida, comprobación de errores y muchas otras operaciones útiles. Buena práctica de programación 6.1 Procure familiarizarse con la vasta colección de clases y métodos que proporciona la API de Java (java.sun.com/ javase/6/docs/api/). En la sección 6.8 presentaremos las generalidades acerca de varios paquetes comunes. En el apéndice J, le explicaremos cómo navegar por la documentación de la API de Java. Observación de ingeniería de software 6.1 Evite reinventar la rueda. Cuando sea posible, reutilice las clases y métodos de la API de Java. Esto reduce el tiempo de desarrollo de los programas y evita que se introduzcan errores de programación. Los métodos (también conocidos como funciones o procedimientos en otros lenguajes) permiten al progra- mador dividir un programa en módulos, por medio de la separación de sus tareas en unidades autónomas. Usted ha declarado métodos en todos los programas que ha escrito; a estos métodos se les conoce algunas veces como métodos declarados por el programador. Las instrucciones en los cuerpos de los métodos se escriben sólo una vez, y se reutilizan tal vez desde varias ubicaciones en un programa; además, están ocultas de otros métodos. Una razón para dividir un programa en módulos mediante los métodos es la metodología “divide y vence- rás”, que hace que el desarrollo de programas sea más fácil de administrar, ya que se pueden construir programas a partir de piezas pequeñas y simples. Otra razón es la reutilización de software (usar los métodos existentes como bloques de construcción para crear nuevos programas). A menudo se pueden crear programas a partir de métodos estandarizados, en vez de tener que crear código personalizado. Por ejemplo, en los programas anteriores no tuvimos que definir cómo leer los valores de datos del teclado; Java proporciona estas herramientas en la clase Scanner. Una tercera razón es para evitar la repetición de código. El proceso de dividir un programa en métodos significativos hace que el programa sea más fácil de depurar y mantener. Observación de ingeniería de software 6.2 Para promover la reutilización de software, cada método debe limitarse de manera que realice una sola tarea bien definida, y su nombre debe expresar esa tarea con efectividad. Estos métodos hacen que los programas sean más fáci- les de escribir, depurar, mantener y modificar. Tip para prevenir errores 6.1 Un método pequeño que realiza una tarea es más fácil de probar y depurar que un método más grande que realiza muchas tareas. Observación de ingeniería de software 6.3 Si no puede elegir un nombre conciso que exprese la tarea de un método, tal vez esté tratando de realizar diversas tareas en un mismo método. Por lo general, es mejor dividirlo en varias declaraciones de métodos más pequeños. Un método se invoca mediante una llamada, y cuando el método que se llamó completa su tarea, devuelve un resultado, o simplemente el control al método que lo llamó. Una analogía a esta estructura de programa es la forma jerárquica de la administración (figura 6.1). Un jefe (el solicitante) pide a un trabajador (el método lla- mado) que realice una tarea y que le reporte (devuelva) los resultados después de completar la tarea. El método jefe, no sabe cómo el método trabajador, realiza sus tareas designadas. Tal vez el trabajador llame a otros métodos trabajadores, sin que lo sepa el jefe. Este “ocultamiento” de los detalles de implementación fomenta la buena ingeniería de software. La figura 6.1 muestra al método jefe comunicándose con varios métodos trabajadores en forma jerárquica. El método jefe divide las responsabilidades entre los diversos métodos trabajadores. Observe que trabajador1 actúa como “método jefe” de trabajador4 y trabajador5. 6.2 Módulos de programas en Java 213
  • 252. 214 Capítulo 6 Métodos: un análisis más detallado 6.3 Métodos static, campos static y la clase Math Toda clase proporciona métodos que realizan tareas comunes en objetos de esa clase. Por ejemplo, para introducir datos mediante el teclado, hemos llamado métodos en un objeto Scanner que se inicializó en su constructor para obtener la entrada del flujo de entrada estándar (System.in). Como aprenderá en el capítulo 14, Archivos y flujos, puede inicializar un objeto Scanner que reciba información del flujo de entrada estándar, y un segundo objeto Scanner que reciba información de un archivo. Cada método de entrada que se llame en el objeto Scan- ner del flujo de entrada estándar obtendría su entrada del teclado, y cada método de entrada que se llame en el objeto Scanner del archivo obtendría su entrada del archivo especificado en el disco. Aunque la mayoría de los métodos se ejecutan en respuesta a las llamadas a métodos en objetos específicos, éste no es siempre el caso. Algunas veces un método realiza una tarea que no depende del contenido de ningún objeto. Dicho método se aplica a la clase en la que está declarado como un todo, y se conoce como método sta- tic o método de clase. Es común que las clases contengan métodos static convenientes para realizar tareas comunes. Por ejemplo, recuerde que en la figura 5.6 utilizamos el método static pow de la clase Math para elevar un valor a una potencia. Para declarar un método como static, coloque la palabra clave static antes del tipo de valor de retorno en la declaración del método. Puede llamar a cualquier método static especificando el nombre de la clase en la que está declarado el método, seguido de un punto (.) y del nombre del método, como sigue: NombreClase.nombreMétodo (argumentos) Aquí utilizaremos varios métodos de la clase Math para presentar el concepto de los métodos static. La clase Math cuenta con una colección de métodos que nos permiten realizar cálculos matemáticos comunes. Por ejemplo, podemos calcular la raíz cuadrada de 900.0 con una llamada al siguiente método static: Math.sqrt( 900.0 ) La expresión anterior se evalúa como 30.0. El método sqrt recibe un argumento de tipo double y devuelve un resultado del mismo tipo. Para imprimir el valor de la llamada anterior al método en una ventana de comandos, podríamos escribir la siguiente instrucción: System.out.println( Math.sqrt( 900.0 ) ); En esta instrucción, el valor que devuelve sqrt se convierte en el argumento para el método println. Observe que no hubo necesidad de crear un objeto Math antes de llamar al método sqrt. Observe también que todos los métodos de la clase Math son static; por lo tanto, cada uno se llama anteponiendo al nombre del método el nombre de la clase Math y el separador punto (.). Observación de ingeniería de software 6.4 La clase Math es parte del paquete java.lang, que el compilador importa de manera implícita, por lo que no es necesario importarla para utilizar sus métodos. Figura 6.1 | Relación jerárquica entre el método jefe y los métodos trabajadores. Jefe trabajador2 trabajador3 trabajador1 trabajador5 trabajador4
  • 253. Los argumentos para los métodos pueden ser constantes, variables o expresiones. Si c = 13.0, d = 3.0 y f = 4.0, entonces la instrucción System.out.println( Math.sqrt( c + d * f ) ); calcula e imprime la raíz cuadrada de 13.0 + 3.0 * 4.0 = 25.0; a saber, 5.0. La figura 6.2 sintetiza varios de los métodos de la clase Math. En la figura 6.2, x y y son de tipo double. Método Descripción Ejemplo abs( x ) valor absoluto de x abs( 23.7 ) es 23.7 abs ( 0.0 ) es 0.0 abs( –23.7 ) es 23.7 ceil( x ) redondea x al entero más pequeño que no sea menor de x ceil( 9.2 ) es 10.0 ceil( -9.8 ) es -9.0 cos( x ) coseno trigonométrico de x (x está en radianes) cos( 0.0 ) es 1.0 exp( x ) método exponencial ex exp( 1.0 ) es 2.71828 exp( 2.0 ) es 7.38906 floor( x ) redondea x al entero más grande que no sea mayor de x floor( 9.2 ) es 9.0 floor( -9.8 ) es -10.0 log( x ) logaritmo natural de x (base e) log( Math.E ) es 1.0 log( Math.E * Math.E ) es 2.0 max( x, y ) el valor más grande de x y y max( 2.3, 12.7 ) es 12.7 max( -2.3, -12.7 ) es –2.3 min( x, y ) el valor más pequeño de x y y min( 2.3, 12.7 ) es 2.3 min( -2.3, -12.7 ) es -12.7 pow( x, y ) x elevado a la potencia y (xy) pow( 2.0, 7.0 ) es 128.0 pow( 9.0, 0.5 ) es 3.0 sin( x ) seno trigonométrico de x (x está en radianes) sin( 0.0 ) es 0.0 sqrt( x ) raíz cuadrada de x sqrt( 900.0 ) es 30.0 tan( x ) tangente trigonométrica de x (x está en radianes) tan( 0.0 ) es 0.0 Figura 6.2 | Métodos de la clase Math. Constantes PI y E de la clase Math La clase Math también declara dos campos que representan unas constantes matemáticas de uso común: Math. PI y Math.E. La constante Math.PI (3.14159265358979323846) es la proporción de la circunferencia de un círculo con su diámetro. La constante Math.E (2.7182818284590452354) es el valor de la base para los logarit- mos naturales (que se calculan con el método static log de la clase Math). Estos campos se declaran en la clase Math con los modificadores public, final y static. Al hacerlos public, otros programadores pueden utilizar estos campos en sus propias clases. Cualquier campo declarado con la palabra clave final es constante; su valor no puede modificarse después de inicializar el campo. Tanto PI como E se declaran como final, ya que sus valores nunca cambian. Al hacer a estos campos static, se puede acceder a ellos mediante el nombre de clase Math y un separador de punto (.), justo igual que los métodos de la clase Math. En la sección 3.5 vimos que cuando cada objeto de una clase mantiene su propia copia de un atributo, el campo que representa a ese atributo se conoce también como variable de instancia: cada objeto de la clase tiene una instancia separada de la variable en memoria. Hay campos para los cuales cada objeto de una clase no tiene una instancia separada de ese campo. Éste es el caso 6.3 Métodos static, campos static y la clase Math 215
  • 254. 216 Capítulo 6 Métodos: un análisis más detallado con los campos static, que se conocen también como variables de clase. Cuando se crean objetos de una clase que contiene campos static, todos los objetos de la clase comparten una copia de los campos static de esa clase. En conjunto, las variables de clase (es decir, las variables static) y las variables de instancia representan a los campos de una clase. En la sección 8.11 aprenderá más acerca de los campos static. ¿Por qué el método main se declara como static? ¿Por qué main debe declararse como static? Cuando se ejecuta la Máquina Virtual de Java (JVM) con el coman- do java, la JVM trata de invocar al método main de la clase que usted le especifica; cuando no se han creado obje- tos de esa clase. Al declarar a main como static, la JVM puede invocar a main sin tener que crear una instancia de la clase. El método main se declara con el siguiente encabezado: public static void main( String args[ ] ) Cuando usted ejecuta su aplicación, especifica el nombre de su clase como un argumento para el comando java, como sigue java NombreClase argumento1 argumento2 … La JVM carga la clase especificada por NombreClase y utiliza el nombre de esa clase para invocar al método main. En el comando anterior, NombreClase es un argumento de línea de comandos para la JVM, que le indica cuál clase debe ejecutar. Después del NombreClase, también puede especificar una lista de objetos String (separados por espacios) como argumentos de línea de comandos, que la JVM pasará a su aplicación. Dichos argumen- tos pueden utilizarse para especificar opciones (por ejemplo, un nombre de archivo) para ejecutar la aplicación. Como aprenderá en el capítulo 7, Arreglos, su aplicación puede acceder a esos argumentos de línea de comandos y utilizarlos para personalizar la aplicación. Comentarios adicionales acerca del método main En capítulos anteriores, todas las aplicaciones tenían una clase que sólo contenía a main, y posiblemente una segunda clase que main utilizaba para crear y manipular objetos. En realidad, cualquier clase puede contener un método main. De hecho, cada uno de nuestros ejemplos con dos clases podría haberse implementado como una sola clase. Por ejemplo, en la aplicación de las figuras 5.9 y 5.10, el método main (líneas 6 a 16 de la figura 5.10) podría haberse tomado así como estaba, y colocarse en la clase LibroCalificaciones (figura 5.9). Des- pués, para ejecutar la aplicación, sólo habría que escribir el comando java LibroCalificaciones en la ventana de comandos; los resultados de la aplicación serían idénticos a los de la versión con dos clases. Puede colocar un método main en cada clase que declare. La JVM invoca sólo al método main en la clase que se utiliza para ejecutar la aplicación. Algunos programadores aprovechan esto para crear un pequeño programa de prueba en cada clase que declaran. 6.4 Declaración de métodos con múltiples parámetros Los capítulos 3 a 5 presentaron clases que contienen métodos simples, que a lo más tenían un parámetro. A menudo, los métodos requieren más de una pieza de información para realizar sus tareas. Ahora le mostraremos cómo escribir métodos con varios parámetros. La aplicación de las figuras 6.3 y 6.4 utiliza el método maximo, declarado por el programador, para determinar y devolver el mayor de tres valores double que introduce el usuario. Cuando la aplicación empieza a ejecutarse, el método main de la clase PruebaBuscadorMaximo (líneas 7 a 11 de la figura 6.4) crea un objeto de la clase BuscadorMaximo (línea 9) y llama al método determinarMaximo del objeto (línea 10) para producir los resul- tados del programa. En la clase BuscadorMaximo (figura 6.3), las líneas 14 a18 del método determinarMaxi- mo piden al usuario que introduzca tres valores double, y después los leen. La línea 21 llama al método maximo (declarado en las líneas 28 a 41) para determinar el mayor de los tres valores double que se pasan como argumen- tos para el método. Cuando el método maximo devuelve el resultado a la línea 21, el programa asigna el valor de retorno de maximo a la variable local resultado. Después, la línea 24 imprime el valor máximo. Al final de esta sección, hablaremos sobre el uso del operador + en la línea 24. Considere la declaración del método maximo (líneas 28 a 41). La línea 28 indica que el método devuelve un valor double, que el nombre del método es maximo y que el método requiere tres parámetros double (x, y y z) para realizar su tarea. Cuando un método tiene más de un parámetro, éstos se especifican como una lista separada
  • 255. 1 // Fig. 6.3: BuscadorMaximo.java 2 // Método maximo, declarado por el programador. 3 import java.util.Scanner; 4 5 public class BuscadorMaximo 6 { 7 // obtiene tres valores de punto flotante y determina el valor máximo 8 public void determinarMaximo() 9 { 10 // crea objeto Scanner para introducir datos desde la ventana de comandos 11 Scanner entrada = new Scanner( System.in ); 12 13 // pide y recibe como entrada tres valores de punto flotante 14 System.out.print( 15 "Escriba tres valores de punto flotante, separados por espacios: " ); 16 double numero1 = entrada.nextDouble(); // lee el primer valor double 17 double numero2 = entrada.nextDouble(); // lee el segundo valor double 18 double numero3 = entrada.nextDouble(); // lee el tercer valor double 19 20 // determina el valor máximo 21 double resultado = maximo( numero1, numero2, numero3 ); 22 23 // muestra el valor máximo 24 System.out.println( "El maximo es: " + resultado ); 25 } // fin del método determinarMaximo 26 27 // devuelve el máximo de sus tres parámetros double 28 public double maximo( double x, double y, double z ) 29 { 30 double valorMaximo = x; // asume que x es el mayor para empezar 31 32 // determina si y es mayor que valorMaximo 33 if ( y > valorMaximo ) 34 valorMaximo = y; 35 36 // determina si z es mayor que valorMaximo 37 if ( z > valorMaximo ) 38 valorMaximo = z; 39 40 return valorMaximo; 41 } // fin del método maximo 42 } // fin de la clase BuscadorMaximo Figura 6.3 | Método maximo, declarado por el programador, que tiene tres parámetros double. 1 // Fig. 6.4: PruebaBuscadorMaximo.java 2 // Aplicación para evaluar la clase BuscadorMaximo. 3 4 public class PruebaBuscadorMaximo 5 { 6 // punto de inicio de la aplicación 7 public static void main( String args[] ) 8 { 9 BuscadorMaximo buscadorMaximo = new BuscadorMaximo(); 10 buscadorMaximo.determinarMaximo(); 11 } // fin de main 12 } // fin de la clase PruebaBuscadorMaximo Figura6.4 | Aplicación para evaluar la clase BuscadorMaximo. (Parte 1 de 2). 6.4 Declaración de métodos con múltiples parámetros 217
  • 256. 218 Capítulo 6 Métodos: un análisis más detallado por comas. Cuando se hace la llamada a maximo en la línea 21, el parámetro x se inicializa con el valor del argu- mento numero1, el parámetro y se inicializa con el valor del argumento numero2 y el parámetro z se inicializa con el valor del argumento numero3. Debe haber un argumento en la llamada al método para cada parámetro (algunas veces conocido como parámetro formal) en la declaración del método. Además, cada argumento debe ser consis- tente con el tipo del parámetro correspondiente. Por ejemplo, un parámetro de tipo double puede recibir valores como 7.35, 22 o –0.03456, pero no objetos String como "hola", ni los valores booleanos true o false. En la sección 6.7 veremos los tipos de argumentos que pueden proporcionarse en la llamada a un método para cada parámetro de un tipo simple. Para determinar el valor máximo, comenzamos con la suposición de que el parámetro x contiene el valor más grande, por lo que la línea 30 declara la variable local valorMaximo y la inicializa con el valor del parámetro x. Des- de luego, es posible que el parámetro y o z contenga el valor más grande, por lo que debemos comparar cada uno de estos valores con valorMaximo. La instrucción if en las líneas 33 y 34 determina si y es mayor que valorMaximo. De ser así, la línea 34 asigna y a valorMaximo. La instrucción if en las líneas 37 y 38 determina si z es mayor que valorMaximo. De ser así, la línea 38 asigna z a valorMaximo. En este punto, el mayor de los tres valores reside en valorMaximo, por lo que la línea 40 devuelve ese valor a la línea 21. Cuando el control del programa regresa al punto en donde se llamó al método maximo, los parámetros x, y y z de maximo ya no están accesibles en la memoria. Observe que los métodos pueden devolver a lo máximo un valor, pero el valor devuelto puede ser una referencia a un objeto que contenga muchos valores. Observe que resultado es una variable local en el método determinarMaximo, ya que se declara en el bloque que representa el cuerpo del método. Las variables deben declararse como campos de una clase sólo si se requiere su uso en más de un método de la clase, o si el programa debe almacenar sus valores entre las llamadas a los métodos de la clase. Error común de programación 6.1 Declarar parámetros del mismo tipo para un método, como float x, y en vez de float x, float y es un error de sintaxis; se requiere un tipo para cada parámetro en la lista de parámetros. Observación de ingeniería de software 6.5 Un método que tiene muchos parámetros puede estar realizando demasiadas tareas. Considere dividir el método en métodos más pequeños que realicen las tareas separadas. Como lineamiento, trate de ajustar el encabezado del método en una línea, si es posible. Implementación del método maximo mediante la reutilización del método Math.max En la figura 6.2 vimos que la clase Math tiene un método max, el cual puede determinar el mayor de dos valores. Todo el cuerpo de nuestro método para encontrar el valor máximo podría también implementarse mediante dos llamadas a Math.max, como se muestra a continuación: return Math.max( x, Math.max( y, z ) ); La primera llamada a Math.max especifica los argumentos x y Math.max( y, z ). Antes de poder llamar a cual- quier método, todos sus argumentos deben evaluarse para determinar sus valores. Si un argumento es una llamada Figura 6.4 | Aplicación para probar la clase BuscadorMaximo. (Parte 2 de 2). Escriba tres valores de punto flotante, separados por espacios: 6.46 4.12 10.54 El maximo es: 10.54 Escriba tres valores de punto flotante, separados por espacios: 9.35 2.74 5.1 El maximo es: 9.35 Escriba tres valores de punto flotante, separados por espacios: 5.8 12.45 8.32 El maximo es: 12.45
  • 257. a un método, es necesario realizar esta llamada para determinar su valor de retorno. Por lo tanto, en la instrucción anterior, primero se evalúa Math.max( y, z ) para determinar el máximo entre y y z. Después el resultado se pasa como el segundo argumento para la otra llamada a Math.max, que devuelve el mayor de sus dos argumentos. Éste es un buen ejemplo de la reutilización de software: buscamos el mayor de los tres valores reutilizando Math. max, el cual busca el mayor de dos valores. Observe lo conciso de este código, en comparación con las líneas 30 a 40 de la figura 6.3. Ensamblado de cadenas mediante la concatenación Java permite crear objetos String mediante el ensamblado de objetos string más pequeños para formar objetos string más grandes, mediante el uso del operador + (o del operador de asignación compuesto +=). A esto se le conoce como concatenación de objetos string. Cuando ambos operandos del operador + son objetos String, el operador + crea un nuevo objeto String en el cual los caracteres del operando derecho se colocan al final de los caracteres en el operando izquierdo. Por ejemplo, la expresión "hola" + "a todos" crea el objeto String "hola a todos". En la línea 24 de la figura 6.3, la expresión "El maximo es: " + resultado utiliza el operador + con ope- randos de tipo String y double. Cada valor primitivo y cada objeto en Java tienen una representación String. Cuando uno de los operandos del operador + es un objeto String, el otro se convierte en String y después se concatenan los dos. En la línea 24, el valor double se convierte en su representación string y se coloca al final del objeto String "El maximo es: ". Si hay ceros a la derecha en un valor double, éstos se descartan cuando el número se convierte en objeto String. Por ende, el número 9.3500 se representa como 9.35 en el objeto String resultante. Los valores primitivos que se utilizan en la concatenación de objetos String se convierten en objetos String. Si un valor boolean se concatena con un objeto String, el valor boolean se convierte en el objeto String "true" o "false". Todos los objetos tienen un método llamado toString que devuelve una representación String del objeto. Cuando se concatena un objeto con un String, se hace una llamada implícita al método toString de ese objeto para obtener la representación String del mismo. En el capítulo 7, Arreglos, aprenderá más acerca del método toString. Cuando se escribe una literal String extensa en el código fuente de un programa, algunas veces los progra- madores prefieren dividir ese objeto String en varios objetos String más pequeños, para colocarlos en varias líneas de código y mejorar la legibilidad. En este caso, los objetos String pueden reensamblarse mediante el uso de la concatenación. En el capítulo 30, Cadenas, caracteres y expresiones regulares, hablaremos sobre los detalles de los objetos String. Error común de programación 6.2 Es un error de sintaxis dividir una literal String en varias líneas en un programa. Si una literal String no cabe en una línea, divídala en objetos String más pequeños y utilice la concatenación para formar la literal String deseada. Error común de programación 6.3 Confundir el operador +, que se utiliza para la concatenación de cadenas, con el operador + que se utiliza para la suma, puede producir resultados extraños. Java evalúa los operandos de un operador de izquierda a derecha. Por ejemplo, si la variable entera y tiene el valor 5, la expresión "y + 2 = " + y + 2 produce la cadena "y + 2 = 52", no "y + 2 = 7", ya que primero el valor de y (5) se concatena con la cadena "y + 2 =" y después el valor 2 se concatena con la nueva cadena "y + 2 = 5" más larga. La expresión "y + 2 =" + (y + 2) produce el resultado deseado "y + 2 = 7". 6.5 Notas acerca de cómo declarar y utilizar los métodos Hay tres formas de llamar a un método: 1. Utilizando el nombre de un método por sí solo para llamar a otro método de la misma clase, como maximo( numero1, numero2, numero3 ) en la línea 21 de la figura 6.3. 6.5 Notas acerca de cómo declarar y utilizar los métodos 219
  • 258. 220 Capítulo 6 Métodos: un análisis más detallado 2. Utilizando una variable que contiene una referencia a un objeto, seguida de un punto (.) y del nombre del método para llamar a un método del objeto al que se hace referencia, como en la línea 10 de la figura 6.4, buscadorMaximo.determinarMaximo(), con lo cual se llama a un método de la clase Buscador- Maximo desde el método main de PruebaBuscadorMaximo. 3. Utilizando el nombre de la clase y un punto (.) para llamar a un método static de una clase, como Math.sqrt( 900.0 ) en la sección 6.3. Observe que un método static sólo puede llamar directamente a otros métodos static de la misma clase (es decir, usando el nombre del método por sí solo) y solamente puede manipular de manera directa campos static en la misma clase. Para acceder a los miembros no static de la clase, un método static debe usar una referencia a un objeto de esa clase. Recuerde que los métodos static se relacionan con una clase como un todo, mientras que los métodos no static se asocian con una instancia específica (objeto) de la clase y pueden mani- pular las variables de instancia de ese objeto. Es posible que existan muchos objetos de una clase al mismo tiempo, cada uno con sus propias copias de las variables de instancia. Suponga que un método static invoca a un método no static en forma directa. ¿Cómo sabría el método qué variables de instancia manipular de cuál objeto? ¿Qué ocurriría si no existieran objetos de la clase en el momento en el que se invocara el método no static? Es evidente que tal situación sería problemática. Por lo tanto, Java no permite que un método static acceda directamente a los miembros no static de la misma clase. Existen tres formas de regresar el control a la instrucción que llama a un método. Si el método no devuelve un resultado, el control regresa cuando el flujo del programa llega a la llave derecha de terminación del método, o cuando se ejecuta la instrucción return; si el método devuelve un resultado, la instrucción return expresión; evalúa la expresión y después devuelve el resultado al método que hizo la llamada. Error común de programación 6.4 Declarar un método fuera del cuerpo de la declaración de una clase, o dentro del cuerpo de otro método es un error de sintaxis. Error común de programación 6.5 Omitir el tipo de valor de retorno en la declaración de un método es un error de sintaxis. Error común de programación 6.6 Colocar un punto y coma después del paréntesis derecho que encierra la lista de parámetros de la declaración de un método es un error de sintaxis. Error común de programación 6.7 Volver a declarar el parámetro de un método como una variable local en el cuerpo de ese método es un error de compilación. Error común de programación 6.8 Olvidar devolver un valor de un método que debe devolver un valor es un error de compilación. Si se especifica un tipo de valor de retorno distinto de void, el método debe contener una instrucción return que devuelva un valor consistente con el tipo de valor de retorno del método. Devolver un valor de un método cuyo tipo de valor de retorno se haya declarado como void es un error de compilación.
  • 259. 6.6 Pila de llamadas a los métodos y registros de activación Para comprender la forma en que Java realiza las llamadas a los métodos, necesitamos considerar primero una estructura de datos (es decir, una colección de elementos de datos relacionados) conocida como pila. Los estu- diantes pueden considerar una pila como una analogía de una pila de platos. Cuando se coloca un plato en la pila, por lo general se coloca en la parte superior (lo que se conoce como meter el plato en la pila). De manera similar, cuando se extrae un plato de la pila, siempre se extrae de la parte superior (lo que se conoce como sacar el plato de la pila). Las pilas se denominan estructuras de datos “último en entrar, primero en salir” (UEPS; LIFO, por las siglas en inglés de last-in, first-out); el último elemento que se mete (inserta) en la pila es el primero que se saca (extrae) de ella. Cuando una aplicación llama a un método, el método llamado debe saber cómo regresar al que lo llamó, por lo que la dirección de retorno del método que hizo la llamada se mete en la pila de ejecución del programa (tam- bién conocida como pila de llamadas a los métodos). Si ocurre una serie de llamadas a métodos, las direcciones de retorno sucesivas se meten en la pila, en el orden “último en entrar, primero en salir”, para que cada método pueda regresar al que lo llamó. La pila de ejecución del programa también contiene la memoria para las variables locales que se utilizan en cada invocación de un método, durante la ejecución de un programa. Estos datos, que se almacenan como una porción de la pila de ejecución del programa, se conocen como el registro de activación o marco de pila de la llamada a un método. Cuando se hace la llamada a un método, el registro de activación para la llamada se mete en la pila de ejecución del programa. Cuando el método regresa al que lo llamó, el registro de activación para esa llamada al método se saca de la pila y esas variables locales ya no son conocidas para el programa. Si una variable local que contiene una referencia a un objeto es la única variable en el programa con una referencia a ese objeto, cuando se saca de la pila el registro de activación que contiene a esa variable local, el programa ya no puede acce- der a ese objeto, y la JVM lo eliminará de la memoria en algún momento dado, durante la “recolección de basura”. En la sección 8.10 hablaremos sobre la recolección de basura. Desde luego que la cantidad de memoria en una computadora es finita, por lo que sólo puede utilizarse cierta cantidad de memoria para almacenar los registros de activación en la pila de ejecución del programa. Si ocurren más llamadas a métodos de las que se puedan almacenar sus registros de activación en la pila de ejecución del programa, se produce un error conocido como desbordamiento de pila. 6.7 Promoción y conversión de argumentos Otra característica importante de las llamadas a los métodos es la promoción de argumentos: convertir el valor de un argumento al tipo que el método espera recibir en su correspondiente parámetro. Por ejemplo, una aplica- ción puede llamar al método sqrt de Math con un argumento entero, aun cuando el método espera recibir un argumento double (pero no viceversa, como pronto veremos). La instrucción System.out.println( Math.sqrt( 4 ) ); evalúa Math.sqrt( 4 ) correctamente e imprime el valor 2.0. La lista de parámetros de la declaración del méto- do hace que Java convierta el valor int 4 en el valor double 4.0 antes de pasar ese valor a sqrt. Tratar de realizar estas conversiones puede ocasionar errores de compilación, si no se satisfacen las reglas de promoción de Java. Las reglas de promoción especifican qué conversiones son permitidas; esto es, qué conversiones pueden realizarse sin perder datos. En el ejemplo anterior de sqrt, un int se convierte en double sin modificar su valor. No obs- tante, la conversión de un double a un int trunca la parte fraccionaria del valor double; por consecuencia, se pierde parte del valor. La conversión de tipos de enteros largos a tipos de enteros pequeños (por ejemplo, de long a int) puede también producir valores modificados. Las reglas de promoción se aplican a las expresiones que contienen valores de dos o más tipos simples, y a los valores de tipos simples que se pasan como argumentos para los métodos. Cada valor se promueve al tipo “más alto” en la expresión. (En realidad, la expresión utiliza una copia temporal de cada valor; los tipos de los valores originales permanecen sin cambios). La figura 6.5 lista los tipos primitivos y los tipos a los cuales se puede promo- ver cada uno de ellos. Observe que las promociones válidas para un tipo dado siempre se realizan a un tipo más alto en la tabla. Por ejemplo, un int puede promoverse a los tipos más altos long, float y double. Al convertir valores a tipos inferiores en la tabla de la figura 6.5, se producirán distintos valores si el tipo inferior no puede representar el valor del tipo superior (por ejemplo, el valor int 2000000 no puede represen- tarse como un short, y cualquier número de punto flotante con dígitos después de su punto decimal no pueden 6.7 Promoción y conversión de argumentos 221
  • 260. 222 Capítulo 6 Métodos: un análisis más detallado representarse en un tipo entero como long, int o short). Por lo tanto, en casos en los que la información puede perderse debido a la conversión, el compilador de Java requiere que utilicemos un operador de conversión (el cual presentamos en la sección 4.9) para forzar explícitamente la conversión; en caso contrario, ocurre un error de compilación. Eso nos permite “tomar el control” del compilador. En esencia decimos, “Sé que esta conver- sión podría ocasionar pérdida de información, pero para mis fines aquí, eso está bien”. Suponga que el método cuadrado calcula el cuadrado de un entero y por ende requiere un argumento int. Para llamar a cuadrado con un argumento double llamado valorDouble, tendríamos que escribir la llamada al método de la siguiente forma: cuadrado( (int) valorDouble ) La llamada a este método convierte explícitamente el valor de valorDouble a un entero, para usarlo en el método cuadrado. Por ende, si el valor de valorDouble es 4.5, el método recibe el valor 4 y devuelve 16, no 20.25. Tipo Promociones válidas double Ninguna float double long float o double int long, float o double char int, long, float o double short int, long, float o double (pero no char) byte short, int, long, float o double (pero no char) boolean Ninguna (los valores boolean no se consideran números en Java) Figura 6.5 | Promociones permitidas para los tipos primitivos. Error común de programación 6.9 Convertir un valor de tipo primitivo a otro tipo primitivo puede modificar ese valor, si el nuevo tipo no es una promoción válida. Por ejemplo, convertir un valor de punto flotante a un valor entero puede introducir errores de truncamiento (pérdida de la parte fraccionaria) en el resultado. 6.8 Paquetes de la API de Java Como hemos visto, Java contiene muchas clases predefinidas que se agrupan en categorías de clases relacionadas, llamadas paquetes. En conjunto, nos referimos a estos paquetes como la Interfaz de programación de aplicaciones de Java (API de Java), o biblioteca de clases de Java. A lo largo del texto, las declaraciones import especifican las clases requeridas para compilar un programa en Java. Por ejemplo, un programa incluye la declaración import java.util.Scanner; para especificar que el programa utiliza la clase Scanner del paquete java.util. Esto permite a los programa- dores utilizar el nombre de la clase Scanner, en vez de tener que usar el nombre completo calificado de la clase, java.util.Scanner, en el código. Uno de los puntos más fuertes de Java es el extenso número de clases en los paquetes de la API de Java. Algunos paquetes clave se describen en la figura 6.6, que representa sólo una pequeña parte de los componentes reutilizables en la API de Java. Mientras esté aprendiendo este lenguaje, invier- ta una parte de su tiempo explorando las descripciones de los paquetes y las clases en la documentación para la API de Java (java.sun.com/javase/6/docs/api/). El conjunto de paquetes disponibles en Java SE 6 es bastante extenso. Además de los paquetes sintetizados en la figura 6.6, Java SE 6 incluye paquetes para gráficos complejos, interfaces gráficas de usuario avanzadas, impre-
  • 261. Paquete Descripción java.applet El Paquete Applet de Java contiene una clase y varias interfaces requeridas para crear applets de Java; programas que se ejecutan en los navegadores Web. (En el capítulo 20, Introducción a las applets de Java, hablaremos sobre las applets; en el capítulo 10, Programación orientada a objetos: polimorfismo, hablaremos sobre las interfaces). java.awt El Paquete Abstract Window Toolkit de Java contiene las clases e interfaces requeridas para crear y manipular GUIs en Java 1.0 y 1.1. En las versiones actuales de Java, se utilizan con frecuencia los componentes de la GUI de Swing, incluidos en los paquetes javax.swing. (Algunos elementos del paquete java.awt se describen en el capítulo 11, Componentes de la GUI: parte 1, en el capítulo 12, Gráficos y Java 2D™, y en el capítulo 22, Componentes de la GUI: parte 2). java.awt.event El Paquete Abstract Window Toolkit Event de Java contiene clases e interfaces que habili- tan el manejo de eventos para componentes de la GUI en los paquetes java.awt y javax. swing. (Aprenderá más acerca de este paquete en el capítulo 11, Componentes de la GUI: parte 1, y en el capítulo 22, Componentes de la GUI: parte 2). java.io El Paquete de Entrada/Salida de Java contiene clases e interfaces que permiten a los progra- mas recibir datos de entrada y mostrar datos de salida. (Aprenderá más acerca de este paquete en el capítulo 14, Archivos y flujos). java.lang El Paquete del Lenguaje Java contiene clases e interfaces (descritas a lo largo de este texto) requeridas por muchos programas de Java. Este paquete es importado por el compilador en todos los programas, por lo que usted no necesita hacerlo. java.net El Paquete de Red de Java contiene clases e interfaces que permiten a los programas comu- nicarse mediante redes de computadoras, como Internet. (Aprenderá más acerca de esto en el capítulo 24, Redes). java.text El Paquete de Texto de Java contiene clases e interfaces que permiten a los programas manipular números, fechas, caracteres y cadenas. El paquete proporciona herramientas de internacionalización que permiten la personalización de un programa con respecto a una configuración regional específica (por ejemplo, un programa puede mostrar cadenas en dis- tintos lenguajes, con base en el país del usuario). java.util El Paquete de Utilerías de Java contiene clases e interfaces utilitarias, que permiten acciones como manipulaciones de fecha y hora, procesamiento de números aleatorios (clase Random), almacenar y procesar grandes cantidades de datos y descomponer cadenas en piezas más pequeñas llamadas tokens (clase StringTokenizer). (Aprenderá más acerca de las caracte- rísticas de este paquete en el capítulo 19, Colecciones). javax.swing El Paquete de Componentes GUI Swing de Java contiene clases e interfaces para los com- ponentes de la GUI Swing de Java, los cuales ofrecen soporte para GUIs portables. (Apren- derá más acerca de este paquete en el capítulo 11, Componentes de la GUI: parte 1, y en el capítulo 22, Componentes de la GUI: parte 2). javax.swing.event El Paquete Swing Event de Java contiene clases e interfaces que permiten el manejo de eventos (por ejemplo, responder a los clics del ratón) para los componentes de la GUI en el paquete javax.swing. (Aprenderá más acerca de este paquete en el capítulo 11, Compo- nentes de la GUI: parte 1, y en el capítulo 22, Componentes de la GUI: parte 2). Figura 6.6 | Paquetes de la API de Java (un subconjunto). sión, redes avanzadas, seguridad, procesamiento de bases de datos, multimedia, accesibilidad (para personas con discapacidades) y muchas otras funciones. Para una visión general de los paquetes en Java SE 6, visite: java.sun.com/javase/6/docs/api/overview-summary.html Además, muchos otros paquetes están disponibles para descargarse en java.sun.com. 6.8 Paquetes de la API de Java 223
  • 262. 224 Capítulo 6 Métodos: un análisis más detallado Puede localizar información adicional acerca de los métodos de una clase predefinida de Java en la docu- mentación para la API de Java, en java.sun.com/javase/6/docs/api/. Cuando visite este sitio, haga clic en el vínculo Index para ver un listado en orden alfabético de todas las clases y los métodos en la API de Java. Localice el nombre de la clase y haga clic en su vínculo para ver la descripción en línea de la clase. Haga clic en el vínculo METHOD para ver una tabla de los métodos de la clase. Cada método static se enlistará con la palabra "static" antes del tipo de valor de retorno del método. Para una descripción más detallada acerca de cómo navegar por la documentación para la API de Java, consulte el apéndice J, Uso de la documentación para la API de Java. Buena práctica de programación 6.2 Es fácil buscar información en la documentación en línea de la API de Java; además proporciona los detalles acerca de cada clase. Al estudiar una clase en este libro, es conveniente que tenga el hábito de buscar la clase en la documen- tación en línea, para obtener información adicional. 6.9 Ejemplo práctico: generación de números aleatorios Ahora analizaremos de manera breve una parte divertida de un tipo popular de aplicaciones de la programación: simulación y juegos. En ésta y en la siguiente sección desarrollaremos un programa de juego bien estructurado con varios métodos. El programa utiliza la mayoría de las instrucciones de control presentadas hasta este punto en el libro, e introduce varios conceptos de programación nuevos. Hay algo en el ambiente de un casino de apuestas que anima a las personas: desde las elegantes mesas de caoba y fieltro para tirar dados, hasta las máquinas tragamonedas. Es el elemento de azar, la posibilidad de que la suerte convierta un bolsillo lleno de dinero en una montaña de riquezas. El elemento de azar puede introducirse en un programa mediante un objeto de la clase Random (paquete java.util), o mediante el método static llamado random, de la clase Math. Los objetos de la clase Random pueden producir valores aleatorios de tipo boolean, byte, float, double, int, long y gaussianos, mientras que el método random de la clase Math puede producir sólo valores de tipo double en el rango 0.0 ≤ x < 1.0, donde x es el valor regresado por el método ran- dom. En los siguientes ejemplos, usamos objetos de tipo Random para producir valores aleatorios. Se puede crear un nuevo objeto generador de números aleatorios de la siguiente manera: Random numerosAleatorios = new Random(); Después, el objeto generador de números aleatorios puede usarse para generar valores boolean, byte, float, double, int, long y gaussianos; aquí sólo hablaremos sobre los valores int aleatorios. Para obtener más infor- mación sobre la clase Random, vaya a java.sun.com/javase/6/docs/api/java/util/Random.html. Considere la siguiente instrucción: int valorAleatorio = numerosAleatorios.nextInt(); El método nextInt de la clase Random genera un valor int aleatorio en el rango de –2,147,483,648 a +2,147,483,647. Si el método nextInt verdaderamente produce valores aleatorios, entonces cualquier valor en ese rango debería tener una oportunidad (o probabilidad) igual de ser elegido cada vez que se llame al método nextInt. Los valores devueltos por nextInt son en realidad números seudoaleatorios (una secuencia de valores producidos por un cálculo matemático complejo). Ese cálculo utiliza la hora actual del día (que, desde luego, cambia constantemente) para sembrar el generador de números aleatorios, de tal forma que cada ejecución de un programa produzca una secuencia distinta de valores aleatorios. El rango de valores producidos directamente por el método nextInt es a menudo distinto del rango de valores requeridos en una aplicación de Java particular. Por ejemplo, un programa que simula el lanzamiento de una moneda sólo requiere 0 para “águila” y 1 para “sol”. Un programa para simular el tiro de un dado de seis lados requeriría enteros aleatorios en el rango de 1 a 6. Un programa que adivine en forma aleatoria el siguiente tipo de nave espacial (de cuatro posibilidades distintas) que volará a lo largo del horizonte en un videojuego reque- riría números aleatorios en el rango de 1 a 4. Para casos como éstos, la clase Random cuenta con otra versión del método nextInt, que recibe un argumento int y devuelve un valor desde 0 hasta (pero sin incluir) el valor del argumento. Por ejemplo, para simular el lanzamiento de monedas, podría utilizar la instrucción int valorAleatorio = numerosAleatorios.nextInt( 2 ); que devuelve 0 o 1.
  • 263. Tirar un dado de seis lados Para demostrar los números aleatorios, desarrollaremos un programa que simula 20 tiros de un dado de seis lados, y que muestra el valor de cada tiro. Para empezar, usaremos nextInt para producir valores aleatorios en el rango de 0 a 5, como se muestra a continuación: cara = numerosAleatorios.nextInt( 6 ); El argumento 6 (que se conoce como el factor de escala) representa el número de valores únicos que nextInt debe producir (en este caso, seis: 0, 1, 2, 3, 4 y 5). A esta manipulación se le conoce como escalar el rango de valores producidos por el método nextInt de Random. Un dado de seis lados tiene los números del 1 al 6 en sus caras, no del 0 al 5. Por lo tanto, desplazamos el rango de números producidos sumando un valor de desplazamiento (en este caso, 1) a nuestro resultado ante- rior, como en cara = 1 + numerosAleatorios.nextInt( 6 ); El valor de desplazamiento (1) especifica el primer valor en el conjunto deseado de enteros aleatorios. La instruc- ción anterior asigna a cara un entero aleatorio en el rango de 1 a 6. La figura 6.7 muestra dos resultados de ejemplo, los cuales confirman que los resultados del cálculo anterior son enteros en el rango de 1 a 6, y que cada ejecución del programa puede producir una secuencia distinta de números aleatorios. La línea 3 importa la clase Random del paquete java.util. La línea 9 crea el objeto nume- rosAleatorios de la clase Random para producir valores aleatorios. La línea 16 se ejecuta 20 veces en un ciclo para tirar el dado. La instrucción if (líneas 21 y 22) en el ciclo empieza una nueva línea de salida después de cada cinco números. 1 // Fig. 6.7: EnterosAleatorios.java 2 // Enteros aleatorios desplazados y escalados. 3 import java.util.Random; // el programa usa la clase Random 4 5 public class EnterosAleatorios 6 { 7 public static void main( String args[] ) 8 { 9 Random numerosAleatorios = new Random(); // generador de números aleatorios 10 int cara; // almacena cada entero aleatorio generado 11 12 // itera 20 veces 13 for ( int contador = 1; contador <= 20; contador++ ) 14 { 15 // elige entero aleatorio del 1 al 6 16 cara = 1 + numerosAleatorios.nextInt( 6 ); 17 18 System.out.printf( "%d ", cara ); // muestra el valor generado 19 20 // si contador es divisible entre 5, empieza una nueva línea de salida 21 if ( contador % 5 == 0 ) 22 System.out.println(); 23 } // fin de for 24 } // fin de main 25 } // fin de la clase EnterosAleatorios Figura 6.7 | Enteros aleatorios desplazados y escalados. (Parte 1 de 2). 1 5 3 6 2 5 2 6 5 2 4 4 4 2 6 3 1 6 2 2 6.9 Ejemplo práctico: generación de números aleatorios 225
  • 264. 226 Capítulo 6 Métodos: un análisis más detallado Tirar un dado de seis lados 6000 veces Para mostrar que los números que produce nextInt ocurren con una probabilidad aproximadamente igual, simularemos 6000 tiros de un dado con la aplicación de la figura 6.8. Cada entero de 1 a 6 debe aparecer aproxi- madamente 1000 veces. Como se muestra en los dos bloques de resultados, al escalar y desplazar los valores producidos por el método nextInt, el programa puede simular de manera realista el tiro de un dado de seis lados. La aplicación utiliza ins- trucciones de control anidadas (la instrucción switch está anidada dentro del for) para determinar el número de ocurrencias de cada lado del dado. La instrucción for (líneas 21 a 47) itera 6000 veces. Durante cada iteración, la línea 23 produce un valor aleatorio del 1 al 6. Después, ese valor se utiliza como la expresión de control (línea 26) de la instrucción switch (líneas 26 a 46). Con base en el valor de cara, la instrucción switch incrementa una de las seis variables contadores durante cada iteración del ciclo. Cuando veamos los arreglos en el capítulo 7, ¡le mostraremos una forma elegante de reemplazar toda la instrucción switch en este programa con una sola ins- trucción! Observe que la instrucción switch no tiene un caso default, ya que hemos creado una etiqueta case para todos los posibles valores que puede producir la expresión en la línea 23. Ejecute el programa varias veces, y observe los resultados. Como verá, cada vez que ejecute el programa, producirá distintos resultados. 1 // Fig. 6.8: TirarDado.java 2 // Tirar un dado de seis lados 6000 veces. 3 import java.util.Random; 4 5 public class TirarDado 6 { 7 public static void main( String args[] ) 8 { 9 Random numerosAleatorios = new Random(); // generador de números aleatorios 10 11 int frecuencia1 = 0; // cuenta de veces que se tiró 1 12 int frecuencia2 = 0; // cuenta de veces que se tiró 2 13 int frecuencia3 = 0; // cuenta de veces que se tiró 3 14 int frecuencia4 = 0; // cuenta de veces que se tiró 4 15 int frecuencia5 = 0; // cuenta de veces que se tiró 5 16 int frecuencia6 = 0; // cuenta de veces que se tiró 6 17 18 int cara; // almacena el valor que se tiró más recientemente 19 20 // sintetiza los resultados de tirar un dado 6000 veces 21 for ( int tiro = 1; tiro <= 6000; tiro++ ) 22 { 23 cara = 1 + numerosAleatorios.nextInt( 6 ); // número del 1 al 6 24 25 // determina el valor del tiro de 1 a 6 e incrementa el contador apropiado 26 switch ( cara ) 27 { 28 case 1: 29 ++frecuencia1; // incrementa el contador de 1s 30 break; Figura 6.8 | Tirar un dado de seis lados 6000 veces. (Parte 1 de 2). Figura 6.7 | Enteros aleatorios desplazados y escalados. (Parte 2 de 2). 6 5 4 2 6 1 2 5 1 3 6 3 2 2 1 6 4 2 6 4
  • 265. 31 case 2: 32 ++frecuencia2; // incrementa el contador de 2s 33 break; 34 case 3: 35 ++frecuencia3; // incrementa el contador de 3s 36 break; 37 case 4: 38 ++frecuencia4; // incrementa el contador de 4s 39 break; 40 case 5: 41 ++frecuencia5; // incrementa el contador de 5s 42 break; 43 case 6: 44 ++frecuencia6; // incrementa el contador de 6s 45 break; // opcional al final del switch 46 } // fin de switch 47 } // fin de for 48 49 System.out.println( "CaratFrequencia" ); // encabezados de salida 50 System.out.printf( "1t%dn2t%dn3t%dn4t%dn5t%dn6t%dn", 51 frecuencia1, frecuencia2, frecuencia3, frecuencia4, 52 frecuencia5, frecuencia6 ); 53 } // fin de main 54 } // fin de la clase TirarDado Figura 6.8 | Tirar un dado de seis lados 6000 veces. (Parte 2 de 2). Cara Frecuencia 1 1029 2 994 3 1017 4 1007 5 972 6 981 Cara Frecuencia 1 982 2 1001 3 1015 4 1005 5 1009 6 988 6.9.1 Escalamiento y desplazamiento generalizados de números aleatorios Anteriormente demostramos la instrucción cara = 1 + numerosAleatorios.nextInt( 6 ); la cual simula el tiro de un dado de seis caras. Esta instrucción siempre asigna a la variable cara un entero en el rango 1 ≤ cara ≤ 6. La amplitud de este rango (es decir, el número de enteros consecutivos en el rango) es 6, y el número inicial en el rango es 1. Si hacemos referencia a la instrucción anterior, podemos ver que la amplitud del rango se determina en base al número 6 que se pasa como argumento para el método nextInt de Random, y que el número inicial del rango es el número 1 que se suma a numerosAleatorios.nextInt( 6 ). Podemos generalizar este resultado de la siguiente manera: numero = valorDesplazamiento + numerosAleatorios.nextInt( factorEscala ); en donde valorDesplazamiento especifica el primer número en el rango deseado de enteros consecutivos y factorEs- cala especifica cuántos números hay en el rango. 6.9 Ejemplo práctico: generación de números aleatorios 227
  • 266. 228 Capítulo 6 Métodos: un análisis más detallado También es posible elegir enteros al azar, a partir de conjuntos de valores distintos a los rangos de enteros consecutivos. Por ejemplo, para obtener un valor aleatorio de la secuencia 2, 5, 8, 11 y 14, podríamos utilizar la siguiente instrucción: numero = 2 + 3 * numerosAleatorios.nextInt( 5 ); En este caso, numerosAleatorios.nextInt( 5 ) produce valores en el rango de 0 a 4. Cada valor producido se multiplica por 3 para producir un número en la secuencia 0, 3, 6, 9 y 12. Después sumamos 2 a ese valor para desplazar el rango de valores y obtener un valor de la secuencia 2, 5, 8, 11 y 14. Podemos generalizar este resultado así: numero = valorDesplazamiento + diferenciaEntreValores * numerosAleatorios.nextInt( factorEscala ); en donde valorDesplazamiento especifica el primer número en el rango deseado de valores, diferenciaEntreValores representa la diferencia entre números consecutivos en la secuencia y factorEscala especifica cuántos números hay en el rango. 6.9.2 Repetitividad de números aleatorios para prueba y depuración Como mencionamos en la sección 6.9, los métodos de la clase Random en realidad generan números seudoalea- torios con base en cálculos matemáticos complejos. Si se llama repetidas veces a cualquiera de los métodos de Random, se produce una secuencia de números que parecen ser aleatorios. El cálculo que producen los números seudoaleatorios utiliza la hora del día como valor de semilla para cambiar el punto inicial de la secuencia. Cada nuevo objeto Random se siembra a sí mismo con un valor basado en el reloj del sistema computacional al momen- to en que se crea el objeto, con lo cual se permite que cada ejecución de un programa produzca una secuencia distinta de números aleatorios. Al depurar una aplicación, algunas veces es útil repetir la misma secuencia exacta de números seudoaleatorios durante cada ejecución del programa. Esta repetitividad nos permite probar que la aplicación esté funcionando para una secuencia específica de números aleatorios, antes de evaluar el programa con distintas secuencias de números aleatorios. Cuando la repetitividad es importante, podemos crear un objeto Random de la siguiente manera: Random numerosAleatorios = new Random( valorSemilla ); El argumento valorSemilla (de tipo long) siembra el cálculo del número aleatorio. Si se utiliza siempre el mismo valor para valorSemilla, el objeto Random produce la misma secuencia de números aleatorios. Para establecer la semilla de un objeto Random en cualquier momento durante la ejecución de un programa, podemos llamar al método setSeed del objeto, como en numerosAleatorios.setSeed( valorSemilla ); Tip de prevención de errores 6.2 Mientras un programa esté en desarrollo, cree el objeto Random con un valor de semilla específico para producir una secuencia repetible de números aleatorios cada vez que se ejecute el programa. Si se produce un error lógico, corrija el error y evalúe el programa otra vez con el mismo valor de semilla; esto le permitirá reconstruir la misma secuencia de números aleatorios que produjeron el error. Una vez que se hayan eliminado los errores lógicos, cree el objeto Random sin utilizar un valor de semilla, para que el objeto Random genere una nueva secuencia de números aleatorios cada vez que se ejecute el programa. 6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones) Un juego de azar popular es el juego de dados conocido como “craps”, el cual se juega en casinos y callejones por todo el mundo. Las reglas del juego son simples: Un jugador tira dos dados. Cada dado tiene seis caras, las cuales contienen uno, dos, tres cuatro, cinco y seis puntos negros, respectivamente. Una vez que los dados dejan de moverse, se calcula la suma de los
  • 267. puntos negros en las dos caras superiores. Si la suma es 7 u 11 en el primer tiro, el jugador gana. Si la suma es 2, 3 o 12 en el primer tiro (llamado “craps”), el jugador pierde (es decir, la “casa” gana). Si la suma es 4, 5, 6, 8, 9 o 10 en el primer tiro, esta suma se convierte en el “punto” del jugador. Para ganar, el jugador debe seguir tirando los dados hasta que salga otra vez “su punto” (es decir, que tire ese mismo valor de punto). El jugador pierde si tira un 7 antes de llegar a su punto. La aplicación en las figuras 6.9 y 6.10 simula el juego de craps, utilizando varios métodos para definir la lógica del juego. En el método main de la clase PruebaCraps (figura 6.10), la línea 8 crea un objeto de la clase Craps (figura 6.9) y la línea 9 llama a su método jugar para iniciar el juego. El método jugar (figura 6.9, líneas 21 a 65) llama al método tirarDado (figura 6.9, líneas 68 a 81) según sea necesario para tirar los dos dados y calcular su suma. Los cuatro resultados de ejemplo en la figura 6.10 muestran que se ganó en el primer tiro, se perdió en el primer tiro, se ganó en un tiro subsiguiente y se perdió en un tiro subsiguiente, en forma respectiva. 1 // Fig. 6.9: Craps.java 2 // La clase Craps simula el juego de dados "craps". 3 import java.util.Random; 4 5 public class Craps 6 { 7 // crea un generador de números aleatorios para usarlo en el método tirarDado 8 private Random numerosAleatorios = new Random(); 9 10 // enumeración con constantes que representan el estado del juego 11 private enum Estado { CONTINUA, GANO, PERDIO }; 12 13 // constantes que representan tiros comunes del dado 14 private final static int DOS_UNOS = 2; 15 private final static int TRES = 3; 16 private final static int SIETE = 7; 17 private final static int ONCE = 11; 18 private final static int DOCE = 12; 19 20 // ejecuta un juego de craps 21 public void jugar() 22 { 23 int miPunto = 0; // punto si no gana o pierde en el primer tiro 24 Estado estadoJuego; // puede contener CONTINUA, GANO o PERDIO 25 26 int sumaDeDados = tirarDados(); // primer tiro de los dados 27 28 // determina el estado del juego y el punto con base en el primer tiro 29 switch ( sumaDeDados ) 30 { 31 case SIETE: // gana con 7 en el primer tiro 32 case ONCE: // gana con 11 en el primer tiro 33 estadoJuego = Estado.GANO; 34 break; 35 case DOS_UNOS: // pierde con 2 en el primer tiro 36 case TRES: // pierde con 3 en el primer tiro 37 case DOCE: // pierde con 12 en el primer tiro 38 estadoJuego = Estado.PERDIO; 39 break; 40 default: // no ganó ni perdió, por lo que guarda el punto 41 estadoJuego = Estado.CONTINUA; // no ha terminado el juego 42 miPunto = sumaDeDados; // guarda el punto 43 System.out.printf( "El punto es %dn", miPunto ); Figura 6.9 | La clase Craps simula el juego de dados “craps”. (Parte 1 de 2). 6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones) 229
  • 268. 230 Capítulo 6 Métodos: un análisis más detallado Hablaremos sobre la declaración de la clase Craps en la figura 6.9. En las reglas del juego, el jugador debe tirar dos dados en el primer tiro y debe hacer lo mismo en todos los tiros subsiguientes. Declaramos el método tirarDados (líneas 68 a 81) para tirar el dado y calcular e imprimir su suma. El método tirarDados se declara una vez, pero se llama desde dos lugares (líneas 26 y 50) en el método jugar, el cual contiene la lógica para un juego completo de craps. El método tirarDados no tiene argumentos, por lo cual su lista de parámetros está vacía. Cada vez que se llama, tirarDados devuelve la suma de los dados, por lo que se indica el tipo de valor de retorno int en el encabezado del método (línea 68). Aunque las líneas 71 y 72 se ven iguales (excepto por el nombre de los dados), no necesariamente producen el mismo resultado. Cada una de estas instrucciones produce un valor aleatorio en el rango de 1 a 6. Observe que numerosAleatorios (se utiliza en las líneas 71 y 72) no se declara en el método, sino que se declara como una variable de instancia private de la clase y se inicializa en la línea 8. Esto nos permite crear un objeto Random que se reutiliza en cada llamada a tirarDados. El juego es razonablemente complejo. El jugador puede ganar o perder en el primer tiro, o puede ganar o perder en cualquier tiro subsiguiente. El método jugar (líneas 21 a 65) utiliza a la variable local miPunto (línea 23) para almacenar el “punto” si el jugador no gana o pierde en el primer tiro, a la variable local estadoJuego (línea 24) para llevar el registro del estado del juego en general y a la variable local sumaDeDados (línea 26) para 44 break; // opcional al final del switch 45 } // fin de switch 46 47 // mientras el juego no esté terminado 48 while ( estadoJuego == Estado.CONTINUA ) // no GANO ni PERDIO 49 { 50 sumaDeDados = tirarDados(); // tira los dados de nuevo 51 52 // determina el estado del juego 53 if ( sumaDeDados == miPunto ) // gana haciendo un punto 54 estadoJuego = Estado.GANO; 55 else 56 if ( sumaDeDados == SIETE ) // pierde al tirar 7 antes del punto 57 estadoJuego = Estado.PERDIO; 58 } // fin de while 59 60 // muestra mensaje de que ganó o perdió 61 if ( estadoJuego == Estado.GANO ) 62 System.out.println( "El jugador gana" ); 63 else 64 System.out.println( “El jugador pierde" ); 65 } // fin del método jugar 66 67 // tira los dados, calcula la suma y muestra los resultados 68 public int tirarDados() 69 { 70 // elige valores aleatorios para los dados 71 int dado1 = 1 + numerosAleatorios.nextInt( 6 ); // primer tiro del dado 72 int dado2 = 1 + numerosAleatorios.nextInt( 6 ); // segundo tiro del dado 73 74 int suma = dado1 + dado2; // suma de los valores de los dados 75 76 // muestra los resultados de este tiro 77 System.out.printf( "El jugador tiro %d + %d = %dn", 78 dado1, dado2, suma ); 79 80 return suma; // devuelve la suma de los dados 81 } // fin del método tirarDados 82 } // fin de la clase Craps Figura 6.9 | La clase Craps simula el juego de dados “craps”. (Parte 2 de 2).
  • 269. 1 // Fig. 6.10: PruebaCraps.java 2 // Aplicación para probar la clase Craps. 3 4 public class PruebaCraps 5 { 6 public static void main( String args[] ) 7 { 8 Craps juego = new Craps(); 9 juego.jugar(); // juega un juego de craps 10 } // fin de main 11 } // fin de la clase PruebaCraps El jugador tiro 5 + 6 = 11 El jugador gana Figura 6.10 | Aplicación para probar la clase Craps. El jugador tiro 2 + 6 = 8 El punto es 8 El jugador tiro 5 + 1 = 6 El jugador tiro 2 + 1 = 3 El jugador tiro 1 + 6 = 7 El jugador pierde El jugador tiro 1 + 2 = 3 El jugador pierde El jugador tiro 5 + 4 = 9 El punto es 9 El jugador tiro 2 + 2 = 4 El jugador tiro 2 + 6 = 8 El jugador tiro 4 + 2 = 6 El jugador tiro 3 + 6 = 9 El jugador gana almacenar la suma de los dados para el tiro más reciente. Observe que miPunto se inicializa con 0 para asegurar que la aplicación se compile. Si no inicializa miPunto, el compilador genera un error ya que miPunto no reci- be un valor en todas las etiquetas case de la instrucción switch y, en consecuencia, el programa podría tratar de utilizar miPunto antes de que se le asigne un valor. En contraste, estadoJuego no requiere inicialización, ya que se le asigna un valor en cada etiqueta case de la instrucción switch; por lo tanto, se garantiza que se inicialice antes de usarse. Observe que la variable local estadoJuego (línea 24) se declara como de un nuevo tipo llamado Estado, el cual declaramos en la línea 11. El tipo Estado se declara como un miembro private de la clase Craps, ya que sólo se utiliza en esa clase. Estado es un tipo declarado por el programador, denominado enumeración, que en su forma más simple declara un conjunto de constantes representadas por identificadores. Una enumeración es un tipo especial de clase, que se introduce mediante la palabra clave enum y un nombre para el tipo (en este caso, Estado). Al igual que con una clase, las llaves ({ y }) delimitan el cuerpo de una declaración de enum. Dentro de las llaves hay una lista, separada por comas, de constantes de enumeración, cada una de las cuales representa un valor único. Los identificadores en una enum deben ser únicos (en el capítulo 8 aprenderá más acerca de las enumeraciones). Buena práctica de programación 6.3 Use sólo letras mayúsculas en los nombres de las constantes de enumeración. Esto hace que resalten y le recuerdan que las constantes de enumeración no son variables. 6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones) 231
  • 270. 232 Capítulo 6 Métodos: un análisis más detallado A las variables de tipo Estado se les debe asignar sólo una de las tres constantes declaradas en la enumeración (línea 11), o se producirá un error de compilación. Cuando el jugador gana el juego, el programa asigna a la variable local estadoJuego el valor Estado.GANO (líneas 33 y 54). Cuando el jugador pierde el juego, la aplica- ción asigna a la variable local estadoJuego el valor Estado.PERDIO (líneas 38 y 57). En cualquier otro caso, el programa asigna a la variable local estadoJuego el valor Estado.CONTINUA (línea 41) para indicar que el juego no ha terminado y hay que tirar los dados otra vez. Buena práctica de programación 6.4 El uso de constantes de enumeración (como Estado.GANO, Estado.PERDIO y Estado.CONTINUA) en vez de valores enteros literales (como 0, 1 y 2) puede hacer que los programas sean más fáciles de leer y de mantener. La línea 26 en el método jugar llama a tirarDados, el cual elige dos valores aleatorios del 1 al 6, muestra el valor del primer dado, el del segundo y la suma de los dos dados, y devuelve esa suma. Después el método jugar entra a la instrucción switch en las líneas 29 a 45, que utiliza el valor de sumaDeDados de la línea 26 para determinar si el jugador ganó o perdió el juego, o si debe continuar con otro tiro. Las sumas de los dados que ocasionan que se gane o pierda el juego en el primer tiro se declaran como constantes public final static int en las líneas 14 a 18. Estos valores se utilizan en las etiquetas case de la instrucción switch. Los nombres de los identificadores utilizan los términos comunes en el casino para estas sumas. Observe que estas constantes, al igual que las constantes enum, se declaran todas con letras mayúsculas por convención, para que resalten en el programa. Las líneas 31 a 34 determinan si el jugador ganó en el primer tiro con SIETE (7) u ONCE (11). Las líneas 35 a 39 determinan si el jugador perdió en el primer tiro con DOS_UNOS (2), TRES (3) o DOCE (12). Después del primer tiro, si el juego no se ha terminado, el caso default (líneas 40 a 44) establece estadoJuego en Estado. CONTINUA, guarda sumaDeDados en miPunto y muestra el punto. Si aún estamos tratando de “hacer nuestro punto” (es decir, el juego continúa de un tiro anterior), se ejecuta el ciclo de las líneas 48 a 58. En la línea 50 se tira el dado otra vez. Si sumaDeDados concuerda con miPunto en la línea 53, la línea 54 establece estadoJuego en Estado.GANO y el ciclo termina, ya que el juego está terminado. En la línea 56, si sumaDeDados es igual a SIETE (7), la línea 57 asigna el valor Estado.PERDIO a estadoJuego y el ciclo termina, ya que se acabó el juego. Cuando termina el juego, las líneas 61 a 64 muestran un mensaje en el que se indica si el jugador ganó o perdió, y el programa termina. Observe el uso de varios mecanismos de control del programa que hemos visto antes. La clase Craps, en con- junto con la clase PruebaCraps, utiliza tres métodos: main, jugar (que se llama desde main) y tirarDados (que se llama dos veces desde jugar), y las instrucciones de control switch, while, if…else e if anidado. Observe también el uso de múltiples etiquetas case en la instrucción switch para ejecutar las mismas instrucciones para las sumas de SIETE y ONCE (líneas 31 y 32), y para las sumas de DOS_UNOS, TRES y DOCE (líneas 35 a 37). Tal vez se esté preguntando por qué declaramos las sumas de los dados como constantes public final sta- tic int en vez de constantes enum. La respuesta está en el hecho de que el programa debe comparar la variable int llamada sumaDeDados (línea 26) con estas constantes para determinar el resultado de cada tiro. Suponga que declararemos constantes que contengan enum Suma (por ejemplo, Suma.DOS_UNOS) para representar las cinco sumas utilizadas en el juego, y que después usaremos estas constantes en las etiquetas case de la instrucción switch (líneas 29 a 45). Hacer esto evitaría que pudiéramos usar sumaDeDados como la expresión de control de la instrucción switch, ya que Java no permite que un int se compare con una constante de enumeración. Para lograr la misma funcionalidad que el programa actual, tendríamos que utilizar una variable sumaActual de tipo Suma como expresión de control para el switch. Por desgracia, Java no proporciona una manera fácil de convertir un valor int en una constante enum específica. Podríamos traducir un int en una constante enum mediante una instrucción switch separada. Sin duda, esto sería complicado y no mejoraría la legibilidad del programa (lo cual echaría a perder el propósito de usar una enum). 6.11 Alcance de las declaraciones Ya hemos visto declaraciones de varias entidades de Java como las clases, los métodos, las variables y los paráme- tros. Las declaraciones introducen nombres que pueden utilizarse para hacer referencia a dichas entidades de Java. El alcance de una declaración es la porción del programa que puede hacer referencia a la entidad declarada por su nombre. Se dice que dicha entidad está “dentro del alcance” para esa porción del programa. En esta sección introduciremos varias cuestiones importantes relacionadas con el alcance. (Para obtener más información sobre
  • 271. el alcance, consulte la Especificación del lenguaje Java, sección 6.3: Alcance de una declaración, en java.sun.com/ docs/books/jls/second_edition/html/names.doc.html#103228). Las reglas básicas de alcance son las siguientes: 1. El alcance de la declaración de un parámetro es el cuerpo del método en el que aparece la declaración. 2. El alcance de la declaración de una variable local es a partir del punto en el cual aparece la declaración, hasta el final de ese bloque. 3. El alcance de la declaración de una variable local que aparece en la sección de inicialización del encabeza- do de una instrucción for es el cuerpo de la instrucción for y las demás expresiones en el encabezado. 4. El alcance de un método o campo de una clase es todo el cuerpo de la clase. Esto permite a los métodos no static de la clase utilizar cualquiera de los campos y otros métodos de la clase. Cualquier bloque puede contener declaraciones de variables. Si una variable local o parámetro en un método tiene el mismo nombre que un campo, el campo se “oculta” hasta que el bloque termina su ejecución; a esto se le llama ocultación de variables (shadowing). En el capítulo 8 veremos cómo acceder a los campos ocultos. Error común de programación 6.10 Cuando una variable local se declara más de una vez en un método, se produce un error de compilación. Tip de prevención de errores 6.3 Use nombres distintos para los campos y las variables locales, para ayudar a evitar los errores lógicos sutiles que se producen cuando se hace la llamada a un método y una variable local de ese método oculta un campo con el mismo nombre en la clase. La aplicación en las figuras 6.11 y 6.12 demuestra las cuestiones de alcance con los campos y las variables locales. Cuando la aplicación empieza a ejecutarse, el método main de la clase PruebaAlcance (figura 6.12, líneas 7 a 11) crea un objeto de la clase Alcance (línea 9) y llama al método iniciar del objeto (línea 10) para producir el resultado de la aplicación (el cual se muestra en la figura 6.12). 1 // Fig. 6.11: Alcance.java 2 // La clase Alcance demuestra los alcances de los campos y las variables locales. 3 4 public class Alcance 5 { 6 // campo accesible para todos los métodos de esta clase 7 private int x = 1; 8 9 // el método iniciar crea e inicializa la variable local x 10 // y llama a los métodos usarVariableLocal y usarCampo 11 public void iniciar() 12 { 13 int x = 5; // la variable local x del método oculta al campo x 14 15 System.out.printf( "la x local en el metodo iniciar es %dn", x ); 16 17 usarVariableLocal(); // usarVariableLocal tiene la x local 18 usarCampo(); // usarCampo usa el campo x de la clase Alcance 19 usarVariableLocal(); // usarVariableLocal reinicia a la x local 20 usarCampo(); // el campo x de la clase Alcance retiene su valor 21 Figura 6.11 | La clase Alcance demuestra los alcances de los campos y las variables locales. (Parte 1 de 2). 6.11 Alcance de las declaraciones 233
  • 272. 234 Capítulo 6 Métodos: un análisis más detallado 1 // Fig. 6.12: PruebaAlcance.java 2 // Aplicación para probar la clase Alcance. 3 4 public class PruebaAlcance 5 { 6 // punto inicial de la aplicación 7 public static void main( String args[] ) 8 { 9 Alcance alcancePrueba = new Alcance(); 10 alcancePrueba.iniciar(); 11 } // fin de main 12 } // fin de la clase PruebaAlcance Figura 6.12 | Aplicación para probar la clase Alcance. la x local en el metodo iniciar es 5 la x local al entrar al metodo usarVariableLocal es 25 la x local antes de salir del metodo usarVariableLocal es 26 el campo x al entrar al metodo usarCampo es 1 el campo x antes de salir del metodo usarCampo es 10 la x local al entrar al metodo usarVariableLocal es 25 la x local antes de salir del metodo usarVariableLocal es 26 el campo x al entrar al metodo usarCampo es 10 el campo x antes de salir del metodo usarCampo es 100 la x local en el metodo iniciar es 5 Figura 6.11 | La clase Alcance demuestra los alcances de los campos y las variables locales. (Parte 2 de 2). 22 System.out.printf( "nla x local en el metodo iniciar es %dn”, x ); 23 } // fin del método iniciar 24 25 // crea e inicializa la variable local x durante cada llamada 26 public void usarVariableLocal() 27 { 28 int x = 25; // se inicializa cada vez que se llama a usarVariableLocal 29 30 System.out.printf( 31 "nla x local al entrar al metodo usarVariableLocal es %dn", x ); 32 ++x; // modifica la variable x local de este método 33 System.out.printf( 34 "la x local antes de salir del metodo usarVariableLocal es %dn", x ); 35 } // fin del método usarVariableLocal 36 37 // modifica el campo x de la clase Alcance durante cada llamada 38 public void usarCampo() 39 { 40 System.out.printf( 41 "nel campo x al entrar al metodo usarCampo es %dn", x ); 42 x *= 10; // modifica el campo x de la clase Alcance 43 System.out.printf( 44 "el campo x antes de salir del metodo usarCampo es %dn", x ); 45 } // fin del método usarCampo 46 } // fin de la clase Alcance
  • 273. En la clase Alcance, la línea 7 declara e inicializa el campo x en 1. Este campo se oculta en cualquier bloque (o método) que declare una variable local llamada x. El método iniciar (líneas 11 a 23) declara una variable local x (línea 13) y la inicializa en 5. El valor de esta variable local se imprime para mostrar que el campo x (cuyo valor es 1) se oculta en el método iniciar. El programa declara otros dos métodos: usarVariableLocal (líneas 26 a 35) y usarCampo (líneas 38 a 45); cada uno de ellos no tiene argumentos y no devuelve resultados. El méto- do iniciar llama a cada método dos veces (líneas 17 a 20). El método usarVariableLocal declara la variable local x (línea 28). Cuando se llama por primera vez a usarVariableLocal (línea 17), crea una variable local x y la inicializa en 25 (línea 28), muestra en pantalla el valor de x (líneas 30 y 31), incrementa x (línea 32) y muestra en pantalla el valor de x otra vez (líneas 33 y 34). Cuando se llama a usarVariableLocal por segunda vez (línea 19), vuelve a crear la variable local x y la reinicializa con 25, por lo que la salida de cada llamada a usarVariableLocal es idéntica. El método usarCampo no declara variables locales. Por lo tanto, cuando hace referencia a x, se utiliza el cam- po x (línea 7) de la clase. Cuando el método usarCampo se llama por primera vez (línea 18), muestra en pantalla el valor (1) del campo x (líneas 40 y 41), multiplica el campo x por 10 (línea 42) y muestra en pantalla el valor (10) del campo x otra vez (líneas 43 y 44) antes de regresar. La siguiente vez que se llama al método usarCampo (línea 20), el campo x tiene el valor modificado de 10, por lo que el método muestra en pantalla un 10 y después un 100. Por último, en el método iniciar el programa muestra en pantalla el valor de la variable local x otra vez (línea 22), para mostrar que ninguna de las llamadas a los métodos modificó la variable local x de iniciar, ya que todos los métodos hicieron referencia a las variables llamadas x en otros alcances. 6.12 Sobrecarga de métodos Pueden declararse métodos con el mismo nombre en la misma clase, siempre y cuando tengan distintos conjun- tos de parámetros (determinados en base al número, tipos y orden de los parámetros). A esto se le conoce como sobrecarga de métodos. Cuando se hace una llamada a un método sobrecargado, el compilador de Java seleccio- na el método apropiado mediante un análisis del número, tipos y orden de los argumentos en la llamada. Por lo general, la sobrecarga de métodos se utiliza para crear varios métodos con el mismo nombre que realicen la misma tarea o tareas similares, pero con distintos tipos o distintos números de argumentos. Por ejemplo, los métodos abs, min y max de Math (sintetizados en la sección 6.3) se sobrecargan con cuatro versiones cada uno: 1. Uno con dos parámetros double. 2. Uno con dos parámetros float. 3. Uno con dos parámetros int. 4. Uno con dos parámetros long. Nuestro siguiente ejemplo demuestra cómo declarar e invocar métodos sobrecargados. En el capítulo 8 presenta- remos ejemplos de constructores sobrecargados. Declaración de métodos sobrecargados En nuestra clase SobrecargaMetodos (figura 6.13) incluimos dos versiones sobrecargadas de un método lla- mado cuadrado: una que calcula el cuadrado de un int (y devuelve un int) y otra que calcula el cuadrado de un double (y devuelve un double). Aunque estos métodos tienen el mismo nombre, además de listas de paráme- tros y cuerpos similares, podemos considerarlos simplemente como métodos diferentes. Puede ser útil si considera- mos los nombres de los métodos como “cuadrado de int” y “cuadrado de double”, respectivamente. Cuando la aplicación empieza a ejecutarse, el método main de la clase PruebaSobrecargaMetodos (figura 6.14, líneas 6 a 10) crea un objeto de la clase SobrecargaMetodos (línea 8) y llama al método probarMetodosSobrecargados del objeto (línea 9) para producir la salida del programa (figura 6.14). En la figura 6.13, la línea 9 invoca al método cuadrado con el argumento 7. Los valores enteros literales se tratan como de tipo int, por lo que la llamada al método en la línea 9 invoca a la versión de cuadrado de las líneas 14 a 19, la cual especifica un parámetro int. De manera similar, la línea 10 invoca al método cuadrado con el argumento 7.5. Los valores de las literales de punto flotante se tratan como de tipo double, por lo que la llama- da al método en la línea 10 invoca a la versión de cuadrado de las líneas 22 a 27, la cual especifica un parámetro double. Cada método imprime en pantalla primero una línea de texto, para mostrar que se llamó al método 6.12 Sobrecarga de métodos 235
  • 274. 236 Capítulo 6 Métodos: un análisis más detallado 1 // Fig. 6.13: SobrecargaMetodos.java 2 // Declaraciones de métodos sobrecargados. 3 4 public class SobrecargaMetodos 5 { 6 // prueba los métodos cuadrado sobrecargados 7 public void probarMetodosSobrecargados() 8 { 9 System.out.printf( "El cuadrado del entero 7 es %dn", cuadrado( 7 ) ); 10 System.out.printf( "El cuadrado del double 7.5 es %fn", cuadrado( 7.5 ) ); 11 } // fin del método probarMetodosSobrecargados 12 13 // método cuadrado con argumento int 14 public int cuadrado( int valorInt ) 15 { 16 System.out.printf( "nSe llamo a cuadrado con argumento int: %dn", 17 valorInt ); 18 return valorInt * valorInt; 19 } // fin del método cuadrado con argumento int 20 21 // método cuadrado con argumento double 22 public double cuadrado( double valorDouble ) 23 { 24 System.out.printf( "nSe llamo a cuadrado con argumento double: %fn", 25 valorDouble ); 26 return valorDouble * valorDouble; 27 } // fin del método cuadrado con argumento double 28 } // fin de la clase SobrecargaMetodos Figura 6.13 | Declaraciones de métodos sobrecargados. 1 // Fig. 6.14: PruebaSobrecargaMetodos.java 2 // Aplicación para probar la clase SobrecargaMetodos. 3 4 public class PruebaSobrecargaMetodos 5 { 6 public static void main( String args[] ) 7 { 8 SobrecargaMetodos sobrecargaMetodos = new SobrecargaMetodos(); 9 sobrecargaMetodos.probarMetodosSobrecargados(); 10 } // fin de main 11 } // fin de la clase PruebaSobrecargaMetodos Se llamo a cuadrado con argumento int: 7 El cuadrado del entero 7 es 49 Se llamo a cuadrado con argumento double: 7.500000 El cuadrado del double 7.5 es 56.250000 Figura 6.14 | Aplicación para probar la clase SobrecargaMetodos. apropiado en cada caso. Observe que los valores en las líneas 10 y 24 se muestran con el especificador de formato %f y que no especificamos una precisión en ninguno de los dos casos. De manera predeterminada, los valores de punto flotante se muestran con seis dígitos de precisión, si ésta no se especifica en el especificador de formato.
  • 275. Cómo se diferencian los métodos sobrecargados entre sí El compilador diferencia los métodos sobrecargados en base a su firma: una combinación del nombre del método y del número, tipos y orden de sus parámetros. Si el compilador sólo se fijara en los nombres de los métodos durante la compilación, el código de la figura 6.13 sería ambiguo; el compilador no sabría cómo distinguir entre los dos métodos cuadrado (líneas 14 a 19 y 22 a 27). De manera interna, el compilador utiliza nombres de métodos más largos que incluyen el nombre del método original, el tipo de cada parámetro y el orden exacto de los parámetros para determinar si los métodos en una clase son únicos en esa clase. Por ejemplo, en la figura 6.13 el compilador podría utilizar el nombre lógico “cuadrado de int” para el método cuadrado que especifica un parámetro int, y el método “cuadrado de double” para el método cua- drado que especifica un parámetro double (los nombres reales que utiliza el compilador son más complicados). Si la declaración de metodo1 empieza así: void metodo1( int a, float b ) entonces el compilador podría usar el nombre lógico “metodo1 de int y float”. Si los parámetros se especifi- caran así: void metodo1( float a, int b ) entonces el compilador podría usar el nombre lógico “metodo1 de float e int”. Observe que el orden de los tipos de los parámetros es importante; el compilador considera que los dos encabezados anteriores de metodo1 son distintos. Tipos de valores de retorno de los métodos sobrecargados Al hablar sobre los nombres lógicos de los métodos que utiliza el compilador, no mencionamos los tipos de valo- res de retorno de los métodos. Esto se debe a que las llamadas a los métodos no pueden diferenciarse en base al tipo de valor de retorno. El programa de la figura 6.15 ilustra los errores que genera el compilador cuando dos métodos tienen la misma firma, pero distintos tipos de valores de retorno. Los métodos sobrecargados pueden tener tipos de valor de retorno distintos si los métodos tienen distintas listas de parámetros. Además, los métodos sobrecargados no necesitan tener el mismo número de parámetros. 1 // Fig. 6.15: SobrecargaMetodos.java 2 // Los métodos sobrecargados con firmas idénticas producen errores de 3 // compilación, aun si los tipos de valores de retorno son distintos. 4 5 public class ErrorSobrecargaMetodos 6 { 7 // declaración del método cuadrado con argumento int 8 public int cuadrado( int x ) 9 { 10 return x * x; 11 } 12 13 // la segunda declaración del método cuadrado con argumento int produce un error 14 // de compilación, aun cuando los tipos de valores de retorno son distintos 15 public double cuadrado( int y ) 16 { 17 return y * y; 18 } 19 } // fin de la clase ErrorSobrecargaMetodos ErrorSobrecargaMetodos.java:15: cuadrado(int) is already defined in ErrorSobrecargaMetodos public double cuadrado( int y ) ^ 1 error Figura 6.15 | Las declaraciones de métodos sobrecargados con firmas idénticas producen errores de compilación, aun si los tipos de valores de retorno son distintos. 6.12 Sobrecarga de métodos 237
  • 276. 238 Capítulo 6 Métodos: un análisis más detallado Error común de programación 6.11 Declarar métodos sobrecargados con listas de parámetros idénticas es un error de compilación, sin importar que los tipos de los valores de retorno sean distintos. 6.13 (Opcional) Ejemplo práctico de GUI y gráficos: colores y figuras rellenas Aunque podemos crear muchos diseños interesantes sólo con líneas y figuras básicas, la clase Graphics pro- porciona muchas herramientas más. Las siguientes dos herramientas que presentaremos son los colores y las figuras rellenas. El color agrega otra dimensión a los dibujos que ve un usuario en la pantalla de la computadora. Las figuras rellenas cubren regiones completas con colores sólidos, en vez de dibujar sólo contornos. Los colores que se muestran en las pantallas de las computadoras se definen en base a sus componentes rojo, verde y azul. Estos componentes, llamados valores RGB, tienen valores enteros de 0 a 255. Entre más alto sea el valor de un componente específico, más intensidad de color tendrá esa figura. Java utiliza la clase Color en el paquete java.awt para representar colores usando sus valores RGB. Por conveniencia, el objeto Color contie- ne 13 objetos static Color predefinidos: Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Co- lor.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.WHITE y Color.YELLOW. La clase Color también contiene un constructor de la forma: public Color( int r, int g, int b ) de manera que podemos crear colores específicos, con sólo especificar valores para los componentes individuales rojo, verde y azul de un color. Los rectángulos y los óvalos rellenos se dibujan usando los métodos fillRect y fillOval de Graphics, respectivamente. Estos dos métodos tienen los mismos parámetros que sus contrapartes drawRect y drawOval sin relleno: los primeros dos parámetros son las coordenadas para la esquina superior izquierda de la figura, mien- tras que los otros dos parámetros determinan su anchura y su altura. El ejemplo de las figuras 6.16 y 6.17 demues- tra los colores y las figuras rellenas, al dibujar y mostrar una cara sonriente amarilla (esto lo verá en su pantalla). 1 // Fig. 6.16: DibujarCaraSonriente.java 2 // Demuestra las figuras rellenas. 3 import java.awt.Color; 4 import java.awt.Graphics; 5 import javax.swing.JPanel; 6 7 public class DibujarCaraSonriente extends JPanel 8 { 9 public void paintComponent( Graphics g ) 10 { 11 super.paintComponent( g ); 12 13 // dibuja la cara 14 g.setColor( Color.YELLOW ); 15 g.fillOval( 10, 10, 200, 200 ); 16 17 // dibuja los ojos 18 g.setColor( Color.BLACK ); 19 g.fillOval( 55, 65, 30, 30 ); 20 g.fillOval( 135, 65, 30, 30 ); 21 22 // dibuja la boca 23 g.fillOval( 50, 110, 120, 60 ); 24 25 // convierte la boca en una sonrisa Figura 6.16 | Cómo dibujar una cara sonriente, usando colores y figuras rellenas. (Parte 1 de 2).
  • 277. 26 g.setColor( Color.YELLOW ); 27 g.fillRect( 50, 110, 120, 30 ); 28 g.fillOval( 50, 120, 120, 40 ); 29 } // fin del método paintComponent 30 } // fin de la clase DibujarCaraSonriente Figura 6.16 | Cómo dibujar una cara sonriente, usando colores y figuras rellenas. (Parte 2 de 2). 1 // Fig. 6.17: PruebaDibujarCaraSonriente.java 2 // Aplicación de prueba que muestra una cara sonriente. 3 import javax.swing.JFrame; 4 5 public class PruebaDibujarCaraSonriente 6 { 7 public static void main( String args[] ) 8 { 9 DibujarCaraSonriente panel = new DibujarCaraSonriente(); 10 JFrame aplicacion = new JFrame(); 11 12 aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); 13 aplicacion.add( panel ); 14 aplicacion.setSize( 230, 250 ); 15 aplicacion.setVisible( true ); 16 } // fin de main 17 } // fin de la clase PruebaDibujarCaraSonriente Figura 6.17 | Creación de un objeto JFrame para mostrar una cara sonriente. Las instrucciones import en las líneas 3 a 5 de la figura 6.16 importan las clases Color, Graphics y JPanel. La clase DibujarCaraSonriente (líneas 7 a 30) utiliza la clase Color para especificar los colores, y utiliza la clase Graphics para dibujar. La clase JPanel proporciona de nuevo el área en la que vamos a dibujar. La línea 14 en el método paintComponent utiliza el método setColor de Graphics para establecer el color actual para dibujar en Color.YELLOW. El método setColor requiere un argumento, el Color a establecer como el color para dibu- jar. En este caso, utilizamos el objeto predefinido Color.YELLOW. La línea 15 dibuja un círculo con un diámetro de 200 para representar la cara; cuando los argumentos anchura y altura son idénticos, el método fillOval dibuja un círculo. A continuación, la línea 18 establece el color en Color.BLACK, y las líneas 19 y 20 dibujan los ojos. La línea 23 dibuja la boca como un óvalo, pero esto no es exactamente lo que queremos. Para crear una cara feliz, vamos a “retocar” la boca. La línea 26 establece el color en Color.YELLOW, de manera que cualquier figura que dibujemos se mezcle con la cara. La línea 27 dibuja un rectángulo con la mitad de altura que la boca. Esto “borra” la mitad superior de la boca, dejando sólo la mitad inferior. Para crear una mejor sonrisa, la línea 28 dibuja otro óvalo para cubrir ligeramente la porción superior de la boca. La clase PruebaDibujarCaraSonriente (figura 6.17) crea y muestra un objeto JFrame que contiene el dibujo. Cuando se muestra el objeto JFrame, el sistema llama al método paintComponent para dibujar la cara sonriente. 6.13 (Opcional) Ejemplo práctico de GUI y gráficos: colores y figuras rellenas 239
  • 278. 240 Capítulo 6 Métodos: un análisis más detallado Ejercicios del ejemplo práctico de GUI y gráficos 6.1 Usando el método fillOval, dibuje un tiro al blanco que alterne entre dos colores aleatorios, como en la figura 6.18. Use el constructor Color( int r, int g, int b ) con argumentos aleatorios para generar colores aleatorios. 6.2 Cree un programa para dibujar 10 figuras rellenas al azar en colores, posiciones y tamaños aleatorios (figura 6.19). El método paintComponent debe contener un ciclo que itere 10 veces. En cada iteración, el ciclo debe determi- nar si se dibujará un rectángulo o un óvalo relleno, crear un color aleatorio y elegir las coordenadas y las medidas al azar. Las coordenadas deben elegirse con base en la anchura y la altura del panel. Las longitudes de los lados deben limitarse a la mitad de la anchura o altura de la ventana. Figura 6.18 | Un tiro al blanco con dos colores alternantes al azar. Figura 6.19 | Figuras generadas al azar.
  • 279. 6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones de las clases En las secciones del Ejemplo práctico de Ingeniería de Software al final de los capítulos 3 a 5, llevamos a cabo los primeros pasos en el diseño orientado a objetos de nuestro sistema ATM. En el capítulo 3 identificamos las clases que necesitaremos implementar, y creamos nuestro primer diagrama de clases. En el capítulo 4 describimos varios atributos de nuestras clases. En el capítulo 5 examinamos los estados de nuestros objetos y modelamos sus transi- ciones de estado y actividades. En esta sección determinaremos algunas de las operaciones (o comportamientos) de las clases que son necesarias para implementar el sistema ATM. Identificar las operaciones Una operación es un servicio que proporcionan los objetos de una clase a los clientes (usuarios) de esa clase. Con- sidere las operaciones de algunos objetos reales. Las operaciones de un radio incluyen el sintonizar su estación y ajustar su volumen (que, por lo general, lo hace una persona que ajusta los controles del radio). Las operaciones de un automóvil incluyen acelerar (operación invocada por el conductor cuando oprime el pedal del acelerador), desacelerar (operación invocada por el conductor cuando oprime el pedal del freno o cuando suelta el pedal del acelerador), dar vuelta y cambiar velocidades. Los objetos de software también pueden ofrecer operaciones; por ejemplo, un objeto de gráficos de software podría ofrecer operaciones para dibujar un círculo, dibujar una línea, dibujar un cuadrado, etcétera. Un objeto de software de hoja de cálculo podría ofrecer operaciones como impri- mir la hoja de cálculo, totalizar los elementos en una fila o columna, y graficar la información de la hoja de cálculo como un gráfico de barras o de pastel. Podemos derivar muchas de las operaciones de cada clase mediante un análisis de los verbos y las frases ver- bales clave en el documento de requerimientos. Después relacionamos cada una de ellas con las clases específicas en nuestro sistema (figura 6.20). Las frases verbales en la figura 6.20 nos ayudan a determinar las operaciones de cada clase. Modelar las operaciones Para identificar las operaciones, analizamos las frases verbales que se listan para cada clase en la figura 6.20. La frase “ejecuta transacciones financieras” asociada con la clase ATM implica que esta clase instruye a las transacciones a que se ejecuten. Por lo tanto, cada una de las clases SolicitudSaldo, Retiro y Deposito necesitan una ope- ración para proporcionar este servicio al ATM. Colocamos esta operación (que hemos nombrado ejecutar) en Clase Verbos y frases verbales ATM ejecuta transacciones financieras SolicitudSaldo [ninguna en el documento de requerimientos] Retiro [ninguna en el documento de requerimientos] Deposito [ninguna en el documento de requerimientos] BaseDatosBanco autentica a un usuario, obtiene el saldo de una cuenta, abona un monto de depósito a una cuenta, carga un monto de retiro a una cuenta Cuenta obtiene el saldo de una cuenta, abona un monto de depósito a una cuenta, carga un monto de retiro a una cuenta Pantalla muestra un mensaje al usuario Teclado recibe entrada numérica del usuario DispensadorEfectivo dispensa efectivo, indica si contiene suficiente efectivo para satisfacer una solicitud de retiro RanuraDeposito recibe un sobre de depósito Figura 6.20 | Verbos y frases verbales para cada clase en el sistema ATM. 6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones... 241
  • 280. 242 Capítulo 6 Métodos: un análisis más detallado el tercer compartimiento de las tres clases de transacciones en el diagrama de clases actualizado de la figura 6.21. Durante una sesión con el ATM, el objeto ATM invocará estas operaciones de transacciones, según sea necesario. Para representar las operaciones (que se implementan en forma de métodos en Java), UML lista el nombre de la operación, seguido de una lista separada por comas de parámetros entre paréntesis, un signo de punto y coma y el tipo de valor de retorno: nombreOperación( parámetro1, parámetro2, …, parámetroN ) : tipo de valor de retorno Cada parámetro en la lista separada por comas consiste en un nombre de parámetro, seguido de un signo de dos puntos y del tipo del parámetro: nombreParámetro : tipoParámetro Por el momento, no listamos los parámetros de nuestras operaciones; en breve identificaremos y modelare- mos los parámetros de algunas de las operaciones. Para algunas de estas operaciones no conocemos todavía los tipos de valores de retorno, por lo que también las omitiremos del diagrama. Estas omisiones son perfectamente normales en este punto. A medida que avancemos en nuestro proceso de diseño e implementación, agregaremos el resto de los tipos de valores de retorno. La figura 6.20 lista la frase “autentica a un usuario” enseguida de la clase BaseDatosBanco; la base de datos es el objeto que contiene la información necesaria de la cuenta para determinar si el número de cuenta y el NIP introducidos por un usuario concuerdan con los de una cuenta en el banco. Por lo tanto, la clase BaseDatos- Banco necesita una operación que proporcione un servicio de autenticación al ATM. Colocamos la operación Figura 6.21 | Las clases en el sistema ATM, con atributos y operaciones. ATM usuarioAutenticado : Boolean = false SolicitudSaldo numeroCuenta : Integer DispensadorEfectivo cuenta : Integer = 500 RanuraDeposito Pantalla Teclado Retiro numeroCuenta : Integer monto : Double BaseDatosBanco Deposito numeroCuenta : Integer monto : Double autenticarUsuario() : Boolean obtenerSaldoDisponible() : Double obtenerSaldoTotal() : Double abonar() cargar() Cuenta numeroCuenta : Integer nip : Integer saldoDisponible : Double saldoTotal : Double validarNIP() : Boolean obtenerSaldoDisponible() : Double obtenerSaldoTotal() : Double abonar() cargar() ejecutar() ejecutar() mostrarMensaje() dispensarEfectivo() haySuficienteEfectivoDisponible() : Boolean obtenerEntrada() : Integer ejecutar() seRecibioSobre() : Boolean
  • 281. autenticarUsuario en el tercer compartimiento de la clase BaseDatosBanco (figura 6.21). No obstante, un objeto de la clase Cuenta y no de la clase BaseDatosBanco es el que almacena el número de cuenta y el NIP a los que se debe acceder para autenticar a un usuario, por lo que la clase Cuenta debe proporcionar un servicio para validar un NIP obtenido como entrada del usuario, y compararlo con un NIP almacenado en un objeto Cuenta. Por ende, agregamos una operación validarNIP a la clase Cuenta. Observe que especificamos un tipo de valor de retorno Boolean para las operaciones autenticarUsuario y validarNIP. Cada operación devuelve un valor que indica que la operación tuvo éxito al realizar su tarea (es decir, un valor de retorno true) o que no tuvo éxito (es decir, un valor de retorno false). La figura 6.20 lista varias frases verbales adicionales para la clase BaseDatosBanco: “extrae el saldo de una cuenta”, “abona un monto de depósito a una cuenta” y “carga un monto de retiro a una cuenta”. Al igual que “autentica a un usuario”, estas frases restantes se refieren a los servicios que debe proporcionar la base de datos al ATM, ya que la base de datos almacena todos los datos de las cuentas que se utilizan para autenticar a un usuario y realizar transacciones con el ATM. No obstante, los objetos de la clase Cuenta son los que en realidad realizan las operaciones a las que se refieren estas frases. Por ello, asignamos una operación tanto a la clase BaseDatos- Banco como a la clase Cuenta, que corresponda con cada una de estas frases. En la sección 3.10 vimos que, como una cuenta de banco contiene información delicada, no permitimos que el ATM acceda a las cuentas en forma directa. La base de datos actúa como un intermediario entre el ATM y los datos de la cuenta, evitando el acceso no autorizado. Como veremos en la sección 7.14, la clase ATM invoca las operaciones de la clase BaseDatosBanco, cada una de las cuales a su vez invoca a la operación con el mismo nombre en la clase Cuenta. La frase “obtiene el saldo de una cuenta” sugiere que las clases BaseDatosBanco y Cuenta necesitan una ope- ración obtenerSaldo. Sin embargo, recuerde que creamos dos atributos en la clase Cuenta para representar un saldo: saldoDisponible y saldoTotal. Una solicitud de saldo requiere el acceso a estos dos atributos del saldo, de manera que pueda mostrarlos al usuario, pero un retiro sólo requiere verificar el valor de saldoDisponible. Para permitir que los objetos en el sistema obtengan cada atributo de saldo en forma individual, agregamos las operaciones obtenerSaldoDisponible y obtenerSaldoTotal al tercer compartimiento de las clases Base- DatosBanco y Cuenta (figura 6.21). Especificamos un tipo de valor de retorno Double para estas operaciones, debido a que los atributos de los saldos que van a obtener son de tipo Double. Las frases “abona un monto de depósito a una cuenta” y “carga un monto de retiro a una cuenta” indican que las clases BaseDatosBanco y Cuenta deben realizar operaciones para actualizar una cuenta durante un depósito y un retiro, respectivamente. Por lo tanto, asignamos las operaciones abonar y cargar a las clases BaseDatosBanco y Cuenta. Tal vez recuerde que cuando se abona a una cuenta (como en un depósito) se suma un monto sólo al atributo saldoTotal. Por otro lado, cuando se carga a una cuenta (como en un retiro) se resta el monto tanto del saldo total como del saldo disponible. Ocultamos estos detalles de implementación dentro de la clase Cuenta. Éste es un buen ejemplo de encapsulamiento y ocultamiento de información. Si éste fuera un sistema ATM real, las clases BaseDatosBanco y Cuenta también proporcionarían un con- junto de operaciones para permitir que otro sistema bancario actualizara el saldo de la cuenta de un usuario des- pués de confirmar o rechazar todo, o parte de, un depósito. Por ejemplo, la operación confirmarMontoDeposito sumaría un monto al atributo saldoDisponible, y haría que los fondos depositados estuvieran disponibles para retirarlos. La operación rechazarMontoDeposito restaría un monto al atributo saldoTotal para indicar que un monto especificado, que se había depositado recientemente a través del ATM y se había sumado al saldoTotal, no se encontró en el sobre de depósito. El banco invocaría esta operación después de determinar que el usuario no incluyó el monto correcto de efectivo o que algún cheque no fue validado (es decir, que “rebotó”). Aunque al agregar estas operaciones nuestro sistema estaría más completo, no las incluiremos en nuestros diagramas de clases ni en nuestra implementación, ya que se encuentran más allá del alcance de este ejemplo práctico. La clase Pantalla “muestra un mensaje al usuario” en diversos momentos durante una sesión con el ATM. Toda la salida visual se produce a través de la pantalla del ATM. El documento de requerimientos describe muchos tipos de mensajes (por ejemplo, un mensaje de bienvenida, un mensaje de error, un mensaje de agra- decimiento) que la pantalla muestra al usuario. El documento de requerimientos también indica que la pantalla muestra indicadores y menús al usuario. No obstante, un indicador es en realidad sólo un mensaje que describe lo que el usuario debe introducir a continuación, y un menú es en esencia un tipo de indicador que consiste en una serie de mensajes (es decir, las opciones del menú) que se muestran en forma consecutiva. Por lo tanto, en vez de asignar a la clase Pantalla una operación individual para mostrar cada tipo de mensaje, indicador y menú, basta con crear una operación que pueda mostrar cualquier mensaje especificado por un parámetro. Colocamos esta operación (mostrarMensaje) en el tercer compartimiento de la clase Pantalla en nuestro diagrama de 6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones... 243
  • 282. clases (figura 6.21). Observe que no nos preocupa el parámetro de esta operación en estos momentos; lo mode- laremos más adelante en esta sección. De la frase “recibe entrada numérica del usuario” listada por la clase Teclado en la figura 6.20, podemos con- cluir que la clase Teclado debe realizar una operación obtenerEntrada. A diferencia del teclado de una compu- tadora, el teclado del ATM sólo contiene los números del 0 al 9, por lo cual especificamos que esta operación devuelve un valor entero. Si recuerda, en el documento de requerimientos vimos que en distintas situaciones, tal vez se requiera que el usuario introduzca un tipo distinto de número (por ejemplo, un número de cuenta, un NIP, el número de una opción del menú, un monto de depósito como número de centavos). La clase Teclado sólo obtiene un valor numérico para un cliente de la clase; no determina si el valor cumple con algún criterio específi- co. Cualquier clase que utilice esta operación debe verificar que el usuario haya introducido un número apropiado según el caso, y después debe responder de manera acorde (por ejemplo, mostrar un mensaje de error a través de la clase Pantalla). [Nota: cuando implementemos el sistema, simularemos el teclado del ATM con el teclado de una computadora y, por cuestión de simpleza, asumiremos que el usuario no escribirá datos de entrada que no sean números, usando las teclas en el teclado de la computadora que no aparezcan en el teclado del ATM]. La figura 6.20 lista la frase “dispensa efectivo” para la clase DispensadorEfectivo. Por lo tanto, creamos la operación dispensarEfectivo y la listamos bajo la clase DispensadorEfectivo en la figura 6.21. La clase Dis- pensadorEfectivo también “indica si contiene suficiente efectivo para satisfacer una solicitud de retiro”. Para esto incluimos a haySuficienteEfectivoDisponible, una operación que devuelve un valor de tipo Boolean de UML, en la clase DispensadorEfectivo. La figura 6.20 también lista la frase “recibe un sobre de depósito” para la clase RanuraDeposito. La ranura de depósito debe indicar si recibió un sobre, por lo que colocamos una operación seRecibioSobre, la cual devuelve un valor Boolean, en el tercer compartimiento de la clase Ranu- raDeposito. [Nota: es muy probable que una ranura de depósito de hardware real envíe una señal al ATM para indicarle que se recibió un sobre. No obstante, simularemos este comportamiento con una operación en la clase RanuraDeposito, que la clase ATM pueda invocar para averiguar si la ranura de depósito recibió un sobre]. No listamos ninguna operación para la clase ATM en este momento. Todavía no sabemos de algún servicio que proporcione la clase ATM a otras clases en el sistema. No obstante, cuando implementemos el sistema en código de Java, tal vez emerjan las operaciones de esta clase junto con las operaciones adicionales de las demás clases en el sistema. Identificar y modelar los parámetros de operación Hasta ahora no nos hemos preocupado por los parámetros de nuestras operaciones; sólo hemos tratado de obtener una comprensión básica de las operaciones de cada clase. Ahora daremos un vistazo más de cerca a varios paráme- tros de operación. Para identificar los parámetros de una operación, analizamos qué datos requiere la operación para realizar su tarea asignada. Considere la operación autenticarUsuario de la clase BaseDatosBanco. Para autenticar a un usuario, esta operación debe conocer el número de cuenta y el NIP que suministra el usuario. Por lo tanto, especificamos que la operación autenticarUsuario debe recibir los parámetros enteros numeroCuentaUsuario y nipUsuario, que la operación debe comparar con el número de cuenta y el NIP de un objeto Cuenta en la base de datos. Colocaremos después de estos nombres de parámetros la palabra Usuario, para evitar confusión entre los nom- bres de los parámetros de la operación y los nombres de los atributos que pertenecen a la clase Cuenta. Listamos estos parámetros en el diagrama de clases de la figura 6.22, el cual modela sólo a la clase BaseDatosBanco. [Nota: es perfectamente normal modelar sólo una clase en un diagrama de clases. En este caso lo que más nos preocu- pa es analizar los parámetros de esta clase específica, por lo que omitimos las demás clases. Más adelante en los diagramas de clase de este ejemplo práctico, en donde los parámetros dejarán de ser el centro de nuestra atención, los omitiremos para ahorrar espacio. No obstante, recuerde que las operaciones que se listan en estos diagramas siguen teniendo parámetros]. Recuerde que para modelar a cada parámetro en una lista de parámetros separados por comas, UML lista el nombre del parámetro, seguido de un signo de dos puntos y el tipo del parámetro (en notación de UML). Así, la figura 6.22 especifica que la operación autenticarUsuario recibe dos parámetros: numeroCuentaUsuario y nipUsuario, ambos de tipo Integer. Cuando implementemos el sistema en Java, representaremos estos pará- metros con valores int. Las operaciones obtenerSaldoDisponible, obtenerSaldoTotal, abonar y cargar de la clase BaseDa- tosBanco también requieren un parámetro nombreCuentaUsuario para identificar la cuenta a la cual la base de datos debe aplicar las operaciones, por lo que incluimos estos parámetros en el diagrama de clases de la figura 244 Capítulo 6 Métodos: un análisis más detallado
  • 283. 6.22. Además, las operaciones abonar y cargar requieren un parámetro Double llamado monto, para especificar el monto de dinero que se abonará o cargará, respectivamente. El diagrama de clases de la figura 6.23 modela los parámetros de las operaciones de la clase Cuenta. La ope- ración validarNIP sólo requiere un parámetro nipUsuario, el cual contiene el NIP especificado por el usuario, que se comparará con el NIP asociado a la cuenta. Al igual que sus contrapartes en la clase BaseDatosBanco, las operaciones abonar y cargar en la clase Cuenta requieren un parámetro Double llamado monto, el cual indica la cantidad de dinero involucrada en la operación. Las operaciones obtenerSaldoDisponible y obtenerSal- doTotal en la clase Cuenta no requieren datos adicionales para realizar sus tareas. Observe que las operaciones de la clase Cuenta no requieren un parámetro de número de cuenta para diferenciar una cuenta de otra, ya que cada una de estas operaciones se puede invocar sólo en un objeto Cuenta específico. La figura 6.24 modela la clase Pantalla con un parámetro especificado para la operación mostrarMensaje. Esta operación requiere sólo un parámetro String llamado mensaje, el cual indica el texto que debe mostrarse en pantalla. Recuerde que los tipos de los parámetros que se enlistan en nuestros diagramas de clases están en nota- ción de UML, por lo que el tipo String que se enlista en la figura 6.24 se refiere al tipo de UML. Cuando imple- mentemos el sistema en Java, utilizaremos de hecho la clase String de Java para representar este parámetro. El diagrama de clases de la figura 6.25 especifica que la operación dispensarEfectivo de la clase Dispen- sadorEfectivo recibe un parámetro Double llamado monto para indicar el monto de efectivo (en dólares) que se dispensará al usuario. La operación haySuficienteEfectivoDisponible también recibe un parámetro Double llamado monto para indicar el monto de efectivo en cuestión. figura 6.22 | La clase BaseDatosBanco con parámetros de operación. BaseDatosBanco autenticarUsuario(nombreCuentaUsuario : Integer, nipUsuario : Integer) : Boolean obtenerSaldoDisponible(numeroCuentaUsuario : Integer) : Double obtenerSaldoTotal(numeroCuentaUsuario : Integer) : Double abonar(numeroCuentaUsuario : Integer, monto : Double) cargar(numeroCuentaUsuario : Integer, monto : Double) Figura 6.23 | La clase Cuenta con parámetros de operación. Cuenta numeroCuenta : Integer nip : Integer saldoDisponible : Double saldoTotal : Double validarNIP (nipUsuario : Integer) : Boolean obtenerSaldoDisponible() : Double obtenerSaldoTotal() : Double abonar(monto : Double) cargar(monto : Double) Figura 6.24 | La clase Pantalla con parámetros de operación. Pantalla mostrarMensaje( mensaje : String ) 6.14 (Opcional) Ejemplo práctico de Ingeniería de Software: identificación de las operaciones... 245
  • 284. Observe que no hablamos sobre los parámetros para la operación ejecutar de las clases SolicitudSaldo, Retiro y Depósito, de la operación obtenerEntrada de la clase Teclado y la operación seRecibioSobre de la clase RanuraDeposito. En este punto de nuestro proceso de diseño, no podemos determinar si estas operaciones requieren datos adicionales para realizar sus tareas, por lo que dejaremos sus listas de parámetros vacías. A medida que avancemos por el ejemplo práctico, tal vez decidamos agregar parámetros a estas operaciones. En esta sección hemos determinado muchas de las operaciones que realizan las clases en el sistema ATM. Identificamoslosparámetrosylostiposdevaloresderetornodealgunasoperaciones.Amedidaquecontinuemoscon nuestro proceso de diseño, el número de operaciones que pertenezcan a cada clase puede variar; podríamos descubrir que se necesitan nuevas operaciones o que ciertas operaciones actuales no son necesarias; y podría- mos determinar que algunas de las operaciones de nuestras clases necesitan parámetros adicionales y tipos de valores de retorno distintos. Ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 6.1 ¿Cuál de las siguientes opciones no es un comportamiento? a) Leer datos de un archivo. b) Imprimir los resultados. c) Imprimir texto. d) Obtener la entrada del usuario. 6.2 Si quisiera agregar al sistema ATM una operación que devuelva el atributo monto de la clase Retiro, ¿cómo y en dónde especificaría esta operación en el diagrama de clases de la figura 6.21? 6.3 Describa el significado del siguiente listado de operaciones, el cual podría aparecer en un diagrama de clases para el diseño orientado a objetos de una calculadora: sumar( x : Integer, y : Integer ) : Integer Respuestas a los ejercicios de autoevaluación del Ejemplo práctico de Ingeniería de Software 6.1 c. 6.2 Para especificar una operación que obtenga el atributo monto de la clase Retiro, se debe colocar el siguiente listado de operaciones en el (tercer) compartimiento de operaciones de la clase Retiro: obtenerMonto( ) : Double 6.3 Este listado de operaciones indica una operación llamada sumar, la cual recibe los enteros x y y como paráme- tros y devuelve un valor entero. 6.15 Conclusión En este capítulo aprendió más acerca de los detalles de la declaración de métodos. También conoció la diferencia entre los métodos static y los no static, y le mostramos cómo llamar a los métodos static, anteponiendo al nombre del método el nombre de la clase en la cual aparece, y el separador punto (.). Aprendió a utilizar el operador + para realizar concatenaciones de cadenas. Aprendió a declarar constantes con nombre, usando los tipos enum y las variables public final static. Vio cómo usar la clase Random para generar conjuntos de núme- ros aleatorios, que pueden usarse para simulaciones. También aprendió acerca del alcance de los campos y las variables locales en una clase. Por último, aprendió que varios métodos en una clase pueden sobrecargarse, al proporcionar métodos con el mismo nombre y distintas firmas. Dichos métodos pueden usarse para realizar las mismas tareas, o tareas similares, usando distintos tipos o distintos números de parámetros. Figura 6.25 | La clase DispensadorEfectivo con parámetros de operación. DispensadorEfectivo dispensarEfectivo( monto : Double ) haySuficienteEfectivoDisponible( monto : Double ) : Boolean cuenta : Integer = 500 246 Capítulo 6 Métodos: un análisis más detallado
  • 285. En el capítulo 7 aprenderá a mantener listas y tablas de datos en arreglos. Verá una implementación más elegante de la aplicación que tira un dado 6000 veces, y dos versiones mejoradas de nuestro ejemplo práctico LibroCalificaciones que estudió en los capítulos 3 a 5. También aprenderá cómo acceder a los argumentos de línea de comandos de una aplicación, los cuales se pasan al método main cuando una aplicación comienza su ejecución. Resumen Sección 6.1 Introducción • La experiencia ha demostrado que la mejor forma de desarrollar y mantener un programa extenso es construirlo a partir de piezas pequeñas y simples, o módulos. A esta técnica se le conoce como “divide y vencerás”. Sección 6.2 Módulos de programas en Java • Hay tres tipos de módulos en Java: métodos, clases y paquetes. Los métodos se declaran dentro de las clases. Por lo general, las clases se agrupan en paquetes para que puedan importarse en los programas y reutilizarse. • Los métodos nos permiten dividir un programa en módulos, al separar sus tareas en unidades autocontenidas. Las instrucciones en un método se escriben sólo una vez, y se ocultan de los demás métodos. • Utilizar los métodos existentes como bloques de construcción para crear nuevos programas es una forma de reutili- zación del software, que nos permite evitar repetir código dentro de un programa. Sección 6.3 Métodos static, campos static y la clase Math • Una llamada a un método especifica el nombre del método a llamar y proporciona los argumentos que el método al que se llamó requiere para realizar su tarea. Cuando termina la llamada al método, éste devuelve un resultado o simplemente devuelve el control al método que lo llamó. • Una clase puede contener métodos static para realizar tareas comunes que no requieren un objeto de la clase. Cualquier información que pueda requerir un método static para realizar sus tareas se le puede enviar en forma de argumentos, en una llamada al método. Para llamar a un método static, se especifica el nombre de la clase en la cual está declarado el método, seguido de un punto (.) y del nombre del método, como en NombreClase.nombreMétodo( argumentos ) • Los argumentos para los métodos pueden ser constantes, variables o expresiones. • La clase Math cuenta con métodos static para realizar cálculos matemáticos comunes; además, declara dos campos que representan constantes matemáticas de uso común: Math.PI y Math.E. La constante Math.PI (3.14159265358979323846) es la relación entre la circunferencia de un círculo y su diámetro. La constante Math.E (2.7182818284590452354) es el valor de la base para los logaritmos naturales (que se calculan con el método sta- tic Math log). • Math.PI y Math.E se declaran con los modificadores public, final y static. Al hacerlos public, otros progra- madores pueden usar estos campos en sus propias clases. Cualquier campo declarado con la palabra clave final es constante; su valor no se puede modificar una vez que se inicializa el campo. Tanto PI como E se declaran final, ya que sus valores nunca cambian. Al hacer a estos campos static, se puede acceder a ellos a través del nombre de la clase Math y un separador punto (.), justo igual que con los métodos de la clase Math. • Cuando se crean objetos de una clase que contiene campos static (variables de clase), todos los objetos de esa clase comparten una copia de los campos static. En conjunto, las variables de clase y las variables de instancia de la clase representan sus campos. En la sección 8.11 aprenderá más acerca de los campos static. • Al ejecutar la Máquina Virtual de Java (JVM) con el comando java, la JVM trata de invocar al método main de la clase que usted le especifique. La JVM carga la clase especificada por NombreClase y utiliza el nombre de esa clase para invocar al método main. Puede especificar una lista opcional de objetos String (separados por espacios) como argumentos de línea de comandos, que la JVM pasará a su aplicación. • Puede colocar un método main en cualquier clase que declare; sólo se llamará al método main en la clase que usted utilice para ejecutar la aplicación. Algunos programadores aprovechan esto para crear un pequeño programa de prueba en cada clase que declaran. Resumen 247
  • 286. 248 Capítulo 6 Métodos: un análisis más detallado Sección 6.4 Declaración de métodos con múltiples parámetros • Cuando se hace una llamada a un método, el programa crea una copia de los valores de los argumentos del método y los asigna a los parámetros correspondientes del mismo, que se crean e inicializan cuando se hace la llamada al método. Cuando el control del programa regresa al punto en el que se hizo la llamada al método, los parámetros del mismo se eliminan de la memoria. • Un método puede devolver a lo más un valor, pero el valor devuelto podría ser una referencia a un objeto que con- tenga muchos valores. • Las variables deben declararse como campos de una clase, sólo si se requieren para usarlos en más de un método de la clase, o si el programa debe guardar sus valores entre distintas llamadas a los métodos de la clase. Sección 6.5 Notas acerca de cómo declarar y utilizar los métodos • Hay tres formas de llamar a un método: usar el nombre de un método por sí solo para llamar a otro método de la misma clase; usar una variable que contenga una referencia a un objeto, seguida de un punto (.) y del nombre del método, para llamar a un método del objeto al que se hace referencia; y usar el nombre de la clase y un punto (.) para llamar a un método static de una clase. • Hay tres formas de devolver el control a una instrucción que llama a un método. Si el método no devuelve un resul- tado, el control regresa cuando el flujo del programa llega a la llave derecha de terminación del método, o cuando se ejecuta la instrucción return; si el método devuelve un resultado, la instrucción return expresión; evalúa la expresión, y después regresa de inmediato el valor resultante al método que hizo la llamada. • Cuando un método tiene más de un parámetro, los parámetros se especifican como una lista separada por comas. Debe haber un argumento en la llamada al método para cada parámetro en su declaración. Además, cada argumen- to debe ser consistente con el tipo del parámetro correspondiente. Si un método no acepta argumentos, la lista de parámetros está vacía. • Los objetos String se pueden concatenar mediante el uso del operador +, que coloca los caracteres del operando derecho al final de los que están en el operando izquierdo. • Cada valor primitivo y objeto en Java tiene una representación String. Cuando se concatena un objeto con un String, el objeto se convierte en un String y después, los dos String se concatenan. • Para los valores primitivos que se utilizan en la concatenación de cadenas, la JVM maneja la conversión de los valores primitivos a objetos String. Si un valor boolean se concatena con un objeto String, se utiliza la palabra "true" o la palabra "false" para representar el valor boolean. Si hay ceros a la derecha en un valor de punto flotante, se descartan cuando el número se concatena a un objeto String. • Todos los objetos en Java tienen un método especial, llamado toString, el cual devuelve una representación String del contenido del objeto. Cuando se concatena un objeto con un String, la JVM llama de manera implícita al método toString del objeto, para obtener la representación String del mismo. • Cuando se escribe una literal String extensa en el código fuente de un programa, algunas veces los programadores dividen esa literal String en varias literales String más pequeñas, y las colocan en varias líneas de código para mejorar la legibilidad, y después vuelven a ensamblar las literales String mediante la concatenación. Sección 6.6 Pila de llamadas a los métodos y registros de activación • Las pilas se conocen como estructuras de datos tipo “último en entrar, primero en salir (UEPS)”; el último elemento que se mete (inserta) en la pila es el primer elemento que se saca (extrae) de ella. • Un método al que se llama debe saber cómo regresar al método que lo llamó, por lo que la dirección de retorno del método que hace la llamada se mete en la pila de ejecución del programa cuando se llama al método. Si ocurre una serie de llamadas a métodos, las direcciones de retorno sucesivas se meten en la pila, en el orden último en entrar, primero en salir, de manera que el último método en ejecutarse sea el primero en regresar al método que lo llamó. • La pila de ejecución del programa contiene la memoria para las variables locales que se utilizan en cada invocación de un método, durante la ejecución de un programa. Este dato se conoce como el registro de activación, o marco de pila, de la llamada al método. Cuando se hace una llamada a un método, el registro de activación para la llamada a ese método se mete en la pila de ejecución del programa. Cuando el método regresa al método que lo llamó, el registro de activación para esta llamada al método se saca de la pila, y esas variables locales ya no son conocidas para el programa. Si una variable local que contiene una referencia a un objeto es la única variable en el programa con una
  • 287. Resumen 249 referencia a ese objeto, cuando el registro de activación que contiene esa variable local se saca de la pila, el programa ya no puede acceder al objeto y, en un momento dado, la JVM lo eliminará de la memoria durante la “recolección de basura”. • La cantidad de memoria en una computadora es finita, por lo que sólo puede utilizarse cierta cantidad de memoria para almacenar registros de activación en la pila de ejecución del programa. Si hay más llamadas a métodos de las que se puedan almacenar en sus registros de activación en la pila de ejecución del programa, se produce un error conocido como desbordamiento de pila. La aplicación se compilará correctamente, pero su ejecución producirá un desbordamiento de pila. Sección 6.7 Promoción y conversión de argumentos • Una característica importante de las llamadas a métodos es la promoción de argumentos: convertir el valor de un argumento al tipo que el método espera recibir en su parámetro correspondiente. • Hay un conjunto de reglas de promoción que se aplican a las expresiones que contienen valores de dos o más tipos primitivos, y a los valores de tipos primitivos que se pasan como argumentos para los métodos. Cada valor se pro- mueve al tipo “más alto” en la expresión. En casos en los que se puede perder información debido a la conversión, el compilador de Java requiere que utilicemos un operador de conversión de tipos para obligar explícitamente a que ocurra la conversión. Sección 6.9 Ejemplo práctico: generación de números aleatorios • Los objetos de la clase Random (paquete java.util) pueden producir valores int, long, float o double. El método random de Math puede producir valores double en el rango 0.0 ≤ x < 1.0, en donde x es el valor devuelto por el método random. • El método nextInt de Random genera un valor int aleatorio en el rango de –2,147,483,648 a +2,147,483,647. Los valores devueltos por nextInt son en realidad números seudoaleatorios: una secuencia de valores producidos por un cálculo matemático complejo. Ese cálculo utiliza la hora actual del día para sembrar el generador de números aleatorios, de tal forma que cada ejecución del programa produzca una secuencia diferente de valores aleatorios. • La clase Random cuenta con otra versión del método nextInt, la cual recibe un argumento int y devuelve un valor desde 0 hasta el valor del argumento (pero sin incluirlo). • Los números aleatorios en un rango pueden generarse mediante numero = valorDesplazamiento + numerosAleatorios.nextInt( factorEscala ); en donde valorDesplazamiento especifica el primer número en el rango deseado de enteros consecutivos, y factorEs- cala especifica cuántos números hay en el rango. • Los números aleatorios pueden elegirse a partir de rangos de enteros no consecutivos, como en numero = valorDesplazamiento + diferenciaEntreValores * numerosAleatorios.nextInt( factorEscala ); en donde valorDesplazamiento especifica el primer número en el rango de valores, diferenciaEntreValores representa la diferencia entre números consecutivos en la secuencia y factorEscala especifica cuántos números hay en el rango. • Para depurar, algunas veces es conveniente repetir la misma secuencia de números seudoaleatorios durante cada ejecución del programa, para demostrar que su aplicación funciona para una secuencia específica de números aleato- rios, antes de probar el programa con distintas secuencias de números aleatorios. Cuando la repetitividad es impor- tante, puede crear un objeto Random al pasar un valor entero long al constructor. Si se utiliza la misma semilla cada vez que se ejecuta el programa, el objeto Random produce la misma secuencia de números aleatorios. También puede establecer la semilla de un objeto Random en cualquier momento, llamando al método setSeed del objeto. Sección 6.10 Ejemplo práctico: un juego de probabilidad (introducción a las enumeraciones) • Una enumeración se introduce mediante la palabra clave enum y el nombre de un tipo. Al igual que con cualquier clase, las llaves ({ y }) delimitan el cuerpo de una declaración enum. Dentro de las llaves hay una lista separada por comas de constantes de enumeración, cada una de las cuales representa un valor único. Los identificadores en una enum deben ser únicos. A las variables de tipo enum sólo se les pueden asignar constantes de ese tipo enum. • Las constantes también pueden declararse como variables public final static. Dichas constantes se declaran todas con letras mayúsculas por convención, para hacer que resalten en el programa. Sección 6.11 Alcance de las declaraciones • El alcance es la porción del programa en la que se puede hacer referencia a una entidad, como una variable o un método, por su nombre. Se dice que dicha entidad está “dentro del alcance” para esa porción del programa.
  • 288. 250 Capítulo 6 Métodos: un análisis más detallado • El alcance de la declaración de un parámetro es el cuerpo del método en el que aparece esa declaración. • El alcance de la declaración de una variable local es a partir del punto en el que aparece la declaración, hasta el final de ese bloque. • El alcance de una etiqueta en una instrucción break o continue etiquetada es el cuerpo de la instrucción etique- tada. • El alcance de la declaración de una variable local que aparece en la sección de inicialización del encabezado de una instrucción for es el cuerpo de la instrucción for, junto con las demás expresiones en el encabezado. • El alcance de un método o campo de una clase es todo el cuerpo de la clase. Esto permite que los métodos de una clase utilicen nombres simples para llamar a los demás métodos de la clase y acceder a los campos de la misma. • Cualquier bloque puede contener declaraciones de variables. Si una variable local o parámetro en un método tiene el mismo nombre que un campo, éste se oculta hasta que el bloque termina de ejecutarse. Sección 6.12 Sobrecarga de métodos • Java permite que se declaren varios métodos con el mismo nombre en una clase, siempre y cuando los métodos tengan distintos conjuntos de parámetros (lo cual se determina en base al número, orden y tipos de los parámetros). A esta técnica se le conoce como sobrecarga de métodos. • Los métodos sobrecargados se distinguen por sus firmas: combinaciones de los nombres de los métodos y el número, tipos y orden de sus parámetros. Los métodos no pueden distinguirse en base al tipo de valor de retorno. Terminología alcance de una declaración argumento de línea de comandos bloque campos “ocultos” Color, clase componentes de software reutilizables concatenación de cadenas constante de enumeración declaración de un método desbordamiento de pila desplazar un rango (números aleatorios) dividir en módulos un programa con métodos documentación de la API de Java elemento de probabilidad enum, palabra clave enumeración extraer (de una pila) factor de escala (números aleatorios) fillOval, método de la clase Graphics fillRect, método de la clase Graphics final, palabra clave firma de un método función insertar (en una pila) interfaz de programación de aplicaciones (API) Interfaz de programación de aplicaciones de Java (API) invocar a un método lista de parámetros lista de parámetros separados por comas llamada a método marco de pila método “divide y vencerás” método de clase método declarado por el programador módulo nextInt, método de la clase Random número seudoaleatorio números aleatorios ocultar los detalles de implementación ocultar un campo paquete parámetro parámetro formal pila pila de ejecución del programa pila de llamadas a métodos procedimiento promoción de argumentos promociones de tipos primitivos random de la clase Math Random, clase registro de activación reglas de promoción relación jerárquica método jefe/método trabajador return, palabra clave reutilización de software setColor, método de la clase Graphics setSeed, método de la clase Random simulación sobrecarga de métodos sobrecargar un método último en entrar, primero en salir (UEPS), estructura de datos valor de desplazamiento (números aleatorios) valor de semilla (números aleatorios) valores RGB variable de clase variable local
  • 289. Ejercicios de autoevaluación 6.1 Complete las siguientes oraciones: a) Un método se invoca con un _________________. b) A una variable que se conoce sólo dentro del método en el que está declarada, se le llama ____________. c) La instrucción _________________ en un método llamado puede usarse para regresar el valor de una expresión, al método que hizo la llamada. d) La palabra clave _________________ indica que un método no devuelve ningún valor. e) Los datos pueden agregarse o eliminarse sólo desde _________________ de una pila. f) Las pilas se conocen como estructuras de datos_________________: el último elemento que se mete (inserta) en la pila es el primer elemento que se saca (extrae) de ella. g) Las tres formas de regresar el control de un método llamado a un solicitante son _________________, _________________ y _________________. h) Un objeto de la clase _________________ produce números aleatorios. i) La pila de ejecución del programa contiene la memoria para las variables locales en cada invocación de un método, durante la ejecución de un programa. Estos datos, almacenados como una parte de la pila de ejecución del programa, se conocen como _________________ o _________________ de la llamada al método. j) Si hay más llamadas a métodos de las que puedan almacenarse en la pila de ejecución del programa, se produce un error conocido como _________________. k) El _________________ de una declaración es la porción del programa que puede hacer referencia a la entidad en la declaración, por su nombre. l) En Java, es posible tener varios métodos con el mismo nombre, en donde cada uno opere con distintos tipos o números de argumentos. A esta característica se le llama _________________ de métodos. m) La pila de ejecución del programa también se conoce como la pila de _________________. 6.2 Para la clase Craps de la figura 6.9, indique el alcance de cada una de las siguientes entidades: a) la variable numerosAleatorios. b) la variable dado1. c) el método tirarDado. d) el método jugar. e) la variable sumaDeDados. 6.3 Escriba una aplicación que pruebe si los ejemplos de las llamadas a los métodos de la clase Math que se muestran en la figura 6.2 realmente producen los resultados indicados. 6.4 Proporcione el encabezado para cada uno de los siguientes métodos: a) El método hipotenusa, que toma dos argumentos de punto flotante con doble precisión, llamados lado1 y lado2, y que devuelve un resultado de punto flotante, con doble precisión. b) El método menor, que toma tres enteros x, y y z, y devuelve un entero. c) El método instrucciones, que no toma argumentos y no devuelve ningún valor. (Nota: estos métodos se utilizan comúnmente para mostrar instrucciones a un usuario). d) El método intAFloat, que toma un argumento entero llamado numero y devuelve un resultado de punto flotante. 6.5 Encuentre el error en cada uno de los siguientes segmentos de programas. Explique cómo se puede corregir el error. a) int g() { System.out.println( "Dentro del metodo g" ); int h() { System.out.println( "Dentro del método h" ); } } Ejercicios de autoevaluación 251
  • 290. 252 Capítulo 6 Métodos: un análisis más detallado b) int suma( int x, int y ) { int resultado; resultado = x + y; } c) voit f( float a ); { float a; System.out.println( a ); } d) voit producto() { int a = 6, b = 5, c = 4, resultado; resultado = a * b * c; System.out.printf( "El resultado es %dn", resultado ); return resultado; } 6.6 Escriba una aplicación completa en Java que pida al usuario el radio de tipo double de una esfera, y que llame al método volumenEsfera para calcular y mostrar el volumen de esa esfera. Utilice la siguiente asignación para calcular el volumen: double volumen = ( 4.0 / 3.0 ) * Math.PI * Math.pow( radio, 3 ) Respuestas a los ejercicios de autoevaluación 6.1 a) llamada a un método. b) variable local. c) return. d) void. e) cima. f) último en entrar, primero en salir (UEPS). g) return; o return expresión; o encontrar la llave derecha de cierre de un método. h) Random. i) registro de activación. j) desbordamiento de pila. k) alcance. l) sobrecarga de métodos. m) llamadas a métodos. 6.2 a) el cuerpo de la clase. b) el bloque que define el cuerpo del método tirarDado. c) el cuerpo de la clase. d) el cuerpo de la clase. e) el bloque que define el cuerpo del método jugar. 6.3 La siguiente solución demuestra el uso de los métodos de la clase Math de la figura 6.2: 1 // Ejercicio 6.3: PruebaMath.java 2 // Prueba de los métodos de la clase Math. 3 4 public class PruebaMath 5 { 6 public static void main( String args[] ) 7 { 8 System.out.printf( "Math.abs( 23.7 ) = %fn", Math.abs( 23.7 ) ); 9 System.out.printf( "Math.abs( 0.0 ) = %fn", Math.abs( 0.0 ) ); 10 System.out.printf( "Math.abs( -23.7 ) = %fn", Math.abs( -23.7 ) ); 11 System.out.printf( "Math.ceil( 9.2 ) = %fn", Math.ceil( 9.2 ) ); 12 System.out.printf( "Math.ceil( -9.8 ) = %fn", Math.ceil( -9.8 ) ); 13 System.out.printf( "Math.cos( 0.0 ) = %fn", Math.cos( 0.0 ) ); 14 System.out.printf( "Math.exp( 1.0 ) = %fn", Math.exp( 1.0 ) ); 15 System.out.printf( "Math.exp( 2.0 ) = %fn", Math.exp( 2.0 ) ); 16 System.out.printf( "Math.floor( 9.2 ) = %fn", Math.floor( 9.2 ) ); 17 System.out.printf( "Math.floor( -9.8 ) = %fn", 18 Math.floor( -9.8 ) ); 19 System.out.printf( "Math.log( Math.E ) = %fn", 20 Math.log( Math.E ) ); 21 System.out.printf( "Math.log( Math.E * Math.E ) = %fn", 22 Math.log( Math.E * Math.E ) );
  • 291. 6.4 a) double hipotenusa( double lado1, double lado2 ) b) int menor( int x, int y, int z ) c) void instrucciones() d) float intAFloat( int numero ) 6.5 a) Error: el método h está declarado dentro del método g. Corrección: mueva la declaración de h fuera de la declaración de g. b) Error: se supone que el método debe devolver un entero, pero no es así. Corrección: elimine la variable resultado, y coloque la instrucción return x + y; en el método, o agregue la siguiente instrucción al final del cuerpo del método: return resultado; c) Error: el punto y coma que va después del paréntesis derecho de la lista de parámetros es incorrecto, y el parámetro a no debe volver a declararse en el método. Corrección: elimine el punto y coma que va después del paréntesis derecho de la lista de parámetros, y elimine la declaración float a;. Math.abs( 23.7 ) = 23.700000 Math.abs( 0.0 ) = 0.000000 Math.abs( -23.7 ) = 23.700000 Math.ceil( 9.2 ) = 10.000000 Math.ceil( -9.8 ) = -9.000000 Math.cos( 0.0 ) = 1.000000 Math.exp( 1.0 ) = 2.718282 Math.exp( 2.0 ) = 7.389056 Math.floor( 9.2 ) = 9.000000 Math.floor( -9.8 ) = -10.000000 Math.log( Math.E ) = 1.000000 Math.log( Math.E * Math.E ) = 2.000000 Math.max( 2.3, 12.7 ) = 12.700000 Math.max( -2.3, -12.7 ) = -2.300000 Math.min( 2.3, 12.7 ) = 2.300000 Math.min( -2.3, -12.7 ) = -12.700000 Math.pow( 2.0, 7.0 ) = 128.000000 Math.pow( 9.0, 0.5 ) = 3.000000 Math.sin( 0.0 ) = 0.000000 Math.sqrt( 900.0 ) = 30.000000 Math.sqrt( 9.0 ) = 3.000000 Math.tan( 0.0 ) = 0.000000 23 System.out.printf( "Math.max( 2.3, 12.7 ) = %fn", 24 Math.max( 2.3, 12.7 ) ); 25 System.out.printf( "Math.max( -2.3, -12.7 ) = %fn", 26 Math.max( -2.3, -12.7 ) ); 27 System.out.printf( "Math.min( 2.3, 12.7 ) = %fn", 28 Math.min( 2.3, 12.7 ) ); 29 System.out.printf( "Math.min( -2.3, -12.7 ) = %fn", 30 Math.min( -2.3, -12.7 ) ); 31 System.out.printf( "Math.pow( 2.0, 7.0 ) = %fn", 32 Math.pow( 2.0, 7.0 ) ); 33 System.out.printf( "Math.pow( 9.0, 0.5 ) = %fn", 34 Math.pow( 9.0, 0.5 ) ); 35 System.out.printf( "Math.sin( 0.0 ) = %fn", Math.sin( 0.0 ) ); 36 System.out.printf( "Math.sqrt( 900.0 ) = %fn", 37 Math.sqrt( 900.0 ) ); 38 System.out.printf( "Math.sqrt( 9.0 ) = %fn", Math.sqrt( 9.0 ) ); 39 System.out.printf( "Math.tan( 0.0 ) = %fn", Math.tan( 0.0 ) ); 40 } // fin de main 41 } // fin de la clase PruebaMath Ejercicios de autoevaluación 253
  • 292. 254 Capítulo 6 Métodos: un análisis más detallado d) Error: el método devuelve un valor cuando no debe hacerlo. Corrección: cambie el tipo de valor de retorno de void a int. 6.6 La siguiente solución calcula el volumen de una esfera, utilizando el radio introducido por el usuario: Escriba el radio de la esfera: 4 El volumen es 268.082573 1 // Ejercicio 6.6: Esfera.java 2 // Calcula el volumen de una esfera. 3 import java.util.Scanner; 4 5 public class Esfera 6 { 7 // obtiene el radio del usuario y muestra el volumen de la esfera 8 public void determinarVolumenEsfera() 9 { 10 Scanner entrada = new Scanner( System.in ); 11 12 System.out.print( "Escriba el radio de la esfera: " ); 13 double radio = entrada.nextDouble(); 14 15 System.out.printf( "El volumen es %fn", volumenEsfera( radio ) ); 16 } // fin del método determinarVolumenEsfera 17 18 // calcula y devuelve el volumen de una esfera 19 public double volumenEsfera( double radio ) 20 { 21 double volumen = ( 4.0 / 3.0 ) * Math.PI * Math.pow( radio, 3 ); 22 return volumen; 23 } // fin del método volumenEsfera 24 } // fin de la clase Esfera 1 // Ejercicio 6.6: PruebaEsfera.java 2 // Calcula el volumen de una esfera. 3 4 public class PruebaEsfera 5 { 6 // punto de inicio de la aplicación 7 public static void main( String args[] ) 8 { 9 Esfera miEsfera = new Esfera(); 10 miEsfera.determinarVolumenEsfera(); 11 } // fin de main 12 } // fin de la clase PruebaEsfera Ejercicios 6.7 ¿Cuál es el valor de x después de que se ejecuta cada una de las siguientes instrucciones? a) x = Math.abs( 7.5 ); b) x = Math.floor( 7.5 ); c) x = Math.abs( 0.0 ); d) x = Math.ceil( 0.0 ); e) x = Math.abs( -6.4 ); f) x = Math.ceil( -6.4 ); g) x = Math.ceil( -Math.abs( -8 + Math.floor( -5.5 ) ) );
  • 293. 6.8 Un estacionamiento cobra una cuota mínima de $2.00 por estacionarse hasta tres horas. El estacionamiento cobra $0.50 adicionales por cada hora o fracción que se pase de tres horas. El cargo máximo para cualquier periodo dado de 24 horas es de $10.00. Suponga que ningún automóvil se estaciona durante más de 24 horas a la vez. Escriba una aplicación que calcule y muestre los cargos por estacionamiento para cada cliente que se haya estacionado ayer. Debe introducir las horas de estacionamiento para cada cliente. El programa debe mostrar el cargo para el cliente actual y debe calcular y mostrar el total corriente de los recibos de ayer. El programa debe utilizar el método calcularCargos para determinar el cargo para cada cliente. 6.9 Una aplicación del método Math.floor es redondear un valor al siguiente entero. La instrucción y = Math.floor( x + 0.5 ); redondea el número x al entero más cercano y asigna el resultado a y. Escriba una aplicación que lea valores double y que utilice la instrucción anterior para redondear cada uno de los números a su entero más cercano. Para cada número procesado, muestre tanto el número original como el redondeado. 6.10 Math.floor puede utilizarse para redondear un número hasta un lugar decimal específico. La instrucción y = Math.floor( x * 10 + 0.5 ) / 10; redondea x en la posición de las décimas (es decir, la primera posición a la derecha del punto decimal). La instrucción y = Math.floor( x * 100 + 0.5 ) / 100; redondea x en la posición de las centésimas (es decir, la segunda posición a la derecha del punto decimal). Escriba una aplicación que defina cuatro métodos para redondear un número x en varias formas: a) redondearAInteger( numero ) b) redondearADecimas( numero ) c) redondearACentesimas( numero ) d) redondearAMilesimas( numero ) Para cada valor leído, su programa debe mostrar el valor original, el número redondeado al entero más cercano, el número redondeado a la décima más cercana, el número redondeado a la centésima más cercana y el número redon- deado a la milésima más cercana. 6.11 Responda a cada una de las siguientes preguntas: a) ¿Qué significa elegir números “al azar”? b) ¿Por qué es el método nextInt de la clase Random útil para simular juegos al azar? c) ¿Por qué es a menudo necesario escalar o desplazar los valores producidos por un objeto Random? d) ¿Por qué es la simulación computarizada de las situaciones reales una técnica útil? 6.12 Escriba instrucciones que asignen enteros aleatorios a la variable n en los siguientes rangos: a) 1 ≤ n ≤ 2. b) 1 ≤ n ≤ 100. c) 0 ≤ n ≤ 9. d) 1000 ≤ n ≤ 1112. e) –1 ≤ n ≤ 1. f) –3 ≤ n ≤ 11. 6.13 Para cada uno de los siguientes conjuntos de enteros, escriba una sola instrucción que imprima un número al azar del conjunto: a) 2, 4, 6, 8, 10. b) 3, 5, 7, 9, 11. c) 6, 10, 14, 18, 22. 6.14 Escriba un método llamado enteroPotencia( base, exponente ) que devuelva el valor de baseexponente Por ejemplo, enteroPotencia( 3, 4 ) calcula 34 (o 3 * 3 * 3 * 3 ). Suponga que exponente es un entero positivo distinto de cero y que base es un entero. El método enteroPotencia debe utilizar un ciclo for o while para controlar el cálculo. No utilice ningún método de la biblioteca de matemáticas. Incorpore este método en una aplicación que lea valores enteros para base y exponente, y que realice el cálculo con el método enteroPotencia. Ejercicios 255
  • 294. 256 Capítulo 6 Métodos: un análisis más detallado 6.15 Defina un método llamado hipotenusa que calcule la longitud de la hipotenusa de un triángulo rectángulo, cuando se proporcionen las longitudes de los otros dos lados. (Utilice los datos de ejemplo de la figura 6.26.) El método debe tomar dos argumentos de tipo double y devolver la hipotenusa como un valor double. Incorpore este método en una aplicación que lea los valores para lado1 y lado2, y que realice el cálculo con el método hipotenusa. Determine la longitud de la hipotenusa para cada uno de los triángulos de la figura 6.26. 6.16 Escriba un método llamado multiplo que determine, para un par de enteros, si el segundo entero es múltiplo del primero. El método debe tomar dos argumentos enteros y devolver true si el segundo es múltiplo del primero, y false en caso contrario. [Sugerencia: utilice el operador residuo]. Incorpore este método en una aplicación que reciba como entrada una serie de pares de enteros (un par a la vez) y determine si el segundo valor en cada par es un múltiplo del primero. 6.17 Escriba un método llamado esPar que utilice el operador residuo (%) para determinar si un entero dado es par. El método debe tomar un argumento entero y devolver true si el entero es par, y false en caso contrario. Incorpore este método en una aplicación que reciba como entrada una secuencia de enteros (uno a la vez), y que determine si cada uno es par o impar. 6.18 Escriba un método llamado cuadradoDeAsteriscos que muestre un cuadrado relleno (el mismo número de filas y columnas) de asteriscos cuyo lado se especifique en el parámetro entero lado. Por ejemplo, si lado es 4, el método debe mostrar: **** **** **** **** Incorpore este método a una aplicación que lea un valor entero para el parámetro lado que teclea el usuario, y desplie- gue los asteriscos con el método cuadradoDeAsteriscos. 6.19 Modifique el método creado en el ejercicio 6.18 para formar el cuadrado de cualquier carácter que esté conte- nido en el parámetro tipo carácter caracterRelleno. Por ejemplo, si lado es 5 y caracterRelleno es “#”, el método debe imprimir #### #### #### #### #### 6.20 Escriba una aplicación que pida al usuario el radio de un círculo y que utilice un método llamado circuloArea para calcular e imprimir el área de ese círculo. 6.21 Escriba segmentos de programas que realicen cada una de las siguientes tareas: a) Calcular la parte entera del cociente, cuando el entero a se divide entre el entero b. b) Calcular el residuo entero cuando el entero a se divide entre el entero b. c) Utilizar las piezas de los programas desarrollados en las partes (a) y (b) para escribir un método llamado mostrarDigitos, que reciba un entero entre 1 y 99999, y que lo muestre como una secuencia de dígitos, separando cada par de dígitos por dos espacios. Por ejemplo, el entero 4562 debe aparecer como 4 5 6 2 d) Incorpore el método desarrollado en la parte (c) en una aplicación que reciba como entrada un entero y que llame al método mostrarDigitos, pasándole a este método el entero introducido. Muestre los resultados. Triángulo Lado 1 Lado 2 1 3.0 4.0 2 5.0 12.0 3 8.0 15.0 figura 6.26 | Valores para los lados de los triángulos del ejercicio 6.15.
  • 295. 6.22 Implemente los siguientes métodos enteros: a) El método centigrados que devuelve la equivalencia en grados centígrados de una temperatura en grados fahrenheit, utilizando el cálculo centigrados = 5.0 / 9.0 * ( fahrenheit – 32 ); b) El método fahrenheit que devuelve la equivalencia en grados fahrenheit de una temperatura en grados centígrados, utilizando el cálculo fahrenheit = 9.0 / 5.0 * centigrados + 32; c) Utilice los métodos de las partes (a) y (b) para escribir una aplicación que permita al usuario, ya sea escribir una temperatura en grados fahrenheit y mostrar su equivalente en grados centígrados, o escribir una tem- peratura en grados centígrados y mostrar su equivalente en grados fahrenheit. 6.23 Escriba un método llamado minimo3 que devuelva el menor de tres números de punto flotante. Use el método Math.min para implementar minimo3. Incorpore el método en una aplicación que reciba como entrada tres valores por parte del usuario, determine el valor menor y muestre el resultado. 6.24 Se dice que un número entero es un número perfecto si sus factores, incluyendo 1 (pero no el número entero), al sumarse dan como resultado el número entero. Por ejemplo, 6 es un número perfecto ya que 6 = 1 + 2 + 3. Escriba un método llamado perfecto que determine si el parámetro numero es un número perfecto. Use este método en una aplicación que determine y muestre todos los números perfectos entre 1 y 1000. Imprima los factores de cada número perfecto para confirmar que el número sea realmente perfecto. Ponga a prueba el poder de su computadora, evaluando números más grandes que 1000. Muestre los resultados. 6.25 Se dice que un entero es primo si puede dividirse solamente por 1 y por sí mismo. Por ejemplo, 2, 3, 5 y 7 son primos, pero 4, 6, 8 y 9 no. a) Escriba un método que determine si un número es primo. b) Use este método en una aplicación que determine e imprima todos los números primos menores que 10,000. ¿Cuántos números hasta 10,000 tiene que probar para asegurarse de encontrar todos los números primos? c) Al principio podría pensarse que n/2 es el límite superior para evaluar si un número es primo, pero lo máxi- mo que se necesita es ir hasta la raíz cuadrada de n. ¿Por qué? Vuelva a escribir el programa y ejecútelo de ambas formas. 6.26 Escriba un método que tome un valor entero y devuelva el número con sus dígitos invertidos. Por ejemplo, para el número 7631, el método debe regresar 1367. Incorpore el método en una aplicación que reciba como entrada un valor del usuario y muestre el resultado. 6.27 El máximo común divisor (MCD) de dos enteros es el entero más grande que puede dividir uniformemente a cada uno de los dos números. Escriba un método llamado mcd que devuelva el máximo común divisor de dos enteros. [Sugerencia: tal vez sea conveniente que utilice el algoritmo de Euclides. Puede encontrar información acerca de este algoritmo en es.wikipedia.org/wiki/Algoritmo_de_Euclides]. Incorpore el método en una aplicación que reciba como entrada dos valores del usuario y muestre el resultado. 6.28 Escriba un método llamado puntosCalidad que reciba como entrada el promedio de un estudiante y devuelva 4 si el promedio se encuentra entre 90 y 100, 3 si el promedio se encuentra entre 80 y 89, 2 si el promedio se encuentra entre 70 y 79, 1 si el promedio se encuentra entre 60 y 69, y 0 si el promedio es menor de 60. Incorpore el método en una aplicación que reciba como entrada un valor del usuario y muestre el resultado. 6.29 Escriba una aplicación que simule el lanzamiento de monedas. Deje que el programa lance una moneda cada vez que el usuario seleccione la opción del menú “Lanzar moneda”. Cuente el número de veces que aparezca cada uno de los lados de la moneda. Muestre los resultados. El programa debe llamar a un método separado, llamado tirar, que no tome argumentos y devuelva false en caso de cara, y true en caso de cruz. [Nota: si el programa simula en forma realista el lanzamiento de monedas, cada lado de la moneda debe aparecer aproximadamente la mitad del tiempo.] 6.30 Las computadoras están tomando un papel cada vez más importante en la educación. Escriba un programa que ayude a un estudiante de escuela primaria, para que aprenda a multiplicar. Use un objeto Random para producir dos enteros positivos de un dígito. El programa debe entonces mostrar una pregunta al usuario, como: ¿Cuánto es 6 por 7? Ejercicios 257
  • 296. 258 Capítulo 6 Métodos: un análisis más detallado El estudiante entonces debe escribir la respuesta. Luego, el programa debe verificar la respuesta del estudiante. Si es correcta, dibuje la cadena "Muy bien!" y haga otra pregunta de multiplicación. Si la respuesta es incorrecta, dibuje la cadena "No. Por favor intenta de nuevo." y deje que el estudiante intente la misma pregunta varias veces, hasta que esté correcta. Debe utilizarse un método separado para generar cada pregunta nueva. Este método debe llamarse una vez cuando la aplicación empiece a ejecutarse, y cada vez que el usuario responda correctamente a la pregunta. 6.31 El uso de las computadoras en la educación se conoce como instrucción asistida por computadora (CAI, por sus siglas en inglés). Un problema que se desarrolla en los entornos CAI es la fatiga de los estudiantes. Este problema puede eliminarse si se varía el diálogo de la computadora para mantener la atención del estudiante. Modifique el programa del ejercicio 6.30 de manera que los diversos comentarios se impriman para cada respuesta correcta e incorrecta, de la siguiente manera: Contestaciones a una respuesta correcta: Muy bien! Excelente! Buen trabajo! Sigue asi! Contestaciones a una respuesta incorrecta: No. Por favor intenta de nuevo. Incorrecto. Intenta una vez mas. No te rindas! No. Sigue intentando. Use la generación de números aleatorios para elegir un número entre 1 y 4 que se utilice para seleccionar una contestación apropiada a cada respuesta. Use una instrucción switch para emitir las contestaciones. 6.32 Los sistemas de instrucción asistida por computadora más sofisticados supervisan el rendimiento del estudiante durante cierto tiempo. La decisión de empezar un nuevo tema se basa a menudo en el éxito del estudiante con los temas anteriores. Modifique el programa del ejercicio 6.31 para contar el número de respuestas correctas e incorrectas por parte del estudiante. Una vez que el estudiante escriba 10 respuestas, su programa debe calcular el porcentaje de respuestas correctas. Si éste es menor del 75%, imprima Por favor pida ayuda adicional a su instructor y re- inicie el programa, para que otro estudiante pueda probarlo. 6.33 Escriba una aplicación que juegue a “adivina el número” de la siguiente manera: su programa elige el número a adivinar, seleccionando un entero aleatorio en el rango de 1 a 1000. La aplicación muestra el indicador Adivine un número entre 1 y 1000. El jugador escribe su primer intento. Si la respuesta del jugador es incorrecta, su programa debe mostrar el mensaje Demasiado alto. Intente de nuevo. o Demasiado bajo. Intente de nuevo., para ayudar a que el jugador “se acerque” a la respuesta correcta. El programa debe pedir al usuario que escriba su siguiente intento. Cuando el usuario escriba la respuesta correcta, muestre el mensaje Felicidades. Adivino el numero! y permita que el usuario elija si desea jugar otra vez. [Nota: la técnica para adivinar empleada en este problema es similar a una búsqueda binaria, que veremos en el capítulo 16, Búsqueda y ordenamiento]. 6.34 Modifique el programa del ejercicio 6.33 para contar el número de intentos que haga el jugador. Si el número es 10 o menos, imprima el mensaje O ya sabia usted el secreto, o tuvo suerte! Si el jugador adivina el número en 10 intentos, imprima el mensaje Aja! Sabía usted el secreto! Si el jugador hace más de 10 intentos, imprima el mensaje Deberia haberlo hecho mejor! ¿Por qué no se deben requerir más de 10 intentos? Bueno, en cada “buen intento”, el jugador debe poder eliminar la mitad de los números, después la mitad de los números restantes, y así en lo sucesivo. 6.35 En los ejercicios 6.30 al 6.32 se desarrolló un programa de instrucción asistida por computadora para enseñar a un estudiante de escuela primara cómo multiplicar. Realice las siguientes mejoras: a) Modifique el programa para que permita al usuario introducir un nivel de capacidad escolar. Un nivel de 1 significa que el programa debe usar sólo números de un dígito en los problemas, un nivel 2 significa que el programa debe utilizar números de dos dígitos máximo, etcétera. b) Modifique el programa para permitir al usuario que elija el tipo de problemas aritméticos que desea estu- diar. Una opción de 1 significa problemas de suma solamente, 2 significa problemas de resta, 3 significa pro- blemas de multiplicación, 4 significa problemas de división y 5 significa una mezcla aleatoria de problemas de todos estos tipos.
  • 297. 6.36 Escriba un método llamado distancia, para calcular la distancia entre dos puntos (x1, y1) y (x2, y2). Todos los números y valores de retorno deben ser de tipo double. Incorpore este método en una aplicación que permita al usuario introducir las coordenadas de los puntos. 6.37 Modifique el programa Craps de la figura 6.9 para permitir apuestas. Inicialice la variable saldoBanco con $1000. Pida al jugador que introduzca una apuesta. Compruebe que esa apuesta sea menor o igual al saldoBanco y, si no lo es, haga que el usuario vuelva a introducir la apuesta hasta que se introduzca un valor válido. Después de esto, comience un juego de craps. Si el jugador gana, agregue la apuesta al saldoBanco e imprima el nuevo saldoBanco. Si el jugador pierde, reste la apuesta al saldoBanco, imprima el nuevo saldoBanco, compruebe si saldoBanco se ha vuelto cero y, de ser así, imprima el mensaje "Lo siento. Se quedo sin fondos!" A medida que el juego progrese, imprima varios mensajes para crear algo de “charla”, como "Oh, se esta yendo a la quiebra, verdad?", o "Oh, vamos, arriesguese!", o "La hizo en grande. Ahora es tiempo de cambiar sus fichas por efectivo!". Implemente la “charla” como un método separado que seleccione en forma aleatoria la cadena a mostrar. 6.38 Escriba una aplicación que muestre una tabla de los equivalentes en binario, octal y hexadecimal de los números decimales en el rango de 1 al 256. Si no está familiarizado con estos sistemas numéricos, lea el apéndice E primero. Ejercicios 259
  • 298. Arreglos OBJETIVO S En este capítulo aprenderá a: Conocer qué son los arreglos. Utilizar arreglos para almacenar datos en, y obtenerlos de listas y tablas de valores. Declarar arreglos, inicializarlos y hacer referencia a elementos individuales de los arreglos. Utilizar la instrucción for mejorada para iterar a través de los arreglos. Pasar arreglos a los métodos. Declarar y manipular arreglos multidimensionales. Escribir métodos que utilicen listas de argumentos de longitud variable. Leer los argumentos de línea de comandos en un programa. Q Q Q Q Q Q Q Q Ahora ve, escríbelo ante ellos en una tabla, y anótalo en un libro. —Isaías 30:8 Ir más allá es tan malo como no llegar. —Confucio Comienza en el principio… y continúa hasta que llegues al final; después detente. —Lewis Carroll 7
  • 299. 7.1 Introducción En este capítulo presentamos el importante tema de las estructuras de datos: colecciones de elementos de datos relacionados. Los arreglos son estructuras de datos que consisten de elementos de datos relacionados, del mismo tipo. Los arreglos son entidades de longitud fija; conservan la misma longitud una vez creados, aunque puede reasignarse una variable tipo arreglo de tal forma que haga referencia a un nuevo arreglo de distinta longitud. En los capítulos 17 a 19 estudiaremos con detalle las estructuras de datos. Después de hablar acerca de cómo se declaran, crean y inicializan los arreglos, presentaremos una serie de ejemplos prácticos que demuestran varias manipulaciones comunes de los arreglos. También presentaremos un ejemplo práctico en el que se examina la forma en que los arreglos pueden ayudar a simular los procesos de barajar y repartir cartas, para utilizarlos en una aplicación que implementa un juego de cartas. Después presen- taremos la instrucción for mejorada de java, la cual permite que un programa acceda a los datos en un arreglo con más facilidad que la instrucción for controlada por contador, que presentamos en la sección 5.3. Hay dos secciones de este capítulo en las que se amplía el ejemplo práctico de la clase LibroCalificaciones de los capítu- los 3 a 5. En especial, utilizaremos los arreglos para permitir que la clase mantenga un conjunto de calificaciones en memoria y analizar las calificaciones que obtuvieron los estudiantes en distintos exámenes en un semestre; dos herramientas que no están presentes en las versiones anteriores de la clase. Éstos y otros ejemplos del capítulo demostrarán las formas en las que los arreglos permiten a los programadores organizar y manipular datos. 7.2 Arreglos En Java, un arreglo es un grupo de variables (llamadas elementos o componentes) que contienen valores, todos del mismo tipo. Recuerde que los tipos en Java se dividen en dos categorías: tipos primitivos y tipos de referen- cia. Los arreglos son objetos, por lo que se consideran como tipos de referencia. Como veremos pronto, lo que consideramos generalmente como un arreglo es en realidad una referencia a un objeto arreglo en memoria. Los elementos de un arreglo pueden ser tipos primitivos o de referencia (incluyendo arreglos, como veremos en la sección 7.9). Para hacer referencia a un elemento específico en un arreglo, debemos especificar el nombre de la referencia al arreglo y el número de la posición del elemento en el arreglo. El número de la posición del elemen- to se conoce formalmente como el índice o subíndice del elemento. En la figura 7.1 se muestra una representación lógica de un arreglo de enteros, llamado c. Este arreglo con- tiene 12 elementos. Un programa puede hacer referencia a cualquiera de estos elementos mediante una expresión de acceso a un arreglo que incluye el nombre del arreglo, seguido por el índice del elemento específico encerrado 7.1 Introducción 7.2 Arreglos 7.3 Declaración y creación de arreglos 7.4 Ejemplos acerca del uso de los arreglos 7.5 Ejemplo práctico: simulación para barajar y repartir cartas 7.6 Instrucción for mejorada 7.7 Paso de arreglos a los métodos 7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones 7.9 Arreglos multidimensionales 7.10 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo bidimensional 7.11 Listas de argumentos de longitud variable 7.12 Uso de argumentos de línea de comandos 7.13 (Opcional) Ejemplo práctico de GUI y gráficos: cómo dibujar arcos 7.14 (Opcional) Ejemplo práctico de Ingeniería de Software: colaboración entre los objetos 7.15 Conclusión Resumen | Terminología | Ejercicios de autoevaluación | Respuestas a los ejercicios de autoevaluación | Ejercicios | Sección especial: construya su propia computadora Pla n g e ne r a l 7.2 Arreglos 261
  • 300. 262 Capítulo 7 Arreglos Figura 7.1 | Un arreglo con 12 elementos. -45 62 -3 1 6453 78 0 -89 1543 72 0 6 c[ 0 ] Nombre del arreglo (c) Índice (o subíndice) del elemento en el arreglo c c[ 7 ] c[ 8 ] c[ 9 ] c[ 10 ] c[ 11 ] c[ 6 ] c[ 5 ] c[ 4 ] c[ 3 ] c[ 2 ] c[ 1 ] entre corchetes ([]). El primer elemento en cualquier arreglo tiene el índice cero, y algunas veces se le denomina elemento cero. Por lo tanto, los elementos del arreglo c son c[ 0 ], c[ 1 ], c[ 2 ], y así en lo sucesivo. El mayor índice en el arreglo c es 11: 1 menos que 12, el número de elementos en el arreglo. Los nombres de los arreglos siguen las mismas convenciones que los demás nombres de variables. Un índice debe ser un entero positivo. Un programa puede utilizar una expresión como índice. Por ejemplo, si suponemos que la variable a es 5 y que b es 6, entonces la instrucción c[ a + b ] += 2; suma 2 al elemento c[ 11 ] del arreglo. Observe que el nombre del arreglo con subíndice es una expresión de acceso al arreglo. Dichas expresiones pueden utilizarse en el lado izquierdo de una asignación, para colocar un nuevo valor en un elemento del arreglo. Error común de programación 7.1 Usar un valor de tipo long como índice de un arreglo produce un error de compilación. Un índice debe ser un valor int, o un valor de un tipo que pueda promoverse a int; a saber, byte, short o char, pero no long. Examinaremos el arreglo c de la figura 7.1 con más detalle. El nombre del arreglo es c. Cada instancia de un objeto conoce su propia longitud y mantiene esta información en un campo length. La expresión c.length accede al campo length del arreglo c para determinar la longitud del arreglo. Observe que, aun cuando el miem- bro length de un arreglo es public, no puede cambiarse, ya que es una variable final. La manera en que se hace referencia a los 12 elementos de este arreglo es: c[ 0 ], c[ 1 ], c[ 2 ], …, c[ 11 ]. El valor de c[ 0 ] es -45, el valor de c[ 1 ] es 6, el de c[ 2 ] es 0, el de c[ 7 ] es 62 y el de c[ 11 ] es 78. Para calcular la suma de los valores contenidos en los primeros tres elementos del arreglo c y almacenar el resultado en la variable suma, escribiríamos lo siguiente: suma = c[ 0 ] + c[ 1 ] + c[ 2 ]; Para dividir el valor de c[ 6 ] entre 2 y asignar el resultado a la variable x, escribiríamos lo siguiente: x = c[ 6 ] / 2; 7.3 Declaración y creación de arreglos Los objetos arreglo ocupan espacio en memoria. Al igual que los demás objetos, los arreglos se crean con la palabra clave new. Para crear un objeto arreglo, el programador especifica el tipo de cada elemento y el número
  • 301. 7.3 Declaración y creación de arreglos 263 de elementos que se requieren para el arreglo, como parte de una expresión para crear un arreglo que utiliza la palabra clave new. Dicha expresión devuelve una referencia que puede almacenarse en una variable tipo arreglo. La siguiente declaración y expresión crea un objeto arreglo, que contiene 12 elementos int, y almacena la refe- rencia del arreglo en la variable c: int c[] = new int[ 12 ]; Esta expresión puede usarse para crear el arreglo que se muestra en la figura 7.1. Esta tarea también puede reali- zarse en dos pasos, como se muestra a continuación: int c[ ]; // declara la variable arreglo c = new int[ 12 ]; // crea el arreglo; lo asigna a la variable tipo arreglo En la declaración, los corchetes que van después del nombre de la variable c indican que c es una variable que hará referencia a un arreglo de valores int (es decir, c almacenará una referencia a un objeto arreglo). En la instrucción de asignación, la variable arreglo c recibe la referencia a un nuevo objeto arreglo de 12 elementos int. Al crear un arreglo, cada uno de sus elementos recibe un valor predeterminado: cero para los elementos numéricos de tipos primitivos, false para los elementos boolean y null para las referencias (cualquier tipo no primitivo). Como pronto veremos, podemos proporcionar valores iniciales para los elementos no específicos ni predeterminados al crear un arreglo. Error común de programación 7.2 En la declaración de un arreglo, si se especifica el número de elementos en los corchetes de la declaración (por ejemplo, int c[ 12 ];) se produce un error de sintaxis. Un programa puede crear varios arreglos en una sola declaración. La siguiente declaración de un arreglo String reserva 100 elementos para b y 27 para x: String b[] = new String[ 100 ], x[] = new String[ 27 ]; En este caso, se aplica el nombre de la clase String a cada variable en la declaración. Por cuestión de legibilidad, es preferible declarar sólo una variable en cada declaración, como en: String b[] = new string[ 100 ]; // crea el arreglo b String x[] = new string[ 27 ]; // crea el arreglo x Buena práctica de programación 7.1 Por cuestión de legibilidad, declare sólo una variable en cada declaración. Mantenga cada declaración en una línea separada e incluya un comentario que describa a la variable que está declarando. Cuando se declara un arreglo, su tipo y los corchetes pueden combinarse al principio de la declaración para indicar que todos los identificadores en la declaración son variables tipo arreglo. Por ejemplo, la declaración double[] arreglo1, arreglo2; indica que arreglo1 y arreglo2 son variables tipo “arreglo de double”. La anterior declaración es equiva- lente a: double arreglo1[]; double arreglo2[]; o double[] arreglo1; double[] arreglo2; Los pares anteriores de declaraciones son equivalentes; cuando se declara sólo una variable en cada declaración, los corchetes pueden colocarse ya sea antes del tipo, o después del nombre de la variable tipo arreglo.
  • 302. 264 Capítulo 7 Arreglos Error común de programación 7.3 Declarar múltiples variables tipo arreglo en una sola declaración puede provocar errores sutiles. Considere la declara- ción int[] a, b, c;. Si a, b y c deben declararse como variables tipo arreglo, entonces esta declaración es correcta; al colocar corchetes directamente después del tipo, indicamos que todos los identificadores en la declaración son variables tipo arreglo. No obstante, si sólo a debe ser una variable tipo arreglo, y b y c deben ser variables int indi- viduales, entonces esta declaración es incorrecta; la declaración int a[], b, c; lograría el resultado deseado. Un programa puede declarar arreglos de cualquier tipo. Cada elemento de un arreglo de tipo primitivo contiene un valor del tipo declarado del arreglo. De manera similar, en un arreglo de un tipo de referencia, cada elemento es una referencia a un objeto del tipo declarado del arreglo. Por ejemplo, cada elemento de un arreglo int es un valor int, y cada elemento de un arreglo String es una referencia a un objeto String. 7.4 Ejemplos acerca del uso de los arreglos En esta sección presentaremos varios ejemplos que muestran cómo declarar, crear e inicializar arreglos y cómo manipular sus elementos. Cómo crear e inicializar un arreglo En la aplicación de la figura 7.2 se utiliza la palabra clave new para crear un arreglo de 10 elementos int, los cuales inicialmente tienen el valor cero (el valor predeterminado para las variables int). En la línea 8 se declara arreglo, una referencia capaz de referirse a un arreglo de elementos int. En la línea 10 se crea el objeto arreglo y se asigna su referencia a la variable arreglo. La línea 12 imprime los encabezados de las columnas. La primera columna representa el índice (0 a 9) para cada elemento del arreglo, y la segunda el valor predeterminado (0) de cada elemento del arreglo. 1 // Fig. 7.2: InicArreglo.java 2 // Creación de un arreglo. 3 4 public class InicArreglo 5 { 6 public static void main( String args[] ) 7 { 8 int arreglo[]; // declara un arreglo con el mismo nombre 9 10 arreglo = new int[ 10 ]; // crea el espacio para el arreglo 11 12 System.out.printf( "%s%8sn", "Indice", "Valor" ); // encabezados de columnas 13 14 // imprime el valor de cada elemento del arreglo 15 for ( int contador = 0; contador < arreglo.length; contador++ ) 16 System.out.printf( "%5d%8dn", contador, arreglo[ contador ] ); 17 } // fin de main 18 } // fin de la clase InicArreglo Figura 7.2 | Inicialización de los elementos de un arreglo con valores predeterminados de cero. Indice Valor 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0
  • 303. La instrucción for en las líneas 15 y 16 imprime el número de índice (representado por contador) y el valor de cada elemento del arreglo (representado por arreglo[ contador ]). Observe que al principio la variable de control del ciclo contador es 0 (los valores de los índices empiezan en 0, por lo que al utilizar un conteo con base cero se permite al ciclo acceder a todos los elementos del arreglo. La condición de continuación de ciclo de la instrucción for utiliza la expresión arreglo.length (línea 15) para determinar la longitud del arreglo. En este ejemplo la longitud del arreglo es de 10, por lo que el ciclo continúa ejecutándose mientras el valor de la variable de control contador sea menor que 10. El valor más alto para el subíndice de un arreglo de 10 elementos es 9, por lo que al utilizar el operador “menor que” en la condición de continuación de ciclo se garantiza que el ciclo no trate de acceder a un elemento más allá del final del arreglo (es decir, durante la iteración final del ciclo, contador es 9). Pronto veremos lo que hace Java cuando encuentra un subíndice fuera de rango en tiempo de ejecución. Uso de un inicializador de arreglo Un programa puede crear un arreglo e inicializar sus elementos con un inicializador de arreglo, que es una lista de expresiones separadas por comas (la cual se conoce también como lista inicializadora) encerrada entre llaves ({ y }); la longitud del arreglo se determina en base al número de elementos en la lista inicializadora. Por ejemplo, la declaración int n[] = { 10, 20, 30, 40, 50 }; crea un arreglo de cinco elementos con los valores de índices 0, 1, 2, 3 y 4. El elemento n[ 0 ] se inicializa con 10, n[ 1 ] se inicializa con 20, y así en lo sucesivo. Esta declaración no requiere que new cree el objeto arreglo. Cuando el compilador encuentra la declaración de un arreglo que incluye una lista inicializadora, cuenta el número de inicializadores en la lista para determinar el tamaño del arreglo, y después establece la operación new apropiada “detrás de las cámaras”. La aplicación de la figura 7.3 inicializa un arreglo de enteros con 10 valores (línea 9) y muestra el arreglo en formato tabular. El código para mostrar los elementos del arreglo (líneas 14 y 15) es idéntico al de la figura 7.2 (líneas 15 y 16). 7.4 Ejemplos acerca del uso de los arreglos 265 1 // Fig. 7.3: InicArreglo.java 2 // Inicialización de los elementos de un arreglo con un inicializador de arreglo. 3 4 public class InicArreglo 5 { 6 public static void main( String args[] ) 7 { 8 // la lista inicializadora especifica el valor para cada elemento 9 int arreglo[] = { 32, 27, 64, 18, 95, 14, 90, 70, 60, 37 }; 10 11 System.out.printf( "%s%8sn", "Indice", "Valor" ); // encabezados de columnas 12 13 // imprime el valor del elemento de cada arreglo 14 for ( int contador = 0; contador < arreglo.length; contador++ ) 15 System.out.printf( "%5d%8dn", contador, arreglo[ contador ] ); 16 } // fin de main 17 } // fin de la clase InicArreglo Figura 7.3 | Inicialización de los elementos de un arreglo con un inicializador de arreglo. (Parte 1 de 2). Indice Valor 0 32 1 27 2 64 3 18 4 95 5 14 6 90
  • 304. 266 Capítulo 7 Arreglos 1 // Fig. 7.4: InicArreglo.java 2 // Cálculo de los valores a colocar en los elementos de un arreglo. 3 4 public class InicArreglo 5 { 6 public static void main( String args[] ) 7 { 8 final int LONGITUD_ARREGLO = 10; // declara la constante 9 int arreglo[] = new int[ LONGITUD_ARREGLO ]; // crea el arreglo 10 11 // calcula el valor para cada elemento del arreglo 12 for ( int contador = 0; contador < arreglo.length; contador++ ) 13 arreglo[ contador ] = 2 + 2 * contador; 14 15 System.out.printf( "%s%8sn", "Indice", "Valor" ); // encabezados de columnas 16 17 // imprime el valor de cada elemento del arreglo 18 for ( int contador = 0; contador < arreglo.length; contador++ ) 19 System.out.printf( "%5d%8dn", contador, arreglo[ contador ] ); 20 } // fin de main 21 } // fin de la clase InicArreglo Figura 7.4 | Cálculo de los valores a colocar en los elementos de un arreglo. Figura 7.3 | Inicialización de los elementos de un arreglo con un inicializador de arreglo. (Parte 2 de 2). Cálculo de los valores a guardar en un arreglo La aplicación de la figura 7.4 crea un arreglo de 10 elementos y asigna a cada elemento uno de los enteros pares del 2 al 20 (2, 4, 6, …, 20). Después, la aplicación muestra el arreglo en formato tabular. La instrucción for en las líneas 12 y 13 calcula el valor de un elemento del arreglo, multiplicando el valor actual de la variable de control contador por 2, y después le suma 2. La línea 8 utiliza el modificador final para declarar la variable constante LONGITUD_ARREGLO con el valor 10. Las variables constantes (también conocidas como variables final) deben inicializarse antes de usarlas, y no pueden modificarse de ahí en adelante. Si trata de modificar una variable final después de inicializarla en su declaración (como en la línea 8), el compilador genera el siguiente mensaje de error: cannot assign a value to final variable nombreVariable Si tratamos de acceder al valor de una variable final antes de inicializarla, el compilador produce el siguiente mensaje de error variable nombreVariable might not have been initialized Indice Valor 0 2 1 4 2 6 3 8 4 10 5 12 6 14 7 16 8 18 9 20 7 70 8 60 9 37
  • 305. Buena práctica de programación 7.2 Las variables constantes también se conocen como constantes con nombre o variables de sólo lectura. Con fre- cuencia, dichas variables mejoran la legibilidad de un programa, en comparación con los programas que utilizan valores literales (por ejemplo, 10); una constante con nombre como LONGITUD_ARREGLO indica sin duda su propósi- to, mientras que un valor literal podría tener distintos significados, con base en el contexto en el que se utiliza. Error común de programación 7.4 Asignar un valor a una constante después de inicializarla es un error de compilación. Error común de programación 7.5 Tratar de usar una constante antes de inicializarla es un error de compilación. Suma de los elementos de un arreglo A menudo, los elementos de un arreglo representan una serie de valores que se emplearán en un cálculo. Por ejemplo, si los elementos del arreglo representan las calificaciones de un examen, tal vez el profesor desee sumar el total de los elementos del arreglo y utilizar esa suma para calcular el promedio de la clase para el exa- men. Los ejemplos que utilizan la clase LibroCalificaciones más adelante en este capítulo, figura 7.14 y 7.18, utilizan esta técnica. La aplicación de la figura 7.5 suma los valores contenidos en el arreglo entero de 10 elementos. El programa declara, crea e inicializa el arreglo en la línea 8. La instrucción for realiza los cálculos. [Nota: los valores suminis- trados como inicializadores de arreglos generalmente se introducen en un programa, en vez de especificarse en una lista inicializadora. Por ejemplo, una aplicación podría recibir los valores del usuario o de un archivo en disco (como veremos en el capítulo 14, Archivos y flujos). Al hacer que los datos se introduzcan como entrada en el programa éste se hace más flexible, ya que puede utilizarse con distintos conjuntos de datos]. Uso de gráficos de barra para mostrar los datos de un arreglo en forma gráfica Muchas aplicaciones presentan datos a los usuarios en forma gráfica. Por ejemplo, con frecuencia los valores numé- ricos se muestran como barras en un gráfico de barras. En dicho gráfico, las barras más largas representan valores numéricos más grandes en forma proporcional. Una manera sencilla de mostrar los datos numéricos en forma gráfica es mediante un gráfico de barras que muestre cada valor numérico como una barra de asteriscos (*). 7.4 Ejemplos acerca del uso de los arreglos 267 1 // Fig. 7.5: SumaArreglo.java 2 // Cálculo de la suma de los elementos de un arreglo. 3 4 public class SumaArreglo 5 { 6 public static void main( String args[] ) 7 { 8 int arreglo[] = { 87, 68, 94, 100, 83, 78, 85, 91, 76, 87 }; 9 int total = 0; 10 11 // suma el valor de cada elemento al total 12 for ( int contador = 0; contador < arreglo.length; contador++ ) 13 total += arreglo[ contador ]; 14 15 System.out.printf( "Total de los elementos del arreglo: %dn", total ); 16 } // fin de main 17 } // fin de la clase SumaArreglo Figura 7.5 | Cálculo de la suma de los elementos de un arreglo. Total de los elementos del arreglo: 849
  • 306. 268 Capítulo 7 Arreglos A los profesores les gusta examinar a menudo la distribución de las calificaciones en un examen. Un profesor podría graficar el número de calificaciones en cada una de varias categorías, para visualizar la distribución de las calificaciones. Suponga que las calificaciones en un examen fueron 87, 68, 94, 100, 83, 78, 85, 91, 76 y 87. Observe que hubo una calificación de 100, dos calificaciones en el rango de 90 a 99, cuatro calificaciones en el rango de 80 a 89, dos en el rango de 70 a 79, una en el rango de 60 a 69 y ninguna por debajo de 60. Nuestra siguiente aplicación (figura 7.6) almacena estos datos de distribución de las calificaciones en un arreglo de 11 ele- mentos, cada uno de los cuales corresponde a una categoría de calificaciones. Por ejemplo, arreglo[ 0 ] indica el número de calificaciones en el rango de 0 a 9, arreglo[ 7 ] indica el número de calificaciones en el rango de 70 a 79 y arreglo[ 10 ] indica el número de calificaciones de 100. Las dos versiones de la clase LibroCali- ficaciones que veremos más adelante en este capítulo (figuras 7.14 y 7.18) contienen código para calcular estas frecuencias de calificaciones, con base en un conjunto de calificaciones. Por ahora crearemos el arreglo en forma manual, mediante un análisis del conjunto de calificaciones. 1 // Fig. 7.6: GraficoBarras.java 2 // Programa para imprimir gráficos de barras. 3 4 public class GraficoBarras 5 { 6 public static void main( String args[] ) 7 { 8 int arreglo[] = { 0, 0, 0, 0, 0, 0, 1, 2, 4, 2, 1 }; 9 10 System.out.println( "Distribucion de calificaciones:" ); 11 12 // para cada elemento del arreglo, imprime una barra del gráfico 13 for ( int contador = 0; contador < arreglo.length; contador++ ) 14 { 15 // imprime etiqueta de la barra ( "00-09: ", ..., "90-99: ", "100: " ) 16 if ( contador == 10 ) 17 System.out.printf( "%5d: ", 100 ); 18 else 19 System.out.printf( "%02d-%02d: ", 20 contador * 10, contador * 10 + 9 ); 21 22 // imprime barra de asteriscos 23 for ( int estrellas = 0; estrellas < arreglo[ contador ]; estrellas++ ) 24 System.out.print( "*" ); 25 26 System.out.println(); // inicia una nueva línea de salida 27 } // fin de for externo 28 } // fin de main 29 } // fin de la clase GraficoBarras Figura 7.6 | Programa para imprimir gráficos de barras. Distribucion de calificaciones: 00-09: 10-19: 20-29: 30-39: 40-49: 50-59: 60-69: * 70-79: ** 80-89: **** 90-99: ** 100: *
  • 307. La aplicación lee los números del arreglo y grafica la información en forma de un gráfico de barras. El progra- ma muestra cada rango de calificaciones seguido de una barra de asteriscos, que indican el número de calificacio- nes en ese rango. Para etiquetar cada barra, las líneas 16 a 20 imprimen un rango de calificaciones (por ejemplo, "70-79: ") con base en el valor actual de contador. Cuando contador es 10, la línea 17 imprime 100 con una anchura de campo de 5, seguida de dos puntos y un espacio, para alinear la etiqueta "100: " con las otras etique- tas de las barras. La instrucción for anidada (líneas 23 y 24) imprime las barras en pantalla. Observe la condición de continuación de ciclo en la línea 23 (estrellas < arreglo[ contador ]). Cada vez que el programa llega al for interno, el ciclo cuenta desde 0 hasta arreglo[ contador ], con lo cual utiliza un valor en arreglo para determinar el número de asteriscos a mostrar en pantalla. En este ejemplo, los valores de arreglo[ 0 ] hasta arreglo[ 5 ] son 0, ya que ningún estudiante recibió una calificación menor de 60. Por ende, el programa no muestra asteriscos enseguida de los primeros seis rangos de calificaciones. Observe que la línea 19 utiliza el espe- cificador de formato %02d para imprimir los números en un rango de calificaciones. Este especificador indica que se debe dar formato a un valor int como un campo de dos dígitos. La bandera 0 en el especificador de formato indica que los valores con menos dígitos que la anchura de campo (2) deben empezar con un 0 a la izquierda. Uso de los elementos de un arreglo como contadores En ocasiones, los programas utilizan variables tipo contador para sintetizar datos, como los resultados de una encuesta. En la figura 6.8 utilizamos contadores separados en nuestro programa para tirar dados, para rastrear el número de veces que aparecía cada una de las caras de un dado con seis lados, al tiempo que la aplicación tiraba el dado 6000 veces. En la figura 7.7 se muestra una versión de la aplicación de la figura 6.8, esta vez usando un arreglo. 7.4 Ejemplos acerca del uso de los arreglos 269 1 // Fig. 7.7: TirarDado.java 2 // Tira un dado de seis lados 6000 veces. 3 import java.util.Random; 4 5 public class TirarDado 6 { 7 public static void main( String args[] ) 8 { 9 Random numerosAleatorios = new Random(); // generador de números aleatorios 10 int frecuencia[] = new int[ 7 ]; // arreglo de contadores de frecuencia 11 12 // tira el dado 6000 veces; usa el valor del dado como índice de frecuencia 13 for ( int tiro = 1; tiro <= 6000; tiro++ ) 14 ++frecuencia[ 1 + numerosAleatorios.nextInt( 6 ) ]; 15 16 System.out.printf( "%s%10sn", "Cara", "Frecuencia" ); 17 18 // imprime el valor de cada elemento del arreglo 19 for ( int cara = 1; cara < frecuencia.length; cara++ ) 20 System.out.printf( "%4d%10dn", cara, frecuencia[ cara ] ); 21 } // fin de main 22 } // fin de la clase TirarDado Figura 7.7 | Programa para tirar dados que utiliza arreglos en vez de switch. Cara Frecuencia 1 1015 2 999 3 998 4 996 5 1044 6 948
  • 308. 270 Capítulo 7 Arreglos La figura 7.7 utiliza el arreglo frecuencia (línea 10) para contar las ocurrencias de cada lado del dado. La instrucción individual en la línea 14 de este programa sustituye las líneas 23 a 46 de la figura 6.8. La línea 14 utiliza el valor aleatorio para determinar qué elemento de frecuencia debe incrementar durante cada iteración del ciclo. El cálculo en la línea 14 produce números aleatorios del 1 al 6, por lo que el arreglo frecuencia debe ser lo bastante grande como para poder almacenar seis contadores. Sin embargo, utilizamos un arreglo de siete elementos, en el cual ignoramos frecuencia[ 0 ]; es más lógico que el valor de cara 1 incremente a fre- cuencia[ 1 ] que a frecuencia[ 0 ]. Por ende, cada valor de cara se utiliza como subíndice para el arreglo frecuencia. También sustituimos las líneas 50 a 52 de la figura 6.8 por un ciclo a través del arreglo frecuencia para imprimir los resultados en pantalla (líneas 19 a 20). Uso de arreglos para analizar los resultados de una encuesta Nuestro siguiente ejemplo utiliza arreglos para sintetizar los resultados de los datos recolectados en una encuesta: Se pidió a cuarenta estudiantes que calificaran la calidad de la comida en la cafetería estudiantil, en una escala del 1 al 10 (en donde 1 significa pésimo y 10 significa excelente). Coloque las 40 respuestas en un arreglo entero y sintetice los resultados de la encuesta. Ésta es una típica aplicación de procesamiento de arreglos (vea la figura 7.8). Deseamos resumir el número de respuestas de cada tipo (es decir, del 1 al 10). El arreglo respuestas (líneas 9 a 11) es un arreglo entero de 40 elementos, y contiene las respuestas de los estudiantes a la encuesta. Utilizamos un arreglo de 11 elementos lla- mado frecuencia (línea 12) para contar el número de ocurrencias de cada respuesta. Cada elemento del arreglo se utiliza como un contador para una de las respuestas de la encuesta, y se inicializa con cero de manera predeter- minada. Al igual que en la figura 7.7, ignoramos frecuencia[ 0 ]. El ciclo for en las líneas 16 y 17 recibe las respuestas del arreglo respuestas una a la vez, e incrementa uno de los 10 contadores en el arreglo frecuencia (de frecuencia[ 1 ] a frecuencia[ 10 ]). La instrucción clave en el ciclo es la línea 17, la cual incrementa el contador de frecuencia apropiado, dependiendo del valor de respuestas[ respuesta ]. Consideraremos varias iteraciones del ciclo for. Cuando la variable de control respuesta es 0, el valor de respuestas[ respuesta ] es el valor de respuestas[ 0 ] (es decir, 1), por lo que el programa interpreta a ++frecuencia[ respuestas[ respuesta ] ] como ++frecuencia[ 1 ] con lo cual se incrementa el valor en el elemento 1 del arreglo. Para evaluar la expresión, empiece con el valor en el conjunto más interno de corchetes (respuesta). Una vez que conozca el valor de respuesta (que es el valor de la variable de control de ciclo en la línea 16), insértelo en la expresión y evalúe el siguiente conjunto más externo de corchetes (respuestas[ respuesta ], que es un valor seleccionado del arreglo respuestas en las líneas 9 a 11). Después utilice el valor resultante como subíndice del arreglo frecuencia, para especificar cuál contador se incrementará. Cuando respuesta es 1, respuestas[ respuesta ] es el valor de respuestas[ 1 ] (2), por lo que el programa interpreta a ++frecuencia[ respuestas[ respuesta ] ] como ++frecuencia[ 2 ] con lo cual se incrementa el elemento 2 del arreglo. Cuando respuesta es 2, respuestas[ respuesta ] es el valor de respuestas[ 2 ] (6), por lo que el programa interpreta a ++frecuencia[ respuestas [ respuesta ] ] como ++frecuencia[ 6 ] con lo cual se incrementa el elemento 6 del arreglo, y así en lo sucesivo. Sin importar el número de respuestas procesadas en la encuesta, el programa sólo requiere un arreglo de 11 elementos (en el cual se ignora el elemento cero) para resumir los resultados, ya que todos los valores de las respuestas se encuentran entre 1 y 10, y los valores de subíndice para un arreglo de 11 elementos son del 0 al 10. Si los datos en el arreglo respuestas tuvieran valores inválidos como 13, el programa trataría de sumar 1 a frecuencia[ 13 ], lo cual se encuentra fuera de los límites del arreglo. Java no permite esto. Cuando se ejecuta un programa en Java, la JVM comprueba los subíndices del arreglo para asegurarse que sean válidos (es
  • 309. decir, deben ser mayores o iguales a 0 y menores que la longitud del arreglo). Si un programa utiliza un subíndice inválido, Java genera una excepción para indicar que se produjo un error en el programa, en tiempo de ejecución. Puede utilizarse una instrucción de control para evitar que ocurra un error tipo “fuera de los límites”. Por ejemplo, la condición en una instrucción de control podría determinar si un subíndice es válido antes de permitir que se utilice en una expresión de acceso a un arreglo. Tip para prevenir errores 7.1 Una excepción indica que ocurrió un error en un programa. A menudo el programador puede escribir código para recuperarse de una excepción y continuar con la ejecución del programa, en vez de terminarlo en forma anormal. Cuando un programa trata de acceder a un elemento fuera de los límites del arreglo, se produce una excepción Array- IndexOutOfBoundsException. En el capítulo 13 hablaremos sobre el manejo de excepciones. Tip para prevenir errores 7.2 Al escribir código para iterar a través de un arreglo, hay que asegurar que el subíndice del arreglo siempre sea mayor o igual a 0 y menor que la longitud del arreglo. La condición de continuación de ciclo debe evitar el acceso a elementos fuera de este rango. 7.4 Ejemplos acerca del uso de los arreglos 271 1 // Fig. 7.8: EncuestaEstudiantes.java 2 // Programa de análisis de una encuesta. 3 4 public class EncuestaEstudiantes 5 { 6 public static void main( String args[] ) 7 { 8 // arreglo de respuestas a una encuesta 9 int respuestas[] = { 1, 2, 6, 4, 8, 5, 9, 7, 8, 10, 1, 6, 3, 8, 6, 10 10, 3, 8, 2, 7, 6, 5, 7, 6, 8, 6, 7, 5, 6, 6, 5, 6, 7, 5, 6, 11 4, 8, 6, 8, 10 }; 12 int frecuencia[] = new int[ 11 ]; // arreglo de contadores de frecuencia 13 14 // para cada respuesta, selecciona el elemento de respuestas y usa ese valor 15 // como índice de frecuencia para determinar el elemento a incrementar 16 for ( int respuesta = 0; respuesta < respuestas.length; respuesta++ ) 17 ++frecuencia[ respuestas[ respuesta ] ]; 18 19 System.out.printf( "%s%10sn", "Calificacion", "Frecuencia" ); 20 21 // imprime el valor de cada elemento del arreglo 22 for ( int calificacion = 1; calificacion < frecuencia.length; calificacion++ ) 23 System.out.printf( "%6d%10dn", calificacion, frecuencia[ calificacion ] ); 24 } // fin de main 25 } // fin de la clase EncuestaEstudiantes Figura 7.8 | Programa de análisis de una encuesta. Calificacion Frecuencia 1 2 2 2 3 2 4 2 5 5 6 11 7 5 8 7 9 1 10 3
  • 310. 272 Capítulo 7 Arreglos 7.5 Ejemplo práctico: simulación para barajar y repartir cartas Hasta ahora, en los ejemplos en este capítulo hemos utilizado arreglos que contienen elementos de tipos pri- mitivos. En la sección 7.2 vimos que los elementos de un arreglo pueden ser de tipos primitivos o de tipos por referencia. En esta sección utilizaremos la generación de números aleatorios y un arreglo de elementos de tipo por referencia (a saber, objetos que representan cartas de juego) para desarrollar una clase que simule los procesos de barajar y repartir cartas. Después podremos utilizar esta clase para implementar aplicaciones que ejecuten jue- gos específicos de cartas. Los ejercicios al final del capítulo utilizan las clases que desarrollaremos aquí para crear una aplicación simple de póquer. Primero desarrollaremos la clase Carta (figura 7.9), la cual representa una carta de juego que tiene una cara ("As", "Dos", "Tres", …, "Joto", "Qüina", "Rey") y un palo ("Corazones", "Diamantes", "Tréboles", "Espadas"). Después desarrollaremos la clase PaqueteDeCartas (figura 7.10), la cual crea un paquete de 52 cartas en las que cada elemento es un objeto Carta. Luego construiremos una aplicación de prueba (figura 7.11) para demostrar las capacidades de barajar y repartir cartas de la clase PaqueteDeCartas. La clase Carta La clase Carta (figura 7.9) contiene dos variables de instancia String (cara y palo) que se utilizan para almace- nar referencias al valor de la cara y al valor del palo para una Carta específica. El constructor de la clase (líneas 10 a 14) recibe dos objetos String que utiliza para inicializar cara y palo. El método toString (líneas 17 a 20) crea un objeto String que consiste en la cara de la carta, el objeto String "de" y el palo de la carta. En el capítulo 6 vimos que el operador + puede utilizarse para concatenar (es decir, combinar) varios objetos String para formar un objeto String más grande. El método toString de Carta puede invocarse en forma explícita para obtener la representación de cadena de un objeto Carta (por ejemplo, "As de Espadas"). El método toString de un objeto se llama en forma implícita cuando el objeto se utiliza en donde se espera un objeto String (por ejemplo, cuando printf imprime en pantalla el objeto como un String, usando el especificador de formato %s, o cuando el objeto se concatena con un objeto String mediante el operador +). Para que ocurra este comportamiento, toString debe declararse con el encabezado que se muestra en la figura 7.9. La clase PaqueteDeCartas La clase PaqueteDeCartas (figura 7.10) declara un arreglo de variables de instancia llamado paquete, el cual contiene objetos Carta (línea 7). Al igual que las declaraciones de arreglos de tipos primitivos, la declaración de un arreglo de objetos incluye el tipo de los elementos en el arreglo, seguido del nombre de la variable del arreglo y 1 // Fig. 7.9: Carta.java 2 // La clase Carta representa una carta de juego. 3 4 public class Carta 5 { 6 private String cara; // cara de la carta ("As", "Dos", ...) 7 private String palo; // palo de la carta ("Corazones", "Diamantes", ...) 8 9 // el constructor de dos argumentos inicializa la cara y el palo de la carta 10 public Carta( String caraCarta, String paloCarta ) 11 { 12 cara = caraCarta; // inicializa la cara de la carta 13 palo = paloCarta; // inicializa el palo de la carta 14 } // fin del constructor de Carta con dos argumentos 15 16 // devuelve representación String de Carta 17 public String toString() 18 { 19 return cara + " de " + palo; 20 } // fin del método toString 21 } // fin de la clase Carta Figura 7.9 | La clase Carta representa una carta de juego.
  • 311. 7.5 Ejemplo práctico: simulación para barajar y repartir cartas 273 1 // Fig. 7.10: PaqueteDeCartas.java 2 // La clase PaqueteDeCartas representa un paquete de cartas de juego. 3 import java.util.Random; 4 5 public class PaqueteDeCartas 6 { 7 private Carta paquete[]; // arreglo de objetos Carta 8 private int cartaActual; // subíndice de la siguiente Carta a repartir 9 private final int NUMERO_DE_CARTAS = 52; // número constante de Cartas 10 private Random numerosAleatorios; // generador de números aleatorios 11 12 // el constructor llena el paquete de Cartas 13 public PaqueteDeCartas() 14 { 15 String caras[] = { "As", "Dos", "Tres", "Cuatro", "Cinco", "Seis", 16 "Siete", "Ocho", "Nueve", "Diez", "Joto", "Quina", "Rey" }; 17 String palos[] = { "Corazones", "Diamantes", "Treboles", "Espadas" }; 18 19 paquete = new Carta[ NUMERO_DE_CARTAS ]; // crea arreglo de objetos Carta 20 cartaActual = 0; // establece cartaActual para que la primera Carta repartida sea paquete[ 0 ] 21 numerosAleatorios = new Random(); // crea generador de números aleatorios 22 23 // llena el paquete con objetos Carta 24 for ( int cuenta = 0; cuenta < paquete.length; cuenta++ ) 25 paquete[ cuenta ] = 26 new Carta( caras[ cuenta % 13 ], palos[ cuenta / 13 ] ); 27 } // fin del constructor de PaqueteDeCartas 28 29 // baraja el paquete de Cartas con algoritmo de una pasada 30 public void barajar() 31 { 32 // después de barajar, la repartición debe empezar en paquete[ 0 ] otra vez 33 cartaActual = 0; // reinicializa cartaActual 34 35 // para cada Carta, selecciona otra Carta aleatoria y las intercambia 36 for ( int primera = 0; primera < paquete.length; primera++ ) 37 { 38 // selecciona un número aleatorio entre 0 y 51 39 int segunda = numerosAleatorios.nextInt( NUMERO_DE_CARTAS ); 40 41 // intercambia Carta actual con la Carta seleccionada al azar 42 Carta temp = paquete[ primera ]; 43 paquete[ primera ] = paquete[ segunda ]; 44 paquete[ segunda ] = temp; 45 } // fin de for 46 } // fin de método barajar 47 48 // reparte una Carta 49 public Carta repartirCarta() 50 { 51 // determina si quedan Cartas por repartir 52 if ( cartaActual < paquete.length ) 53 return paquete[ cartaActual++ ]; // devuelve la Carta actual en el arreglo 54 else 55 return null; // devuelve null para indicar que se repartieron todas las Cartas 56 } // fin del método repartirCarta 57 } // fin de la clase PaqueteDeCartas Figura 7.10 | La clase PaqueteDeCartas representa un paquete de cartas de juego, que pueden barajarse y repartirse, una a la vez.
  • 312. 274 Capítulo 7 Arreglos de corchetes (por ejemplo Carta paquete[ ]). La clase PaqueteDeCartas también declara la variable de instan- cia entera llamada cartaActual (línea 8), que representa la siguiente Carta a repartir del arreglo paquete, y la constante con nombre NUMERO_DE_CARTAS (línea 9), que indica el número de objetos Carta en el paquete (52). El constructor de la clase crea una instancia del arreglo paquete (línea 19) con un tamaño igual a NUME- RO_DE_CARTAS. Cuando se crea por primera vez el arreglo paquete, sus elementos son null de manera prede- terminada, por lo que el constructor utiliza una instrucción for (líneas 24 a 26) para llenar el arreglo paquetes con objetos Carta. La instrucción for inicializa la variable de control cuenta con 0 e itera mientras cuenta sea menor que paquete.length, lo cual hace que cuenta tome el valor de cada entero del 0 al 51 (los subíndices del arreglo paquete). Cada objeto Carta se instancia y se inicializa con dos objetos String: uno del arreglo caras (que contiene los objetos String del "As" hasta el "Rey") y uno del arreglo palos (que contiene los objetos String "Corazones", "Diamantes", "Treboles" y "Espadas"). El cálculo cuenta % 13 siempre produce un valor de 0 a 12 (los 13 subíndices del arreglo caras en las líneas 15 y 16), y el cálculo cuenta / 13 siempre produce un valor de 0 a 3 (los cuatro subíndices del arreglo palos en la línea 17). Cuando se inicializa el arreglo paquete, contiene los objetos Carta con las caras del "As" al "Rey" en orden para cada palo ("Corazones", "Diamantes", "Treboles", "Espadas"). El método barajar (líneas 30 a 46) baraja los objetos Carta en el paquete. El método itera a través de los 52 objetos Carta (subíndices 0 a 51 del arreglo). Para cada objeto Carta se elige al azar un número entre 0 y 51 para elegir otro objeto Carta. A continuación, el objeto Carta actual y el objeto Carta seleccionado al azar se inter- cambian en el arreglo. Este intercambio se realiza mediante las tres asignaciones en las líneas 42 a 44. La variable extra temp almacena en forma temporal uno de los dos objetos Carta que se van a intercambiar. El intercambio no se puede realizar sólo con las dos instrucciones paquete[ primera ] = paquete[ segunda ]; paquete[ segunda ] = paquete[ primera ]; Si paquete[ primera ] es el "As" de "Espadas" y paquete[ segunda ] es la "Quina" de "Corazones", entonces después de la primera asignación, ambos elementos del arreglo contienen la "Quina" de "Corazones" y se pierde el "As" de "Espadas"; es por ello que se necesita la variable extra temp. Una vez que termina el ciclo for, los objetos Carta se ordenan al azar. Sólo se realizan 52 intercambios en una sola pasada del arreglo comple- to, ¡y el arreglo de objetos Carta se baraja! El método repartirCarta (líneas 49 a 56) reparte un objeto Carta en el arreglo. Recuerde que cartaAc- tual indica el subíndice del siguiente objeto Carta que se repartirá (es decir, la Carta en la parte superior del paquete). Por ende, la línea 52 compara cartaActual con la longitud del arreglo paquete. Si el paquete no está vacío (es decir, si cartaActual es menor a 52), la línea 53 regresa el objeto Carta “superior” y postincrementa cartaActual para prepararse para la siguiente llamada a repartirCarta; en caso contrario, se devuelve null. En el capítulo 3 vimos que null representa una “referencia a nada”. Barajar y repartir cartas La aplicación de la figura 7.11 demuestra las capacidades de barajar y repartir cartas de la clase PaqueteDeCar- tas (figura 7.10). La línea 9 crea un objeto PaqueteDeCartas llamado miPaqueteDeCartas. Recuerde que el constructor PaqueteDeCartas crea el paquete con los 52 objetos Carta, en orden por palo y por cara. La línea 10 invoca el método barajar de miPaqueteDeCartas para reordenar los objetos Carta. La instrucción for en las líneas 13 a 19 reparte los 52 objetos Carta en el paquete y los imprime en cuatro columnas, cada una con 13 objetos Carta. Las líneas 16 a 18 reparten e imprimen en pantalla cuatro objetos Carta, cada uno de los cuales se obtiene mediante la invocación al método repartirCarta de miPaqueteDeCartas. Cuando printf imprime en pantalla un objeto Carta con el especificador de formato %-20s, el método toString de Carta (declarado en las líneas 17 a 20 de la figura 7.9) se invoca en forma implícita, y el resultado se imprime justificado a la izquierda, en un campo con una anchura de 20. 7.6 Instrucción for mejorada En ejemplos anteriores demostramos cómo utilizar las instrucciones for controladas por un contador para iterar a través de los elementos en un arreglo. En esta sección presentaremos la instrucción for mejorada, la cual itera a través de los elementos de un arreglo o colección sin utilizar un contador (con lo cual, evita la posibilidad de “salirse” del arreglo). Esta sección habla acerca de cómo utilizar la instrucción for mejorada para iterar a tra-
  • 313. vés de un arreglo. En el capítulo 19, Colecciones, veremos cómo utilizar la instrucción for mejorada con colec- ciones. La sintaxis de una instrucción for mejorada es: for ( parámetro : nombreArreglo ) instrucción en donde parámetro tiene dos partes: un tipo y un identificador (por ejemplo, int numero), y nombreArreglo es el arreglo a través del cual se iterará. El tipo del parámetro debe concordar con el tipo de los elementos en el arreglo. Como se muestra en el siguiente ejemplo, el identificador representa valores sucesivos en el arreglo, en iteraciones sucesivas de la instrucción for mejorada. La figura 7.12 utiliza la instrucción for mejorada (líneas 12 y 13) para calcular la suma de los enteros en un arreglo de calificaciones de estudiantes. El tipo especificado en el parámetro para el for mejorado es int, ya que arreglo contiene valores int; el ciclo selecciona un valor int del arreglo durante cada iteración. La instrucción for mejorada itera a través de valores sucesivos en el arreglo, uno por uno. El encabezado del for mejorado se puede leer como “para cada iteración, asignar el siguiente elemento de arreglo a la variable int numero, después ejecutar la siguiente instrucción”. Por lo tanto, para cada iteración, el identificador numero representa un valor int en arreglo. Las líneas 12 y 13 son equivalentes a la siguiente repetición controlada por un contador que se utiliza en las líneas 12 y 13 de la figura 7.5, para totalizar los enteros en el arreglo: for ( int contador = 0; contador < arreglo.length; contador++ ) total += arreglo[ contador ]; 7.6 Instruccion for mejorada 275 1 // Fig. 7.11: PruebaPaqueteDeCartas.java 2 // Aplicación para barajar y repartir cartas. 3 4 public class PruebaPaqueteDeCartas 5 { 6 // ejecuta la aplicación 7 public static void main( String args[] ) 8 { 9 PaqueteDeCartas miPaqueteDeCartas = new PaqueteDeCartas(); 10 miPaqueteDeCartas.barajar(); // coloca las Cartas en orden aleatorio 11 12 // imprime las 52 Cartas en el orden en el que se reparten 13 for ( int i = 0; i < 13; i++ ) 14 { 15 // reparte e imprime 4 Cartas 16 System.out.printf( "%-20s%-20s%-20s%-20sn", 17 miPaqueteDeCartas.repartirCarta(), miPaqueteDeCartas.repartirCarta(), 18 miPaqueteDeCartas.repartirCarta(), miPaqueteDeCartas.repartirCarta() ); 19 } // fin de for 20 } // fin de main 21 } // fin de la clase PruebaPaqueteDeCartas Figura 7.11 | Aplicación para barajar y repartir cartas. Nueve de Espadas Joto de Corazones Quina de Treboles Siete de Treboles Seis de Corazones Seis de Treboles Joto de Diamantes Tres de Diamantes Diez de Diamantes Cinco de Diamantes As de Treboles Rey de Diamantes Siete de Corazones Cuatro de Corazones Cuatro de Espadas Nueve de Corazones Dos de Espadas Quina de Diamantes Dos de Corazones Quina de Corazones As de Corazones Cuatro de Treboles Cinco de Espadas Joto de Treboles Cinco de Treboles Siete de Diamantes As de Espadas Ocho de Espadas Nueve de Treboles Cuatro de Diamantes Siete de Espadas Rey de Corazones Quina de Espadas Dos de Diamantes Rey de Treboles Diez de Corazones Cinco de Corazones As de Diamantes Rey de Espadas Joto de Espadas Ocho de Diamantes Tres de Espadas Ocho de Treboles Seis de Diamantes Nueve de Diamantes Tres de Treboles Diez de Treboles Dos de Treboles Tres de Corazones Ocho de Corazones Diez de Espadas Seis de Espadas
  • 314. 276 Capítulo 7 Arreglos La instrucción for mejorada simplifica el código para iterar a través de un arreglo. No obstante, observe que la instrucción for mejorada sólo puede utilizarse para obtener elementos del arreglo; no puede utilizarse para modificar los elementos. Si su programa necesita modificar elementos, use la instrucción for tradicional, controlada por contador. La instrucción for mejorada se puede utilizar en lugar de la instrucción for controlada por contador, cuan- do el código que itera a través de un arreglo no requiere acceso al contador que indica el subíndice del elemento actual del arreglo. Por ejemplo, para totalizar los enteros en un arreglo se requiere acceso sólo a los valores de los elementos; el subíndice de cada elemento es irrelevante. No obstante, si un programa debe utilizar un contador por alguna razón que no sea tan sólo iterar a través de un arreglo (por ejemplo, imprimir un número de subíndice al lado del valor de cada elemento del arreglo, como en los primeros ejemplos en este capítulo), use la instrucción for controlada por contador. 1 // Fig. 7.12: PruebaForMejorado.java 2 // Uso de la instrucción for mejorada para sumar el total de enteros en un arreglo. 3 4 public class PruebaForMejorado 5 { 6 public static void main( String args[] ) 7 { 8 int arreglo[] = { 87, 68, 94, 100, 83, 78, 85, 91, 76, 87 }; 9 int total = 0; 10 11 // suma el valor de cada elemento al total 12 for ( int numero : arreglo ) 13 total += numero; 14 15 System.out.printf( "Total de elementos del arreglo: %dn", total ); 16 } // fin de main 17 } // fin de la clase PruebaForMejorado Figura 7.12 | Uso de la instrucción for mejorada para sumar el total de los enteros en un arreglo. Total de elementos del arreglo: 849 7.7 Paso de arreglos a los métodos Esta sección demuestra cómo pasar arreglos y elementos individuales de un arreglo como argumentos para los métodos. Al final de la sección, hablaremos acerca de cómo se pasan todos los tipos de argumentos a los métodos. Para pasar un argumento tipo arreglo a un método, se especifica el nombre del arreglo sin corchetes. Por ejemplo, si el arreglo temperaturasPorHora se declara como double temperaturasPorHora[ ] = new double[ 24 ]; entonces la llamada al método modificarArreglo( temperaturasPorHora ); pasa la referencia del arreglo temperaturasPorHora al método modificarArreglo. Todo objeto arreglo “conoce” su propia longitud (a través de su campo length). Por ende, cuando pasamos a un método la referencia a un objeto arreglo, no necesitamos pasar la longitud del arreglo como un argumento adicional. Para que un método reciba una referencia a un arreglo a través de una llamada a un método, la lista de parámetros del método debe especificar un parámetro tipo arreglo. Por ejemplo, el encabezado para el método modificarArreglo podría escribirse así: void modificarArreglo( int b[] )
  • 315. lo cual indica que modificarArreglo recibe la referencia de un arreglo de enteros en el parámetro b. La llamada a este método pasa la referencia al arreglo temperaturaPorHoras, de manera que cuando el método llamado utili- za la variable b tipo arreglo, hace referencia al mismo objeto arreglo como temperaturasPorHora en el método que hizo la llamada. Cuando un argumento para un método es todo un arreglo, o un elemento individual de un arreglo de un tipo por referencia, el método llamado recibe una copia de la referencia. Sin embargo, cuando un argumento para un método es un elemento individual de un arreglo de un tipo primitivo, el método llamado recibe una copia del valor del elemento. Dichos valores primitivos se conocen como escalares o cantidades escalares. Para pasar un elemento individual de un arreglo a un método, use el nombre indexado del elemento del arreglo como argumento en la llamada al método. La figura 7.13 demuestra la diferencia entre pasar a un método todo un arreglo y pasar un elemento de un arreglo de tipo primitivo. La instrucción for mejorada en las líneas 16 y 17 imprime en pantalla los cinco elementos de arreglo (un arreglo de valores int). La línea 19 invoca al método modificarArreglo y le pa- sa arreglo como argumento. El método modificarArreglo (líneas 36 a 40) recibe una copia de la referencia a arreglo y utiliza esta referencia para multiplicar cada uno de los elementos de arreglo por 2. Para demostrar que se modificaron los elementos de arreglo, la instrucción for en las líneas 23 y 24 imprime en pantalla los cinco elementos de arreglo otra vez. Como se muestra en la salida, el método modificarArreglo duplicó el valor de cada elemento. Observe que no pudimos usar la instrucción for mejorada en las líneas 38 y 39, ya que estamos modificando los elementos del arreglo. 7.7 Paso de arreglos a los métodos 277 1 // Fig. 7.13: PasoArreglo.java 2 // Paso de arreglos y elementos individuales de un arreglo a los métodos. 3 4 public class PasoArreglo 5 { 6 // main crea el arreglo y llama a modificarArreglo y a modificarElemento 7 public static void main( String args[] ) 8 { 9 int arreglo[] = { 1, 2, 3, 4, 5 }; 10 11 System.out.println( 12 "Efectos de pasar una referencia a un arreglo completo:n" + 13 "Los valores del arreglo original son:" ); 14 15 // imprime los elementos originales del arreglo 16 for ( int valor : arreglo ) 17 System.out.printf( " %d", valor ); 18 19 modificarArreglo( arreglo ); // pasa la referencia al arreglo 20 System.out.println( "nnLos valores del arreglo modificado son:" ); 21 22 // imprime los elementos modificados del arreglo 23 for ( int valor : arreglo ) 24 System.out.printf( " %d", valor ); 25 26 System.out.printf( 27 "nnEfectos de pasar el valor de un elemento del arreglo:n" + 28 "arreglo[3] antes de modificarElemento: %dn”, arreglo[ 3 ] ); 29 30 modificarElemento( arreglo[ 3 ] ); // intento por modificar arreglo[ 3 ] 31 System.out.printf( 32 "arreglo[3] despues de modificarElemento: %dn", arreglo[ 3 ] ); 33 } // fin de main 34 35 // multiplica cada elemento de un arreglo por 2 Figura 7.13 | Paso de arreglos y elementos individuales de un arreglo a los métodos. (Parte 1 de 2).
  • 316. 278 Capítulo 7 Arreglos Figura 7.13 | Paso de arreglos y elementos individuales de un arreglo a los métodos. (Parte 2 de 2). Efectos de pasar una referencia a un arreglo completo: Los valores del arreglo original son: 1 2 3 4 5 Los valores del arreglo modificado son: 2 4 6 8 10 Efectos de pasar el valor de un elemento del arreglo: arreglo[3] antes de modificarElemento: 8 Valor del elemento en modificarElemento: 16 arreglo[3] despues de modificarElemento: 8 36 public static void modificarArreglo( int arreglo2[] ) 37 { 38 for ( int contador = 0; contador < arreglo2.length; contador++ ) 39 arreglo2[ contador ] *= 2; 40 } // fin del método modificarArreglo 41 42 // multiplica el argumento por 2 43 public static void modificarElemento( int elemento ) 44 { 45 elemento *= 2; 46 System.out.printf( 47 "Valor del elemento en modificarElemento: %dn", elemento ); 48 } // fin del método modificarElemento 49 } // fin de la clase PasoArreglo La figura 7.13 demuestra a continuación que, cuando se pasa una copia de un elemento individual de un arreglo de tipo primitivo a un método, si se modifica la copia en el método que se llamó, el valor original de ese elemento no se ve afectado en el arreglo del método que hizo la llamada. Las líneas 26 a 28 imprimen en panta- lla el valor de arreglo[ 3 ] (8) antes de invocar al método modificarElemento. La línea 30 llama al método modificarElemento y le pasa arreglo[ 3 ] como argumento. Recuerde que arreglo[ 3 ] es en realidad un valor int (8) en arreglo. Por lo tanto, el programa pasa una copia del valor de arreglo[ 3 ]. El método modificarElemento (líneas 43 a 48) multiplica el valor recibido como argumento por 2, almacena el resultado en su parámetro elemento y después imprime en pantalla el valor de elemento (16). Como los parámetros de los métodos, al igual que las variables locales, dejan de existir cuando el método en el que se declaran termina su ejecución, el parámetro elemento del método se destruye cuando modificarElemento termina. Por lo tanto, cuando el programa devuelve el control a main, las líneas 31 y 32 imprimen en pantalla el valor de arreglo[ 3 ] que no se modificó (es decir, 8). Notas acerca del paso de argumentos a los métodos El ejemplo anterior demostró las distintas maneras en las que se pasan los arreglos y los elementos de arreglos de tipos primitivos a los métodos. Ahora veremos con más detalle la forma en que se pasan los argumentos a los métodos en general. En muchos lenguajes de programación, dos formas de pasar argumentos en las llamadas a métodos son el paso por valor y el paso por referencia (también conocidas como llamada por valor y llama- da por referencia). Cuando se pasa un argumento por valor, se pasa una copia del valor del argumento al método que se llamó. Este método trabaja exclusivamente con la copia. Las modificaciones a la copia del método que se llamó no afectan el valor de la variable original en el método que hizo la llamada. Cuando se pasa un argumento por referencia, el método que se llamó puede acceder al valor del argumento en el método que hizo la llamada directamente, y puede modificar esos datos si es necesario. El paso por referencia mejora el rendimiento, al eliminar la necesidad de copiar cantidades de datos posiblemente extensas. A diferencia de otros lenguajes, Java no permite a los programadores elegir el paso por valor o el paso por referencia; todos los argumentos se pasan por valor. Una llamada a un método puede pasar dos tipos de valores:
  • 317. copias de valores primitivos (como valores de tipo int y double) y copias de referencias a objetos (incluyendo las referencias a arreglos). Los objetos en sí no pueden pasarse a los métodos. Cuando un método modifica un pará- metro de tipo primitivo, las modificaciones a ese parámetro no tienen efecto en el valor original del argumento en el método que hizo la llamada. Por ejemplo, cuando la línea 30 en main de la figura 7.13 pasa arreglo[ 3 ] al método modificarElemento, la instrucción en la línea 45 que duplica el valor del parámetro elemento no tiene efecto sobre el valor de arreglo[ 3 ] en main. Esto también se aplica para los parámetros de tipo por referen- cia. Si usted modifica un parámetro de tipo por referencia al asignarle la referencia de otro objeto, el parámetro hace referencia al nuevo objeto, pero la referencia almacenada en la variable del método que hizo la llamada sigue haciendo referencia al objeto original. Aunque la referencia a un objeto se pasa por valor, un método puede de todas formas interactuar con el obje- to al que se hace referencia, llamando a sus métodos public mediante el uso de la copia de la referencia al objeto. Como la referencia almacenada en el parámetro es una copia de la referencia que se pasó como argumento, el parámetro en el método que se llamó y el argumento en el método que hizo la llamada hacen referencia al mismo objeto en la memoria. Por ejemplo, en la figura 7.13, tanto el parámetro arreglo2 en el método modificarArre glo como la variable arreglo en main hacen referencia al mismo objeto en la memoria. Cualquier modificación que se realice usando el parámetro arreglo2 se lleva a cabo en el mismo objeto al que hace referencia la varia- ble que se pasó como argumento en el método que hizo la llamada. En la figura 7.13, las modificaciones realizadas en modificarArreglo en las que se utiliza arreglo2, afectan al contenido del objeto arreglo al que hace referencia arreglo en main. De esta forma, con una referencia a un objeto, el método que se llamó puede manipular el objeto del método que hizo la llamada directamente. Tip de rendimiento 7.1 Pasar arreglos por referencia tiene sentido por cuestiones de rendimiento. Si los arreglos se pasaran por valor, se pasaría una copia de cada elemento. En los arreglos grandes que se pasan con frecuencia, esto desperdiciaría tiempo y consumiría una cantidad considerable de almacenamiento para las copias de los arreglos. 7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones En esta sección desarrollaremos aún más la clase LibroCalificaciones, que presentamos en el capítulo 3 y expandimos en los capítulos 4 y 5. Recuerde que esta clase representa un libro de calificaciones utilizado por un instructor para almacenar y analizar un conjunto de calificaciones de estudiantes. Las versiones anteriores de esta clase procesan un conjunto de calificaciones introducidas por el usuario, pero no mantienen los valores de las calificaciones individuales en variables de instancia de la clase. Por ende, los cálculos repetidos requieren que el usuario vuelva a introducir las mismas calificaciones. Una manera de resolver este problema sería almacenar cada calificación introducida por el usuario en una instancia individual de la clase. Por ejemplo, podríamos crear las variables de instancia calificacion1, calificacion2, …, calificacion10 en la clase LibroCalificaciones para almacenar 10 calificaciones de estudiantes. No obstante, el código para totalizar las calificaciones y determinar el promedio de la clase sería voluminoso, y la clase no podría procesar más de 10 calificaciones a la vez. En esta sección resolvemos este problema, almacenando las calificaciones en un arreglo. Almacenar las calificaciones de los estudiantes en un arreglo en la clase LibroCalificaciones La versión de la clase LibroCalificaciones (figura 7.14) que presentamos aquí utiliza un arreglo de enteros para almacenar las calificaciones de varios estudiantes en un solo examen. Esto elimina la necesidad de introducir varias veces el mismo conjunto de calificaciones. El arreglo calificaciones se declara como una variable de instancia en la línea 7; por lo tanto, cada objeto LibroCalificaciones mantiene su propio conjunto de calificaciones. El constructor de la clase (líneas 10 a 14) tiene dos parámetros: el nombre del curso y un arreglo de calificaciones. Cuando una aplicación (por ejemplo, la clase PruebaLibroCalificaciones en la figura 7.15) crea un objeto LibroCalificaciones, la aplicación pasa un arreglo int existente al constructor, el cual asigna la referencia del arreglo a la variable de instancia calificaciones (línea 13). El tamaño del arreglo calificaciones se determina en base a la clase que pasa el arreglo al constructor. Por ende, un objeto LibroCalificaciones puede procesar un número de calificaciones variable. Los valores de las calificaciones en el arreglo que se pasa podría introducirlos un usuario desde el teclado, o podrían leerse desde un archivo en el disco (como veremos en el capítulo 14). En 7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones 279
  • 318. 280 Capítulo 7 Arreglos nuestra aplicación de prueba, simplemente inicializamos un arreglo con un conjunto de valores de calificaciones (figura 7.15, línea 10). Una vez que las calificaciones se almacenan en una variable de instancia llamada cali- ficaciones de la clase LibroCalificaciones, todos los métodos de la clase pueden acceder a los elementos de calificaciones según sea necesario, para realizar varios cálculos. 1 // Fig. 7.14: LibroCalificaciones.java 2 // Libro de calificaciones que utiliza un arreglo para almacenar las calificaciones de una prueba. 3 4 public class LibroCalificaciones 5 { 6 private String nombreDelCurso; // nombre del curso que representa este LibroCalificaciones 7 private int calificaciones[]; // arreglo de calificaciones de estudiantes 8 9 // el constructor de dos argumentos inicializa nombreDelCurso y el arreglo calificaciones 10 public LibroCalificaciones( String nombre, int arregloCalif[] ) 11 { 12 nombreDelCurso = nombre; // inicializa nombreDelCurso 13 calificaciones = arregloCalif; // almacena las calificaciones 14 } // fin del constructor de LibroCalificaciones con dos argumentos 15 16 // método para establecer el nombre del curso 17 public void establecerNombreDelCurso( String nombre ) 18 { 19 nombreDelCurso = nombre; // almacena el nombre del curso 20 } // fin del método establecerNombreDelCurso 21 22 // método para obtener el nombre del curso 23 public String obtenerNombreDelCurso() 24 { 25 return nombreDelCurso; 26 } // fin del método obtenerNombreDelCurso 27 28 // muestra un mensaje de bienvenida al usuario de LibroCalificaciones 29 public void mostrarMensaje() 30 { 31 // obtenerNombreDelCurso obtiene el nombre del curso 32 System.out.printf( "Bienvenido al libro de calificaciones paran%s!nn", 33 obtenerNombreDelCurso() ); 34 } // fin del método mostrarMensaje 35 36 // realiza varias operaciones sobre los datos 37 public void procesarCalificaciones() 38 { 39 // imprime el arreglo de calificaciones 40 imprimirCalificaciones(); 41 42 // llama al método obtenerPromedio para calcular la calificación promedio 43 System.out.printf( "nEl promedio de la clase es %.2fn", obtenerPromedio() ); 44 45 // llama a los métodos obtenerMinima y obtenerMaxima 46 System.out.printf( "La calificacion mas baja es %dnLa calificacion mas alta es %dnn", 47 obtenerMinima(), obtenerMaxima() ); Figura 7.14 | La clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones de una prueba. (Parte 1 de 3).
  • 319. 7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones 281 48 49 // llama a imprimirGraficoBarras para imprimir el gráfico de distribución de calificaciones 50 imprimirGraficoBarras(); 51 } // fin del método procesarCalificaciones 52 53 // busca la calificación más baja 54 public int obtenerMinima() 55 { 56 int califBaja = calificaciones[ 0 ]; // asume que calificaciones[ 0 ] es la más baja 57 58 // itera a través del arreglo de calificaciones 59 for ( int calificacion : calificaciones ) 60 { 61 // si calificación es menor que califBaja, se asigna a califBaja 62 if ( calificacion < califBaja ) 63 califBaja = calificacion; // nueva calificación más baja 64 } // fin de for 65 66 return califBaja; // devuelve la calificación más baja 67 } // fin del método obtenerMinima 68 69 // busca la calificación más alta 70 public int obtenerMaxima() 71 { 72 int califAlta = calificaciones[ 0 ]; // asume que calificaciones[ 0 ] es la más alta 73 74 // itera a través del arreglo de calificaciones 75 for ( int calificacion : calificaciones ) 76 { 77 // si calificacion es mayor que califAlta, se asigna a califAlta 78 if ( calificacion > califAlta ) 79 califAlta = calificacion; // nueva calificación más alta 80 } // fin de for 81 82 return califAlta; // devuelve la calificación más alta 83 } // fin del método obtenerMaxima 84 85 // determina la calificación promedio de la prueba 86 public double obtenerPromedio() 87 { 88 int total = 0; // inicializa el total 89 90 // suma las calificaciones para un estudiante 91 for ( int calificacion : calificaciones ) 92 total += calificacion; 93 94 // devuelve el promedio de las calificaciones 95 return (double) total / calificaciones.length; 96 } // fin del método obtenerPromedio 97 98 // imprime gráfico de barras que muestra la distribución de las calificaciones 99 public void imprimirGraficoBarras() 100 { 101 System.out.println( "Distribucion de calificaciones:" ); 102 Figura 7.14 | La clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones de una prueba. (Parte 2 de 3).
  • 320. 282 Capítulo 7 Arreglos El método procesarCalificaciones (líneas 37 a 51) contiene una serie de llamadas a métodos que produce un reporte en el que se resumen las calificaciones. La línea 40 llama al método imprimirCalificaciones para imprimir el contenido del arreglo calificaciones. Las líneas 134 a 136 en el método imprimirCalificaciones utilizan una instrucción for para imprimir las calificaciones de los estudiantes. En este caso se debe utilizar una instrucción for controlada por contador, ya que las líneas 135 y 136 utilizan el valor de la variable contador estudiante para imprimir cada calificación enseguida de un número de estudiante específico (vea la figura 7.15). Aunque los subíndices de los arreglos empiezan en 0, lo común es que el profesor enumere a los estudiantes empe- zando desde 1. Por ende, las líneas 135 y 136 imprimen estudiante + 1 como el número de estudiante para producir las etiquetas "Estudiante 1: ", "Estudiante 2: ", y así en lo sucesivo. A continuación, el método procesarCalificaciones llama al método obtenerPromedio (línea 43) para obtener el promedio de las calificaciones en el arreglo. El método obtenerPromedio (líneas 86 a 96) utiliza una instrucción for mejorada para totalizar los valores en el arreglo calificaciones antes de calcular el promedio. El parámetro en el encabezado de la instrucción for mejorada (por ejemplo, int calificacion) indica que para cada iteración, la variable int calificacion recibe un valor en el arreglo calificaciones. Observe que el cálculo del promedio en la línea 95 utiliza calificaciones.length para determinar el número de calificaciones que se van a promediar. 103 // almacena la frecuencia de las calificaciones en cada rango de 10 calificaciones 104 int frecuencia[] = new int[ 11 ]; 105 106 // para cada calificación, incrementa la frecuencia apropiada 107 for ( int calificacion : calificaciones ) 108 ++frecuencia[ calificacion / 10 ]; 109 110 // para cada frecuencia de calificación, imprime una barra en el gráfico 111 for ( int cuenta = 0; cuenta < frecuencia.length; cuenta++ ) 112 { 113 // imprime etiquetas de las barras ( "00-09: ", ..., "90-99: ", "100: " ) 114 if ( cuenta == 10 ) 115 System.out.printf( "%5d: ", 100 ); 116 else 117 System.out.printf( "%02d-%02d: ", 118 cuenta * 10, cuenta * 10 + 9 ); 119 120 // imprime barra de asteriscos 121 for ( int estrellas = 0; estrellas < frecuencia[ cuenta ]; estrellas++ ) 122 System.out.print( "*" ); 123 124 System.out.println(); // inicia una nueva línea de salida 125 } // fin de for externo 126 } // fin del método imprimirGraficoBarras 127 128 // imprime el contenido del arreglo de calificaciones 129 public void imprimirCalificaciones() 130 { 131 System.out.println( "Las calificaciones son:n" ); 132 133 // imprime la calificación de cada estudiante 134 for ( int estudiante = 0; estudiante < calificaciones.length; estudiante++ ) 135 System.out.printf( "Estudiante %2d: %3dn", 136 estudiante + 1, calificaciones[ estudiante ] ); 137 } // fin del método imprimirCalificaciones 138 } // fin de la clase LibroCalificaciones Figura 7.14 | La clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones de una prueba. (Parte 3 de 3).
  • 321. 7.8 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo para almacenar las calificaciones 283 Las líneas 46 y 47 en el método procesarCalificaciones llaman a los métodos obtenerMinima y obte- nerMaxima para determinar las calificaciones más baja y más alta de cualquier estudiante en el examen, en forma respectiva. Cada uno de estos métodos utiliza una instrucción for mejorada para iterar a través del arreglo cali- ficaciones. Las líneas 59 a 64 en el método obtenerMinima iteran a través del arreglo. Las líneas 62 y 63 com- paran cada calificación con califBaja; si una calificación es menor que califBaja, a califBaja se le asigna esa calificación. Cuando la línea 66 se ejecuta, califBaja contiene la calificación más baja en el arreglo. El método obtenerMaxima (líneas 70 a 83) funciona de manera similar al método obtenerMinima. Por último, la línea 50 en el método procesarCalificaciones llama al método imprimirGraficoBarras para imprimir un gráfico de distribución de los datos de las calificaciones, mediante el uso de una técnica similar a la de la figura 7.6. En ese ejemplo, calculamos en forma manual el número de calificaciones en cada categoría (es decir, de 0 a 9, de 10 a 19, …, de 90 a 99 y 100) con sólo analizar un conjunto de calificaciones. En este ejemplo, las líneas 107 y 108 utilizan una técnica similar a la de las figuras 7.7 y 7.8 para calcular la frecuencia de las califi- caciones en cada categoría. La línea 104 declara y crea el arreglo frecuencia de 11 valores int para almacenar la frecuencia de las calificaciones en cada categoría de éstas. Para cada calificacion en el arreglo calificaciones, las líneas 107 y 108 incrementan el elemento apropiado del arreglo frecuencia. Para determinar qué elemento se debe incrementar, la línea 108 divide la calificacion actual entre 10, mediante la división entera. Por ejemplo, si calificacion es 85, la línea 108 incrementa frecuencia[ 8 ] para actualizar la cuenta de calificaciones en el rango 80-89. Las líneas 111 a 125 imprimen a continuación el gráfico de barras (vea la figura 7.15), con base en los valores en el arreglo frecuencia. Al igual que las líneas 23 y 24 de la figura 7.6, las líneas 121 y 122 de la figura 7.14 utilizan un valor en el arreglo frecuencia para determinar el número de asteriscos a imprimir en cada barra. La clase PruebaLibroCalificaciones para demostrar la clase LibroCalificaciones La aplicación de la figura 7.15 crea un objeto de la clase LibroCalificaciones (figura 7.14) mediante el uso del arreglo int arregloCalif (que se declara y se inicializa en la línea 10). Las líneas 12 y 13 pasan el nombre de un curso y arregloCalif al constructor de LibroCalificaciones. La línea 14 imprime un mensaje de bienvenida, y la línea 15 invoca el método procesarCalificaciones del objeto LibroCalificaciones. La salida muestra el resumen de las 10 calificaciones en miLibroCalificaciones. Figura 7.15 | PruebaLibroCalificaciones crea un objeto LibroCalificaciones usando un arreglo de calificaciones, y después invoca al método procesarCalificaciones para analizarlas. (Parte 1 de 2). 1 // Fig. 7.15: PruebaLibroCalificaciones.java 2 // Crea objeto LibroCalificaciones, usando un arreglo de calificaciones. 3 4 public class PruebaLibroCalificaciones 5 { 6 // el método main comienza la ejecución del programa 7 public static void main( String args[] ) 8 { 9 // arreglo unidimensional de calificaciones de estudiantes 10 int arregloCalif[] = { 87, 68, 94, 100, 83, 78, 85, 91, 76, 87 }; 11 12 LibroCalificaciones miLibroCalificaciones = new LibroCalificaciones( 13 “CS101 Introduccion a la programacion en Java”, arregloCalif ); 14 miLibroCalificaciones.mostrarMensaje(); 15 miLibroCalificaciones.procesarCalificaciones(); 16 } // fin de main 17 } // fin de la clase PruebaLibroCalificaciones Bienvenido al libro de calificaciones para CS101 Introduccion a la programacion en Java!
  • 322. 284 Capítulo 7 Arreglos Las calificaciones son: Estudiante 1: 87 Estudiante 2: 68 Estudiante 3: 94 Estudiante 4: 100 Estudiante 5: 83 Estudiante 6: 78 Estudiante 7: 85 Estudiante 8: 91 Estudiante 9: 76 Estudiante 10: 87 El promedio de la clase es 84.90 La calificacion mas baja es 68 La calificacion mas alta es 100 Distribucion de calificaciones: 00-09: 10-19: 20-29: 30-39: 40-49: 50-59: 60-69: * 70-79: ** 80-89: **** 90-99: ** 100: * Figura 7.15 | PruebaLibroCalificaciones crea un objeto LibroCalificaciones usando un arreglo de calificaciones, y después invoca al método procesarCalificaciones para analizarlas. (Parte 2 de 2). Observación de ingeniería de software 7.1 Un arnés de prueba (o aplicación de prueba) es responsable de crear un objeto de la clase que se probará y de propor- cionarle datos. Estos datos podrían provenir de cualquiera de varias fuentes. Los datos de prueba pueden colocarse directamente en un arreglo con un inicializador de arreglos, pueden provenir del usuario mediante el teclado, de un archivo (como veremos en el capítulo 14) o pueden provenir de una red (como veremos en el capítulo 24). Después de pasar estos datos al constructor de la clase para instanciar el objeto, este arnés de prueba debe llamar al objeto para probar sus métodos y manipular sus datos. La recopilación de datos en el arnés de prueba de esta forma permite a la clase manipular datos de varias fuentes. 7.9 Arreglos multidimensionales Los arreglos multidimensionales de dos dimensiones se utilizan con frecuencia para representar tablas de valores, las cuales consisten en información ordenada en filas y columnas. Para identificar un elemento específico de una tabla, debemos especificar dos subíndices. Por convención, el primero identifica la fila del elemento y el segundo su columna. Los arreglos que requieren dos subíndices para identificar un elemento específico se llaman arre- glos bidimensionales (los arreglos multidimensionales pueden tener más de dos dimensiones). Java no soporta los arreglos multidimensionales directamente, pero permite al programador especificar arreglos unidimensionales, cuyos elementos sean también arreglos unidimensionales, con lo cual se obtiene el mismo efecto. La figura 7.16 ilustra un arreglo bidimensional a, que contiene tres filas y cuatro columnas (es decir, un arreglo de tres por cua- tro). En general, a un arreglo con m filas y n columnas se le llama arreglo de m por n. Cada elemento en el arreglo a se identifica en la figura 7.16 mediante una expresión de acceso a un arreglo de la forma a [ fila ][ columna ]; a es el nombre del arreglo, fila y columna son los subíndices que identifican en forma única a cada elemento en el arreglo a por número de fila y columna. Observe que los nombres de los
  • 323. elementos en la fila 0 tienen todos un primer subíndice de 0, y los nombres de los elementos en la columna 3 tienen un segundo subíndice de 3. Arreglos de arreglos unidimensionales Al igual que los arreglos unidimensionales, los arreglos multidimensionales pueden inicializarse mediante iniciali- zadores de arreglos en las declaraciones. Un arreglo bidimensional b con dos filas y dos columnas podría declararse e inicializarse con inicializadores de arreglos anidados, como se muestra a continuación: int b[ ] [ ] = { { 1, 2 }, {3, 4} }; Los valores del inicializador se agrupan por fila entre llaves. Así, 1 y 2 inicializan a b[ 0 ][ 0 ] y b[ 0 ][ 1 ], respectivamente; 3 y 4 inicializan a b[ 1 ][ 0 ] y b[ 1 ][ 1 ], respectivamente. El compilador cuenta el núme- ro de inicializadores de arreglos anidados (representados por conjuntos de llaves dentro de las llaves externas) en la declaración del arreglo, para determinar el número de filas en el arreglo b. El compilador cuenta los valores inicializadores en el inicializador de arreglos anidado de una fila, para determinar el número de columnas en esa fila. Como veremos en unos momentos, esto significa que las filas pueden tener distintas longitudes. Los arreglos multidimensionales se mantienen como arreglos de arreglos unidimensionales. Por lo tanto, el arreglo b en la declaración anterior está realmente compuesto de dos arreglos unidimensionales separados: uno que contiene los valores en la primera lista inicializadora anidada { 1, 2 } y uno que contiene los valores en la segunda lista inicializadora anidada { 3, 4 }. Así, el arreglo b en sí es un arreglo de dos elementos, cada uno de los cuales es un arreglo unidimensional de valores int. Arreglos bidimensionales con filas de distintas longitudes La forma en que se representan los arreglos multidimensionales los hace bastante flexibles. De hecho, las longitu- des de las filas en el arreglo b no tienen que ser iguales. Por ejemplo, int b[ ][ ] = { { 1, 2 }, { 3, 4, 5 } }; crea el arreglo entero b con dos elementos (los cuales se determinan según el número de inicializadores de arreglos anidados) que representan las filas del arreglo bidimensional. Cada elemento de b es una referencia a un arre- glo unidimensional de variables int. El arreglo int de la fila 0 es un arreglo unidimensional con dos elementos (1 y 2), y el arreglo int de la fila 1 es un arreglo unidimensional con tres elementos (3, 4 y 5). Creación de arreglos bidimensionales mediante expresiones de creación de arreglos Un arreglo multidimensional con el mismo número de columnas en cada fila puede crearse mediante una expre- sión de creación de arreglos. Por ejemplo, en las siguientes líneas se declara el arreglo b y se le asigna una referencia a un arreglo de tres por cuatro: int b[ ][ ] = new int[ 3 ][ 4 ]; 7.9 Arreglos multidimensionales 285 Fila 0 Fila 1 Fila 2 Subíndice de columna Subíndice de fila Nombre del arreglo a[ 0 ][ 0 ] a[ 1 ][ 0 ] a[ 2 ][ 0 ] a[ 0 ][ 1 ] a[ 1 ][ 1 ] a[ 2 ][ 1 ] a[ 0 ][ 2 ] a[ 1 ][ 2 ] a[ 2 ][ 2 ] a[ 0 ][ 3 ] Columna 0 Columna 1 Columna 2 Columna 3 a[ 1 ][ 3 ] a[ 2 ][ 3 ] Figura 7.16 | Arreglo bidimensional con tres filas y cuatro columnas.
  • 324. 286 Capítulo 7 Arreglos En este caso, utilizamos los valores literales 3 y 4 para especificar el número de filas y columnas, respectivamente, pero esto no es obligatorio. Los programas también pueden utilizar variables para especificar las dimensiones de los arreglos, ya que new crea arreglos en tiempo de ejecución, no en tiempo de compilación. Al igual que con los arreglos unidimensionales, los elementos de un arreglo multidimensional se inicializan cuando se crea el objeto arreglo. Un arreglo multidimensional, en el que cada fila tiene un número distinto de columnas, puede crearse de la siguiente manera: int b[][] = new int[ 2 ][ ]; // crea 2 filas b[ 0 ] = new int[ 5 ]; // crea 5 columnas para la fila 0 b[ 1 ] = new int[ 3 ]; // crea 3 columnas para la fila 1 Estas instrucciones crean un arreglo bidimensional con dos filas. La fila 0 tiene cinco columnas y la fila 1 tiene 3. Ejemplo de arreglos bidimensionales: cómo mostrar los valores de los elementos La figura 7.17 demuestra cómo inicializar arreglos bidimensionales con inicializadores de arreglos, y cómo utilizar ciclos for anidados para recorrer los arreglos (es decir, manipular cada uno de los elementos de cada arreglo). El método main de la clase InicArreglo declara dos arreglos. En la declaración de arreglo1 (línea 9) se utilizan inicializadores de arreglos anidados para inicializar la primera fila del arreglo con los valores 1, 2 y 3, y la segunda fila con los valores 4, 5 y 6. En la declaración de arreglo2 (línea 10) se utilizan inicializadores anidados de distintas longitudes. En este caso, la primera fila se inicializa para tener dos elementos con los valores 1 y 2, respectivamente. La segunda fila se inicializa para tener un elemento con el valor 3. La tercera fila se inicializa para tener tres elementos con los valores 4, 5 y 6, respectivamente. 1 // Fig. 7.17: InicArreglo.java 2 // Inicialización de arreglos bidimensionales. 3 4 public class InicArreglo 5 { 6 // crea e imprime arreglos bidimensionales 7 public static void main( String args[] ) 8 { 9 int arreglo1[][] = { { 1, 2, 3 }, { 4, 5, 6 } }; 10 int arreglo2[][] = { { 1, 2 }, { 3 }, { 4, 5, 6 } }; 11 12 System.out.println( "Los valores en arreglo1 por filas son" ); 13 imprimirArreglo( arreglo1 ); // muestra arreglo1 por filas 14 15 System.out.println( "nLos valores en arreglo2 por filas son" ); 16 imprimirArreglo( arreglo2 ); // muestra arreglo2 por filas 17 } // fin de main 18 19 // imprime filas y columnas de un arreglo bidimensional 20 public static void imprimirArreglo( int arreglo[][] ) 21 { 22 // itera a través de las filas del arreglo 23 for ( int fila = 0; fila < arreglo.length; fila++ ) 24 { 25 // itera a través de las columnas de la fila actual 26 for ( int columna = 0; columna < arreglo[ fila ].length; columna++ ) 27 System.out.printf( "%d ", arreglo[ fila ][ columna ] ); 28 29 System.out.println(); // inicia nueva línea de salida 30 } // fin de for externo 31 } // fin del método imprimirArreglo 32 } // fin de la clase InicArreglo Figura 7.17 | Inicialización de arreglos bidimensionales. (Parte 1 de 2).
  • 325. Las líneas 13 y 16 llaman al método imprimirArreglo (líneas 20 a 31) para imprimir los elementos de arreglo1 y arreglo2, respectivamente. El método imprimirArreglo especifica el parámetro tipo arreglo como int arreglo[][] para indicar que el método recibe un arreglo bidimensional. La instrucción for (líneas 23 a 30) imprime las filas de un arreglo bidimensional. En la condición de continuación de ciclo de la instrucción for exterior, la expresión arreglo.length determina el número de filas en el arreglo. En la expresión for inte- rior, la expresión arreglo[ fila ].length determina el número de columnas en la fila actual del arreglo. Esta condición permite al ciclo determinar el número exacto de columnas en cada fila. Manipulaciones comunes en arreglos multidimensionales, realizadas mediante instrucciones for En muchas manipulaciones comunes en arreglos se utilizan instrucciones for. Como ejemplo, la siguiente ins- trucción for asigna a todos los elementos en la fila 2 del arreglo a, en la figura 7.16, el valor de cero: for ( int columna = 0; columna < a[ 2 ].length; columna++ ) a[ 2 ][ columna ] = 0; Especificamos la fila 2; por lo tanto, sabemos que el primer índice siempre será 2 (0 es la primera fila y 1 es la segunda). Este ciclo for varía solamente el segundo índice (es decir, el índice de la columna). Si la fila 2 del arreglo a contiene cuatro elementos, entonces la instrucción for anterior es equivalente a las siguientes instrucciones de asignación: a[ 2 ][ 0 ] = 0; a[ 2 ][ 1 ] = 0; a[ 2 ][ 2 ] = 0; a[ 2 ][ 3 ] = 0; La siguiente instrucción for anidada suma el total de los valores de todos los elementos del arreglo a: int total = 0; for ( int fila = 0; fila < a.length; fila++ ) { for ( int columna = 0; columna < a[ fila ].length; columna++ ) total += a[ fila ][ columna ]; } // fin de for exterior Estas instrucciones for anidadas suman el total de los elementos del arreglo, una fila a la vez. La instrucción for exterior empieza asignando 0 al índice fila, de manera que los elementos de la primera fila puedan totalizarse mediante la instrucción for interior. Después, la instrucción for exterior incrementa fila a 1, de manera que la segunda fila pueda totalizarse. Luego, la instrucción for exterior incrementa fila a 2, para que la tercera fila pueda totalizarse. La variable total puede mostrarse al terminar la instrucción for exterior. En el siguiente ejemplo le mostraremos cómo procesar un arreglo bidimensional de una manera similar, usando instrucciones for mejora- das anidadas. 7.9 Arreglos multidimensionales 287 Los valores en arreglo1 por filas son 1 2 3 4 5 6 Los valores en arreglo2 por filas son 1 2 3 4 5 6 Figura 7.17 | Inicialización de arreglos bidimensionales. (Parte 2 de 2).
  • 326. 288 Capítulo 7 Arreglos 7.10 Ejemplo práctico: la clase LibroCalificaciones que usa un arreglo bidimensional En la sección 7.8 presentamos la clase LibroCalificaciones (figura 7.14), la cual utilizó un arreglo unidimen- sional para almacenar las calificaciones de los estudiantes en un solo examen. En la mayoría de los cursos, los estudiantes presentan varios exámenes. Es probable que los profesores quieran analizar las calificaciones a lo largo de todo el curso, tanto para un solo estudiante como para la clase en general. Cómo almacenar las calificaciones de los estudiantes en un arreglo bidimensional en la clase LibroCalificaciones La figura 7.18 contie